001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.aspectwerkz.definition;
005:
006: import com.tc.aspectwerkz.DeploymentModel;
007: import com.tc.aspectwerkz.expression.ExpressionContext;
008: import com.tc.aspectwerkz.expression.ExpressionInfo;
009: import com.tc.aspectwerkz.expression.ExpressionVisitor;
010: import com.tc.aspectwerkz.cflow.CflowBinding;
011: import com.tc.aspectwerkz.perx.PerObjectAspect;
012: import com.tc.aspectwerkz.transform.Properties;
013: import com.tc.aspectwerkz.util.SequencedHashMap;
014:
015: import java.util.ArrayList;
016: import java.util.Collection;
017: import java.util.HashMap;
018: import java.util.HashSet;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.Map;
022: import java.util.Set;
023:
024: /**
025: * TODO clean up - remove methods not used, refactor etc.
026: * <p/>
027: * Abstraction of the system definition, defines the aspect system.
028: *
029: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
030: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
031: */
032: public class SystemDefinition {
033:
034: /**
035: * Empty hash map.
036: */
037: public static final Map EMPTY_HASH_MAP = new HashMap();
038:
039: /**
040: * Maps the aspects to it's name.
041: */
042: private final Map m_aspectMap = new HashMap();
043:
044: /**
045: * Maps the mixins to it's name.
046: */
047: private final Map m_mixinMap = new SequencedHashMap();
048:
049: /**
050: * The UUID for this definition.
051: */
052: private String m_uuid = "default";
053:
054: /**
055: * The include packages.
056: */
057: private final Set m_includePackages = new HashSet();
058:
059: /**
060: * The exclude packages.
061: */
062: private final Set m_excludePackages = new HashSet();
063:
064: /**
065: * The prepare packages.
066: */
067: private final Set m_preparePackages = new HashSet();
068:
069: /**
070: * All prepared pointcuts defined in the system.
071: */
072: private final Map m_deploymentScopes = new HashMap();
073:
074: /**
075: * Creates a new instance, creates and sets the system cflow aspect.
076: */
077: public SystemDefinition(final String uuid) {
078: setUuid(uuid);
079: }
080:
081: /**
082: * Sets the UUID for the definition.
083: *
084: * @param uuid the UUID
085: */
086: private void setUuid(final String uuid) {
087: m_uuid = uuid;
088: }
089:
090: /**
091: * Returns the UUID for the definition.
092: *
093: * @return the UUID
094: */
095: public String getUuid() {
096: return m_uuid;
097: }
098:
099: /**
100: * Returns the include packages.
101: *
102: * @return the include packages
103: */
104: public Set getIncludePackages() {
105: return m_includePackages;
106: }
107:
108: /**
109: * Returns the exclude packages.
110: *
111: * @return the exclude packages
112: */
113: public Set getExcludePackages() {
114: return m_excludePackages;
115: }
116:
117: /**
118: * Returns a collection with the aspect definitions registered.
119: *
120: * @return the aspect definitions
121: */
122: public Collection getAspectDefinitions() {
123: Collection clone = new ArrayList(m_aspectMap.size());
124: for (Iterator it = m_aspectMap.values().iterator(); it
125: .hasNext();) {
126: clone.add(it.next());
127: }
128: return clone;
129: }
130:
131: /**
132: * Returns a collection with the mixin definitions registered.
133: *
134: * @return the mixin definitions
135: */
136: public Collection getMixinDefinitions() {
137: Collection clone = new ArrayList(m_mixinMap.size());
138: for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
139: clone.add(it.next());
140: }
141: return clone;
142: }
143:
144: /**
145: * Returns a collection with the advice definitions registered.
146: *
147: * @return the advice definitions
148: */
149: public Collection getAdviceDefinitions() {
150: final Collection adviceDefs = new ArrayList();
151: for (Iterator it = m_aspectMap.values().iterator(); it
152: .hasNext();) {
153: AspectDefinition aspectDef = (AspectDefinition) it.next();
154: adviceDefs.addAll(aspectDef.getAroundAdviceDefinitions());
155: adviceDefs.addAll(aspectDef.getBeforeAdviceDefinitions());
156: adviceDefs.addAll(aspectDef.getAfterAdviceDefinitions());
157: }
158: return adviceDefs;
159: }
160:
161: /**
162: * Returns a specific aspect definition.
163: *
164: * @param name the name of the aspect definition
165: * @return the aspect definition
166: */
167: public AspectDefinition getAspectDefinition(final String name) {
168: return (AspectDefinition) m_aspectMap.get(name);
169: }
170:
171: /**
172: * Returns the mixin definitions matching a specfic expression.
173: *
174: * @param ctx the expression context
175: * @return a list with the mixin definitions
176: */
177: public List getMixinDefinitions(final ExpressionContext ctx) {
178: final List introDefs = new ArrayList();
179: for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
180: MixinDefinition introDef = (MixinDefinition) it.next();
181: for (int i = 0; i < introDef.getExpressionInfos().length; i++) {
182: if (introDef.getExpressionInfos()[i].getExpression()
183: .match(ctx)) {
184: introDefs.add(introDef);
185: }
186: }
187: }
188: return introDefs;
189: }
190:
191: /**
192: * Returns the interface introductions for a certain class merged with the implementation based introductions as
193: * well.
194: *
195: * @param ctx the expression context
196: * @return the names
197: */
198: public List getInterfaceIntroductionDefinitions(
199: final ExpressionContext ctx) {
200: if (ctx == null) {
201: throw new IllegalArgumentException(
202: "context can not be null");
203: }
204: List interfaceIntroductionDefs = new ArrayList();
205: for (Iterator iterator = m_aspectMap.values().iterator(); iterator
206: .hasNext();) {
207: AspectDefinition aspectDef = (AspectDefinition) iterator
208: .next();
209: for (Iterator it = aspectDef
210: .getInterfaceIntroductionDefinitions().iterator(); it
211: .hasNext();) {
212: InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it
213: .next();
214: ExpressionInfo[] expressionInfos = introDef
215: .getExpressionInfos();
216: for (int i = 0; i < expressionInfos.length; i++) {
217: ExpressionInfo expressionInfo = expressionInfos[i];
218: ExpressionVisitor expression = expressionInfo
219: .getExpression();
220: if (expression.match(ctx)) {
221: interfaceIntroductionDefs.add(introDef);
222: }
223: }
224: }
225: }
226: return interfaceIntroductionDefs;
227: }
228:
229: /**
230: * Adds a new aspect definition
231: * For each of its bounded pointcut, register cflow aspects as necessary.
232: *
233: * @param aspectDef the aspect definition
234: */
235: public void addAspect(final AspectDefinition aspectDef) {
236: if (aspectDef == null) {
237: throw new IllegalArgumentException(
238: "aspect definition can not be null");
239: }
240:
241: synchronized (m_aspectMap) {
242: if (m_aspectMap.containsKey(aspectDef.getName())) {
243: return;
244: }
245:
246: // register a PerObjectAspect if deployment-model is perThis or perTarget
247: if (DeploymentModel.PER_TARGET.equals(aspectDef
248: .getDeploymentModel())) {
249: addAspect(PerObjectAspect.getAspectDefinition(this ,
250: aspectDef));
251: } else if (DeploymentModel.PER_THIS.equals(aspectDef
252: .getDeploymentModel())) {
253: addAspect(PerObjectAspect.getAspectDefinition(this ,
254: aspectDef));
255: }
256:
257: // register the "cflow" aspects for this aspect bindings
258: // note: this one will even support cflow(xx && cflow())
259: // note: the cflow aspect MUST be registered FIRST for precedence purpose
260: // so that pcX && cflow(pcX) match on pcX
261: for (Iterator iterator = aspectDef.getAdviceDefinitions()
262: .iterator(); iterator.hasNext();) {
263: AdviceDefinition adviceDefinition = (AdviceDefinition) iterator
264: .next();
265: List cflowBindings = CflowBinding
266: .getCflowBindingsForCflowOf(adviceDefinition
267: .getExpressionInfo());
268: for (Iterator cflows = cflowBindings.iterator(); cflows
269: .hasNext();) {
270: CflowBinding cflowBinding = (CflowBinding) cflows
271: .next();
272: if (!cflowBinding.isCflowBelow()) {
273: addAspect(cflowBinding.getAspectDefinition(
274: this , aspectDef.getClassInfo()
275: .getClassLoader()));
276: }
277: }
278: }
279:
280: // register the aspect itself
281: m_aspectMap.put(aspectDef.getName(), aspectDef);
282:
283: // register the "cflowbelow" aspects for this aspect bindings
284: // note: this one will even support cflowbelow(xx && cflowbelow())
285: // note: the cflowbelow aspect MUST be registered LAST for precedence purpose
286: // so that pcX && cflowbelow(pcX) does not match on just the pcX joinpoint
287: for (Iterator iterator = aspectDef.getAdviceDefinitions()
288: .iterator(); iterator.hasNext();) {
289: AdviceDefinition adviceDefinition = (AdviceDefinition) iterator
290: .next();
291: List cflowBindings = CflowBinding
292: .getCflowBindingsForCflowOf(adviceDefinition
293: .getExpressionInfo());
294: for (Iterator cflows = cflowBindings.iterator(); cflows
295: .hasNext();) {
296: CflowBinding cflowBinding = (CflowBinding) cflows
297: .next();
298: if (cflowBinding.isCflowBelow()) {
299: addAspect(cflowBinding.getAspectDefinition(
300: this , aspectDef.getClassInfo()
301: .getClassLoader()));
302: }
303: }
304: }
305: }
306: }
307:
308: /**
309: * Adds a new aspect definition, overwrites the previous one with the same name (if there is one).
310: *
311: * @param aspectDef the aspect definition
312: */
313: public void addAspectOverwriteIfExists(
314: final AspectDefinition aspectDef) {
315: if (aspectDef == null) {
316: throw new IllegalArgumentException(
317: "aspect definition can not be null");
318: }
319: synchronized (m_aspectMap) {
320: m_aspectMap.put(aspectDef.getName(), aspectDef);
321: }
322: }
323:
324: /**
325: * Adds a new mixin definition.
326: *
327: * @param mixinDef the mixin definition
328: */
329: public void addMixinDefinition(final MixinDefinition mixinDef) {
330: if (mixinDef == null) {
331: throw new IllegalArgumentException(
332: "mixin definition can not be null");
333: }
334: synchronized (m_mixinMap) {
335: if (m_mixinMap.containsKey(mixinDef.getMixinImpl()
336: .getName())) {
337: MixinDefinition def = (MixinDefinition) m_mixinMap
338: .get(mixinDef.getMixinImpl().getName());
339: def.addExpressionInfos(mixinDef.getExpressionInfos());
340: return;
341: }
342: m_mixinMap.put(mixinDef.getMixinImpl().getName(), mixinDef);
343: }
344: }
345:
346: /**
347: * Adds a new include package.
348: *
349: * @param includePackage the new include package
350: */
351: public void addIncludePackage(final String includePackage) {
352: synchronized (m_includePackages) {
353: m_includePackages.add(includePackage + '.');
354: }
355: }
356:
357: /**
358: * Adds a new exclude package.
359: *
360: * @param excludePackage the new exclude package
361: */
362: public void addExcludePackage(final String excludePackage) {
363: synchronized (m_excludePackages) {
364: m_excludePackages.add(excludePackage + '.');
365: }
366: }
367:
368: /**
369: * Adds a new prepare package.
370: *
371: * @param preparePackage the new prepare package
372: */
373: public void addPreparePackage(final String preparePackage) {
374: synchronized (m_preparePackages) {
375: m_preparePackages.add(preparePackage + '.');
376: }
377: }
378:
379: /**
380: * Returns the prepare packages.
381: *
382: * @return
383: */
384: public Set getPreparePackages() {
385: return m_preparePackages;
386: }
387:
388: /**
389: * Checks if a class should be included.
390: *
391: * @param className the name or the class
392: * @return boolean
393: */
394: public boolean inIncludePackage(final String className) {
395: if (className == null) {
396: throw new IllegalArgumentException(
397: "class name can not be null");
398: }
399: if (m_includePackages.isEmpty()) {
400: return true;
401: }
402: for (Iterator it = m_includePackages.iterator(); it.hasNext();) {
403: String packageName = (String) it.next();
404: if (className.startsWith(packageName)) {
405: return true;
406: }
407: }
408: return false;
409: }
410:
411: /**
412: * Checks if a class should be excluded.
413: *
414: * @param className the name or the class
415: * @return boolean
416: */
417: public boolean inExcludePackage(final String className) {
418: if (className == null) {
419: throw new IllegalArgumentException(
420: "class name can not be null");
421: }
422: for (Iterator it = m_excludePackages.iterator(); it.hasNext();) {
423: String packageName = (String) it.next();
424: if (className.startsWith(packageName)) {
425: return true;
426: }
427: }
428: return false;
429: }
430:
431: /**
432: * Checks if a class is in prepare declaration
433: *
434: * @param className the name or the class
435: * @return boolean
436: */
437: public boolean inPreparePackage(String className) {
438: if (className == null) {
439: throw new IllegalArgumentException(
440: "class name can not be null");
441: }
442: for (Iterator it = m_preparePackages.iterator(); it.hasNext();) {
443: String packageName = (String) it.next();
444: if (className.startsWith(packageName)) {
445: return true;
446: }
447: }
448: return false;
449: }
450:
451: /**
452: * Checks if a context has a pointcut.
453: *
454: * @param ctx the expression context
455: * @return boolean
456: */
457: public boolean hasPointcut(final ExpressionContext ctx) {
458: if (ctx == null) {
459: throw new IllegalArgumentException(
460: "context can not be null");
461: }
462: for (Iterator it = m_aspectMap.values().iterator(); it
463: .hasNext();) {
464: AspectDefinition aspectDef = (AspectDefinition) it.next();
465: for (Iterator it2 = aspectDef.getAdviceDefinitions()
466: .iterator(); it2.hasNext();) {
467: AdviceDefinition adviceDef = (AdviceDefinition) it2
468: .next();
469: final ExpressionInfo expressionInfo = adviceDef
470: .getExpressionInfo();
471: if (expressionInfo == null) {
472: continue;
473: }
474: ExpressionVisitor expression = expressionInfo
475: .getExpression();
476:
477: if (expression.match(ctx)) {
478: if (Properties.VERBOSE_LOGGING) {
479: System.out
480: .println("-----------------------------------------------------");
481: System.out.println("Pointcut match");
482: System.out.println(" Pointcut " + expression);
483: System.out.println(" Aspect "
484: + aspectDef.getName());
485: System.out.println(" Advice "
486: + adviceDef.getName());
487: System.out.println(" Target "
488: + ctx.getReflectionInfo());
489: System.out.println(" Within "
490: + ctx.getWithinReflectionInfo());
491: }
492: return true;
493: }
494: }
495: }
496: return false;
497: }
498:
499: /**
500: * Checks if a class is advised.
501: *
502: * @param ctxs an array with the expression contexts
503: * @return boolean
504: */
505: public boolean isAdvised(final ExpressionContext[] ctxs) {
506: if (ctxs == null) {
507: throw new IllegalArgumentException(
508: "context array can not be null");
509: }
510: for (Iterator it = m_aspectMap.values().iterator(); it
511: .hasNext();) {
512: AspectDefinition aspectDef = (AspectDefinition) it.next();
513: List advices = aspectDef.getAdviceDefinitions();
514: for (Iterator it2 = advices.iterator(); it2.hasNext();) {
515: AdviceDefinition adviceDef = (AdviceDefinition) it2
516: .next();
517: for (int i = 0; i < ctxs.length; i++) {
518: ExpressionContext ctx = ctxs[i];
519: final ExpressionInfo expressionInfo = adviceDef
520: .getExpressionInfo();
521: if (expressionInfo == null) {
522: continue;
523: }
524: if (expressionInfo
525: .getAdvisedClassFilterExpression().match(
526: ctx)) {
527: return true;
528: }
529: }
530: }
531: }
532: return false;
533: }
534:
535: /**
536: * Checks if a class is advised.
537: *
538: * @param ctx the expression context
539: * @return boolean
540: */
541: public boolean isAdvised(final ExpressionContext ctx) {
542: if (ctx == null) {
543: throw new IllegalArgumentException(
544: "context can not be null");
545: }
546: for (Iterator it = m_aspectMap.values().iterator(); it
547: .hasNext();) {
548: AspectDefinition aspectDef = (AspectDefinition) it.next();
549: List advices = aspectDef.getAdviceDefinitions();
550: for (Iterator it2 = advices.iterator(); it2.hasNext();) {
551: AdviceDefinition adviceDef = (AdviceDefinition) it2
552: .next();
553: final ExpressionInfo expressionInfo = adviceDef
554: .getExpressionInfo();
555: if (expressionInfo == null) {
556: continue;
557: }
558: if (expressionInfo.getAdvisedClassFilterExpression()
559: .match(ctx) /*||
560: expressionInfo.getAdvisedCflowClassFilterExpression().match(ctx) ALEX XXX CFLOW*/) {
561: return true;
562: }
563: }
564: }
565: return false;
566: }
567:
568: /**
569: * Checks if a class has an mixin.
570: *
571: * @param ctxs an array with the expression contexts
572: * @return boolean
573: */
574: public boolean hasMixin(final ExpressionContext[] ctxs) {
575: if (ctxs == null) {
576: throw new IllegalArgumentException(
577: "context array can not be null");
578: }
579: for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
580: MixinDefinition introDef = (MixinDefinition) it.next();
581: ExpressionInfo[] expressionInfos = introDef
582: .getExpressionInfos();
583: for (int i = 0; i < expressionInfos.length; i++) {
584: ExpressionInfo expressionInfo = expressionInfos[i];
585: for (int j = 0; j < ctxs.length; j++) {
586: if (expressionInfo.getExpression().match(ctxs[j])) {
587: return true;
588: }
589: }
590: }
591: }
592: return false;
593: }
594:
595: /**
596: * Checks if a class has an mixin.
597: *
598: * @param ctx the expression context
599: * @return boolean
600: */
601: public boolean hasMixin(final ExpressionContext ctx) {
602: if (ctx == null) {
603: throw new IllegalArgumentException(
604: "context can not be null");
605: }
606: for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
607: MixinDefinition introDef = (MixinDefinition) it.next();
608: ExpressionInfo[] expressionInfos = introDef
609: .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: return false;
618: }
619:
620: /**
621: * Checks if a class is advised with an interface introduction.
622: *
623: * @param ctxs the expression contexts
624: * @return boolean
625: */
626: public boolean hasIntroducedInterface(final ExpressionContext[] ctxs) {
627: if (ctxs == null) {
628: throw new IllegalArgumentException(
629: "context array can not be null");
630: }
631: for (Iterator iterator = m_aspectMap.values().iterator(); iterator
632: .hasNext();) {
633: AspectDefinition aspectDef = (AspectDefinition) iterator
634: .next();
635: for (Iterator it = aspectDef
636: .getInterfaceIntroductionDefinitions().iterator(); it
637: .hasNext();) {
638: InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it
639: .next();
640: ExpressionInfo[] expressionInfos = introDef
641: .getExpressionInfos();
642: for (int i = 0; i < expressionInfos.length; i++) {
643: ExpressionInfo expressionInfo = expressionInfos[i];
644: for (int j = 0; j < ctxs.length; j++) {
645: if (expressionInfo.getExpression().match(
646: ctxs[i])) {
647: return true;
648: }
649: }
650: }
651: }
652: }
653: return false;
654: }
655:
656: /**
657: * Checks if a class is advised with an interface introduction.
658: *
659: * @param ctx the expression context
660: * @return boolean
661: */
662: public boolean hasIntroducedInterface(final ExpressionContext ctx) {
663: if (ctx == null) {
664: throw new IllegalArgumentException(
665: "context can not be null");
666: }
667: for (Iterator iterator = m_aspectMap.values().iterator(); iterator
668: .hasNext();) {
669: AspectDefinition aspectDefinition = (AspectDefinition) iterator
670: .next();
671: for (Iterator it = aspectDefinition
672: .getInterfaceIntroductionDefinitions().iterator(); it
673: .hasNext();) {
674: InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it
675: .next();
676: ExpressionInfo[] expressionInfos = introDef
677: .getExpressionInfos();
678: for (int i = 0; i < expressionInfos.length; i++) {
679: ExpressionInfo expressionInfo = expressionInfos[i];
680: if (expressionInfo.getExpression().match(ctx)) {
681: return true;
682: }
683: }
684: }
685: }
686: return false;
687: }
688:
689: /**
690: * Returns a collection with all deployment scopes in the system.
691: *
692: * @return a collection with all deployment scopes in the system
693: */
694: public Collection getDeploymentScopes() {
695: return m_deploymentScopes.values();
696: }
697:
698: /**
699: * Returns the deployment scope with the name specified.
700: *
701: * @param name the name of the deployment scope
702: * @return the deployment scope with the name specified
703: */
704: public DeploymentScope getDeploymentScope(final String name) {
705: return (DeploymentScope) m_deploymentScopes.get(name);
706: }
707:
708: /**
709: * Adds a deployment scope to the system.
710: *
711: * @param deploymentScope the deployment scope
712: */
713: public void addDeploymentScope(final DeploymentScope deploymentScope) {
714: m_deploymentScopes.put(deploymentScope.getName(),
715: deploymentScope);
716:
717: //TODO do we need to take care of cflow aspects
718: }
719:
720: public boolean equals(Object o) {
721: return ((SystemDefinition) o).m_uuid.equals(m_uuid);
722: }
723:
724: public int hashCode() {
725: return m_uuid.hashCode();
726: }
727:
728: /**
729: * Create a new virtual system definition for the given loader and add the virtual aspect in it.
730: *
731: * @param loader
732: * @return the def
733: */
734: public static SystemDefinition createVirtualDefinitionAt(
735: ClassLoader loader) {
736: SystemDefinition def = new SystemDefinition(
737: SystemDefinitionContainer
738: .getVirtualDefinitionUuidFor(loader));
739: DocumentParser.addVirtualAspect(def);
740: return def;
741: }
742: }
|