1 /***************************************************************************************
2 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.definition;
9
10 import org.codehaus.aspectwerkz.expression.ExpressionContext;
11 import org.codehaus.aspectwerkz.expression.ExpressionInfo;
12 import org.codehaus.aspectwerkz.expression.ExpressionVisitor;
13 import org.codehaus.aspectwerkz.util.SequencedHashMap;
14 import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
15 import org.codehaus.aspectwerkz.cflow.CflowBinding;
16
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 /***
27 * TODO clean up - remove methods not used, refactor etc.
28 * <p/>
29 * Abstraction of the system definition, defines the aspect system.
30 *
31 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
32 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
33 */
34 public class SystemDefinition {
35
36 /***
37 * Empty hash map.
38 */
39 public static final Map EMPTY_HASH_MAP = new HashMap();
40
41 /***
42 * Maps the aspects to it's name.
43 */
44 private final Map m_aspectMap = new SequencedHashMap();
45
46 /***
47 * Maps the mixins to it's name.
48 */
49 private final Map m_mixinMap = new SequencedHashMap();
50
51 /***
52 * The UUID for this definition.
53 */
54 private String m_uuid = "default";
55
56 /***
57 * The include packages.
58 */
59 private final Set m_includePackages = new HashSet();
60
61 /***
62 * The exclude packages.
63 */
64 private final Set m_excludePackages = new HashSet();
65
66 /***
67 * The prepare packages.
68 */
69 private final Set m_preparePackages = new HashSet();
70
71 /***
72 * All prepared pointcuts defined in the system.
73 */
74 private final Map m_deploymentScopes = new HashMap();
75
76 /***
77 * Creates a new instance, creates and sets the system cflow aspect.
78 */
79 public SystemDefinition(final String uuid) {
80 setUuid(uuid);
81
82
83
84
85
86
87
88
89 }
90
91 /***
92 * Sets the UUID for the definition.
93 *
94 * @param uuid the UUID
95 */
96 private void setUuid(final String uuid) {
97 m_uuid = uuid;
98 }
99
100 /***
101 * Returns the UUID for the definition.
102 *
103 * @return the UUID
104 */
105 public String getUuid() {
106 return m_uuid;
107 }
108
109 /***
110 * Returns the include packages.
111 *
112 * @return the include packages
113 */
114 public Set getIncludePackages() {
115 return m_includePackages;
116 }
117
118 /***
119 * Returns the exclude packages.
120 *
121 * @return the exclude packages
122 */
123 public Set getExcludePackages() {
124 return m_excludePackages;
125 }
126
127 /***
128 * Returns a collection with the aspect definitions registered.
129 *
130 * @return the aspect definitions
131 */
132 public Collection getAspectDefinitions() {
133 Collection clone = new ArrayList(m_aspectMap.size());
134 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
135 clone.add(it.next());
136 }
137 return clone;
138 }
139
140 /***
141 * Returns a collection with the mixin definitions registered.
142 *
143 * @return the mixin definitions
144 */
145 public Collection getMixinDefinitions() {
146 Collection clone = new ArrayList(m_mixinMap.size());
147 for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
148 clone.add(it.next());
149 }
150 return clone;
151 }
152
153 /***
154 * Returns a collection with the advice definitions registered.
155 *
156 * @return the advice definitions
157 */
158 public Collection getAdviceDefinitions() {
159 final Collection adviceDefs = new ArrayList();
160 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
161 AspectDefinition aspectDef = (AspectDefinition) it.next();
162 adviceDefs.addAll(aspectDef.getAroundAdviceDefinitions());
163 adviceDefs.addAll(aspectDef.getBeforeAdviceDefinitions());
164 adviceDefs.addAll(aspectDef.getAfterAdviceDefinitions());
165 }
166 return adviceDefs;
167 }
168
169 /***
170 * Returns a specific aspect definition.
171 *
172 * @param name the name of the aspect definition
173 * @return the aspect definition
174 */
175 public AspectDefinition getAspectDefinition(final String name) {
176 return (AspectDefinition) m_aspectMap.get(name);
177 }
178
179 /***
180 * Returns the mixin definitions matching a specfic expression.
181 *
182 * @param ctx the expression context
183 * @return a list with the mixin definitions
184 */
185 public List getMixinDefinitions(final ExpressionContext ctx) {
186 final List introDefs = new ArrayList();
187 for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
188 MixinDefinition introDef = (MixinDefinition) it.next();
189 for (int i = 0; i < introDef.getExpressionInfos().length; i++) {
190 if (introDef.getExpressionInfos()[i].getExpression().match(ctx)) {
191 introDefs.add(introDef);
192 }
193 }
194 }
195 return introDefs;
196 }
197
198 /***
199 * Returns the interface introductions for a certain class merged with the implementation based introductions as
200 * well.
201 *
202 * @param ctx the expression context
203 * @return the names
204 */
205 public List getInterfaceIntroductionDefinitions(final ExpressionContext ctx) {
206 if (ctx == null) {
207 throw new IllegalArgumentException("context can not be null");
208 }
209 List interfaceIntroductionDefs = new ArrayList();
210 for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
211 AspectDefinition aspectDef = (AspectDefinition) iterator.next();
212 for (Iterator it = aspectDef.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
213 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
214 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
215 for (int i = 0; i < expressionInfos.length; i++) {
216 ExpressionInfo expressionInfo = expressionInfos[i];
217 ExpressionVisitor expression = expressionInfo.getExpression();
218 if (expression.match(ctx)) {
219 interfaceIntroductionDefs.add(introDef);
220 }
221 }
222 }
223 }
224 return interfaceIntroductionDefs;
225 }
226
227 /***
228 * Adds a new aspect definition
229 * For each of its bounded pointcut, register cflow aspects as necessary.
230 *
231 * @param aspectDef the aspect definition
232 */
233 public void addAspect(final AspectDefinition aspectDef) {
234 if (aspectDef == null) {
235 throw new IllegalArgumentException("aspect definition can not be null");
236 }
237
238 synchronized (m_aspectMap) {
239 if (m_aspectMap.containsKey(aspectDef.getName())) {
240 return;
241 }
242
243
244
245
246
247 for (Iterator iterator = aspectDef.getAdviceDefinitions().iterator(); iterator.hasNext();) {
248 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator.next();
249 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceDefinition.getExpressionInfo());
250 for (Iterator cflows = cflowBindings.iterator(); cflows.hasNext();) {
251 CflowBinding cflowBinding = (CflowBinding) cflows.next();
252 if (!cflowBinding.isCflowBelow()) {
253 addAspect(cflowBinding.getAspectDefinition(this, aspectDef.getClassInfo().getClassLoader()));
254 }
255 }
256 }
257
258
259 m_aspectMap.put(aspectDef.getName(), aspectDef);
260
261
262
263
264
265 for (Iterator iterator = aspectDef.getAdviceDefinitions().iterator(); iterator.hasNext();) {
266 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator.next();
267 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceDefinition.getExpressionInfo());
268 for (Iterator cflows = cflowBindings.iterator(); cflows.hasNext();) {
269 CflowBinding cflowBinding = (CflowBinding) cflows.next();
270 if (cflowBinding.isCflowBelow()) {
271 addAspect(cflowBinding.getAspectDefinition(this, aspectDef.getClassInfo().getClassLoader()));
272 }
273 }
274 }
275 }
276 }
277
278 /***
279 * Adds a new aspect definition, overwrites the previous one with the same name (if there is one).
280 *
281 * @param aspectDef the aspect definition
282 */
283 public void addAspectOverwriteIfExists(final AspectDefinition aspectDef) {
284 if (aspectDef == null) {
285 throw new IllegalArgumentException("aspect definition can not be null");
286 }
287 synchronized (m_aspectMap) {
288 m_aspectMap.put(aspectDef.getName(), aspectDef);
289 }
290 }
291
292 /***
293 * Adds a new mixin definition.
294 *
295 * @param mixinDef the mixin definition
296 */
297 public void addMixinDefinition(final MixinDefinition mixinDef) {
298 if (mixinDef == null) {
299 throw new IllegalArgumentException("mixin definition can not be null");
300 }
301 synchronized (m_mixinMap) {
302 if (m_mixinMap.containsKey(mixinDef.getMixinImpl().getName())) {
303 MixinDefinition def = (MixinDefinition) m_mixinMap.get(mixinDef.getMixinImpl().getName());
304 def.addExpressionInfos(mixinDef.getExpressionInfos());
305 return;
306 }
307 m_mixinMap.put(mixinDef.getMixinImpl().getName(), mixinDef);
308 }
309 }
310
311 /***
312 * Adds a new include package.
313 *
314 * @param includePackage the new include package
315 */
316 public void addIncludePackage(final String includePackage) {
317 synchronized (m_includePackages) {
318 m_includePackages.add(includePackage + '.');
319 }
320 }
321
322 /***
323 * Adds a new exclude package.
324 *
325 * @param excludePackage the new exclude package
326 */
327 public void addExcludePackage(final String excludePackage) {
328 synchronized (m_excludePackages) {
329 m_excludePackages.add(excludePackage + '.');
330 }
331 }
332
333 /***
334 * Adds a new prepare package.
335 *
336 * @param preparePackage the new prepare package
337 */
338 public void addPreparePackage(final String preparePackage) {
339 synchronized (m_preparePackages) {
340 m_preparePackages.add(preparePackage + '.');
341 }
342 }
343
344 /***
345 * Returns the prepare packages.
346 *
347 * @return
348 */
349 public Set getPreparePackages() {
350 return m_preparePackages;
351 }
352
353 /***
354 * Checks if a class should be included.
355 *
356 * @param className the name or the class
357 * @return boolean
358 */
359 public boolean inIncludePackage(final String className) {
360 if (className == null) {
361 throw new IllegalArgumentException("class name can not be null");
362 }
363 if (m_includePackages.isEmpty()) {
364 return true;
365 }
366 for (Iterator it = m_includePackages.iterator(); it.hasNext();) {
367 String packageName = (String) it.next();
368 >if (className.startsWith(packageName)) {
369 return true;
370 }
371 }
372 return false;
373 }
374
375 /***
376 * Checks if a class should be excluded.
377 *
378 * @param className the name or the class
379 * @return boolean
380 */
381 public boolean inExcludePackage(final String className) {
382 if (className == null) {
383 throw new IllegalArgumentException("class name can not be null");
384 }
385 for (Iterator it = m_excludePackages.iterator(); it.hasNext();) {
386 String packageName = (String) it.next();
387 >if (className.startsWith(packageName)) {
388 return true;
389 }
390 }
391 return false;
392 }
393
394 /***
395 * Checks if a class is in prepare declaration
396 *
397 * @param className the name or the class
398 * @return boolean
399 */
400 public boolean inPreparePackage(String className) {
401 if (className == null) {
402 throw new IllegalArgumentException("class name can not be null");
403 }
404 for (Iterator it = m_preparePackages.iterator(); it.hasNext();) {
405 String packageName = (String) it.next();
406 >if (className.startsWith(packageName)) {
407 return true;
408 }
409 }
410 return false;
411 }
412
413 /***
414 * Checks if a context has a pointcut.
415 *
416 * @param ctx the expression context
417 * @return boolean
418 */
419 public boolean hasPointcut(final ExpressionContext ctx) {
420 if (ctx == null) {
421 throw new IllegalArgumentException("context can not be null");
422 }
423 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
424 AspectDefinition aspectDef = (AspectDefinition) it.next();
425 for (Iterator it2 = aspectDef.getAdviceDefinitions().iterator(); it2.hasNext();) {
426 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
427 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
428 if (expressionInfo == null) {
429 continue;
430 }
431 ExpressionVisitor expression = expressionInfo.getExpression();
432
433 if (expression.match(ctx)) {
434 if (AspectWerkzPreProcessor.DETAILS) {
435 System.out.println(
436 "[TRACE - match: " + expression + " @ "
437 + aspectDef.getQualifiedName() + "/" +
438 adviceDef.getName()
439 );
440 System.out.println("[ for " + ctx.getReflectionInfo());
441 System.out.println("[ within " + ctx.getWithinReflectionInfo());
442 System.out.println("[ type " + ctx.getPointcutType().toString());
443 }
444 return true;
445 }
446 }
447 }
448 return false;
449 }
450
451 /***
452 * Checks if a class is advised.
453 *
454 * @param ctxs an array with the expression contexts
455 * @return boolean
456 */
457 public boolean isAdvised(final ExpressionContext[] ctxs) {
458 if (ctxs == null) {
459 throw new IllegalArgumentException("context array can not be null");
460 }
461 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
462 AspectDefinition aspectDef = (AspectDefinition) it.next();
463 List advices = aspectDef.getAdviceDefinitions();
464 for (Iterator it2 = advices.iterator(); it2.hasNext();) {
465 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
466 for (int i = 0; i < ctxs.length; i++) {
467 ExpressionContext ctx = ctxs[i];
468 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
469 if (expressionInfo == null) {
470 continue;
471 }
472 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
473 if (AspectWerkzPreProcessor.DETAILS) {
474 System.out.println(
475 "[TRACE - earlymatch: " + expressionInfo + " @ "
476 + aspectDef.getQualifiedName() + "/" +
477 adviceDef.getName()
478 );
479 System.out.println("[ for " + ctx.getReflectionInfo());
480 System.out.println("[ within " + ctx.getWithinReflectionInfo());
481 System.out.println("[ type " + ctx.getPointcutType().toString());
482 }
483 return true;
484 }
485 }
486 }
487 }
488 return false;
489 }
490
491 /***
492 * Checks if a class is advised.
493 *
494 * @param ctx the expression context
495 * @return boolean
496 */
497 public boolean isAdvised(final ExpressionContext ctx) {
498 if (ctx == null) {
499 throw new IllegalArgumentException("context can not be null");
500 }
501 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
502 AspectDefinition aspectDef = (AspectDefinition) it.next();
503 List advices = aspectDef.getAdviceDefinitions();
504 for (Iterator it2 = advices.iterator(); it2.hasNext();) {
505 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
506 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
507 if (expressionInfo == null) {
508 continue;
509 }
510 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)
511 ) {
512 return true;
513 }
514 }
515 }
516 return false;
517 }
518
519 /***
520 * Checks if a class has an mixin.
521 *
522 * @param ctxs an array with the expression contexts
523 * @return boolean
524 */
525 public boolean hasMixin(final ExpressionContext[] ctxs) {
526 if (ctxs == null) {
527 throw new IllegalArgumentException("context array can not be null");
528 }
529 for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
530 MixinDefinition introDef = (MixinDefinition) it.next();
531 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
532 for (int i = 0; i < expressionInfos.length; i++) {
533 ExpressionInfo expressionInfo = expressionInfos[i];
534 for (int j = 0; j < ctxs.length; j++) {
535 if (expressionInfo.getExpression().match(ctxs[j])) {
536 return true;
537 }
538 }
539 }
540 }
541 return false;
542 }
543
544 /***
545 * Checks if a class has an mixin.
546 *
547 * @param ctx the expression context
548 * @return boolean
549 */
550 public boolean hasMixin(final ExpressionContext ctx) {
551 if (ctx == null) {
552 throw new IllegalArgumentException("context can not be null");
553 }
554 for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
555 MixinDefinition introDef = (MixinDefinition) it.next();
556 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
557 for (int i = 0; i < expressionInfos.length; i++) {
558 ExpressionInfo expressionInfo = expressionInfos[i];
559 if (expressionInfo.getExpression().match(ctx)) {
560 return true;
561 }
562 }
563 }
564 return false;
565 }
566
567 /***
568 * Checks if a class is advised with an interface introduction.
569 *
570 * @param ctxs the expression contexts
571 * @return boolean
572 */
573 public boolean hasIntroducedInterface(final ExpressionContext[] ctxs) {
574 if (ctxs == null) {
575 throw new IllegalArgumentException("context array can not be null");
576 }
577 for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
578 AspectDefinition aspectDef = (AspectDefinition) iterator.next();
579 for (Iterator it = aspectDef.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
580 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
581 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
582 for (int i = 0; i < expressionInfos.length; i++) {
583 ExpressionInfo expressionInfo = expressionInfos[i];
584 for (int j = 0; j < ctxs.length; j++) {
585 if (expressionInfo.getExpression().match(ctxs[i])) {
586 return true;
587 }
588 }
589 }
590 }
591 }
592 return false;
593 }
594
595 /***
596 * Checks if a class is advised with an interface introduction.
597 *
598 * @param ctx the expression context
599 * @return boolean
600 */
601 public boolean hasIntroducedInterface(final ExpressionContext ctx) {
602 if (ctx == null) {
603 throw new IllegalArgumentException("context can not be null");
604 }
605 for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
606 AspectDefinition aspectDefinition = (AspectDefinition) iterator.next();
607 for (Iterator it = aspectDefinition.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
608 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
609 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
610 for (int i = 0; i < expressionInfos.length; i++) {
611 ExpressionInfo expressionInfo = expressionInfos[i];
612 if (expressionInfo.getExpression().match(ctx)) {
613 return true;
614 }
615 }
616 }
617 }
618 return false;
619 }
620
621 /***
622 * Returns a collection with all deployment scopes in the system.
623 *
624 * @return a collection with all deployment scopes in the system
625 */
626 public Collection getDeploymentScopes() {
627 return m_deploymentScopes.values();
628 }
629
630 /***
631 * Returns the deployment scope with the name specified.
632 *
633 * @param name the name of the deployment scope
634 * @return the deployment scope with the name specified
635 */
636 public DeploymentScope getDeploymentScope(final String name) {
637 return (DeploymentScope) m_deploymentScopes.get(name);
638 }
639
640 /***
641 * Adds a deployment scope to the system.
642 *
643 * @param deploymentScope the deployment scope
644 */
645 public void addDeploymentScope(final DeploymentScope deploymentScope) {
646 m_deploymentScopes.put(deploymentScope.getName(), deploymentScope);
647
648
649 }
650
651 public boolean equals(Object o) {
652 return ((SystemDefinition) o).m_uuid.equals(m_uuid);
653 }
654
655 public int hashCode() {
656 return m_uuid.hashCode();
657 }
658
659 /***
660 * Create a new virtual system definition for the given loader and add the virtual aspect in it.
661 *
662 * @param loader
663 * @return
664 */
665 public static SystemDefinition createVirtualDefinitionAt(ClassLoader loader) {
666 SystemDefinition def = new SystemDefinition(SystemDefinitionContainer.getVirtualDefinitionUuid(loader));
667 DocumentParser.addVirtualAspect(def);
668 return def;
669 }
670 }