KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jac > core > MethodPointcut


1 /*
2   Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2 of the
7   License, or (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   Lesser General Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with this program; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA */

18
19 package org.objectweb.jac.core;
20
21 import gnu.regexp.RE;
22 import gnu.regexp.REException;
23 import gnu.regexp.RESyntax;
24 import java.util.Arrays JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.StringTokenizer JavaDoc;
31 import java.util.Vector JavaDoc;
32 import org.apache.log4j.Logger;
33 import org.objectweb.jac.core.rtti.AbstractMethodItem;
34 import org.objectweb.jac.core.rtti.ClassItem;
35 import org.objectweb.jac.core.rtti.ClassRepository;
36 import org.objectweb.jac.core.rtti.CollectionItem;
37 import org.objectweb.jac.core.rtti.ConstructorItem;
38 import org.objectweb.jac.core.rtti.FieldItem;
39 import org.objectweb.jac.core.rtti.MethodItem;
40 import org.objectweb.jac.util.ExtArrays;
41 import org.objectweb.jac.util.Strings;
42
43 /**
44  * This class can be used by JAC aspect components to easily define a
45  * set of method points on the base program that the aspects will use to
46  * modify its behavior.
47  *
48  * <p>A method pointcut is defined through four pointcut
49  * expressions. For the moment, these pointcut expressions are a
50  * simple extension of regular expressions -- in an EMACS_LISP syntax
51  * (see the GNU-regexp tutorial) that can be combined with the
52  * <code>&&</code> operator (in this case all the regexps must match)
53  * or the <code>||</code> operator (in this case, only one regexp must
54  * match). Before a regexp, you can use the <code>!</code> (not)
55  * operator to inverse the matching.
56  *
57  * <p>Depending on the pointcut expression, you can also
58  * use keywords that will be dynamically interpreted. We intend to
59  * provide a more complete and suited desciption language. Note that
60  * for the moment, the tricky cases that cannot be handled by these
61  * expressions can be treated by programming the
62  * <code>AspectComponent.whenUsingNewInstance</code> method.
63  *
64  * <ul><li><i>The host expression</i>: filters the components that
65  * belong or not to the pointcut on a location basis. To be activated,
66  * the name of the host where the pointcut is currently applied must
67  * match this expression.</li>
68  *
69  * <li><i>The wrappee expression</i>: filters the components that
70  * belong or not to the pointcut on a per-component basis. This
71  * expression assumes that the component is named (that the naming
72  * aspect is woven). If you need to modify all the components of the
73  * same class equaly, then this expression should be <code>".*"</code>
74  * and the <i>wrappee-class expression</i> should be used.</li>
75  *
76  * <li><i>The wrappee-class expression</i>: filters the components
77  * that belong or not to the pointcut on a per-class basis. For
78  * instance, <code>"A || B"</code> says that only the classes that are
79  * named A or B belong to the pointcut; <code>packagename.* &&
80  * classname</code> matches the class that belong to
81  * <code>packagename</code> and that are named
82  * <code>classname</code>.</li>
83  *
84  * <li><i>The wrappee-method expression</i>: for all the components
85  * that match the two previous expressions, defines which methods must
86  * be modified by the pointcut. For instance,
87  * <code>"set.*(int):void"</code> matches all the methods that have
88  * name that begins with "set", that take only one integer argument,
89  * and that return nothing. The regular expression can contain several
90  * builtin keywords to automatically match methods with special
91  * semantics (for instance, <code>ALL</code>: all the methods of the
92  * class, <code>MODIFIERS</code>: all the state modifiers,
93  * <code>ACCESSORS</code>: all the state setters,
94  * <code>GETTERS(fielname1,fieldname2)</code>: the getter for the
95  * given field, <code>SETTERS(...)</code>: the setter for the given
96  * field), <code>WRITERS(...)</code>: all the methods which modify the
97  * given field. For instance, <code>FIELDSETTERS && !.*(int).*</code>
98  * matches all the field setters, excluding the ones for the integer
99  * fields.</li></ul>
100  *
101  * <p>For more informations on pointcut expressions, please see the <a
102  * HREF="doc/tutorial.html#3.2.5>programmer's guide</a>.
103  *
104  * <p>A pointcut is also related to a wrapping class and a wrapping
105  * method (and a precise wrapper, instance of the wrapping class, can
106  * be specified if it is known). This couple implements the aspect
107  * code in all the points that matches the over-depicted pointcut
108  * expression.
109  *
110  * <p>Finally, when a pointcut is applied to the matching elements,
111  * wrappers (instances of the wrapping class) are created (except if
112  * the intialization wrapper is not null). In this case, the
113  * <code>one2one</code> flag tells if one new wrapper instance nust be
114  * created for each base component or if one unique wrapper instance
115  * must be used for all the components.
116  *
117  * @author <a HREF="http://cedric.cnam.fr/~pawlak/index-english.html">Renaud Pawlak</a>
118  * @see AspectComponent */

119
120 public class MethodPointcut extends Pointcut {
121     static Logger logger = Logger.getLogger("pointcut");
122     static Logger loggerName = Logger.getLogger("pointcut.name");
123     static Logger loggerHost = Logger.getLogger("pointcut.host");
124     static Logger loggerPath = Logger.getLogger("pointcut.path");
125     static Logger loggerKeywords = Logger.getLogger("pointcut.keywords");
126     static Logger loggerCreate = Logger.getLogger("pointcut.create");
127     static Logger loggerWrappers = Logger.getLogger("wappers");
128
129     Vector JavaDoc wrappeeExprs = new Vector JavaDoc();
130     Vector JavaDoc wrappeeRegexps = new Vector JavaDoc();
131     Vector JavaDoc wrappeeClassExprs = new Vector JavaDoc();
132     Vector JavaDoc wrappeeClassRegexps = new Vector JavaDoc();
133     Vector JavaDoc wrappeeMethodExprs = new Vector JavaDoc();
134     Vector JavaDoc wrappeeMethodRegexps = new Vector JavaDoc();
135     Vector JavaDoc hostExprs = new Vector JavaDoc();
136     Vector JavaDoc hostRegexps = new Vector JavaDoc();
137     Vector JavaDoc iwrappeeExprs = new Vector JavaDoc();
138     Vector JavaDoc iwrappeeClassExprs = new Vector JavaDoc();
139     Vector JavaDoc iwrappeeMethodExprs = new Vector JavaDoc();
140     Vector JavaDoc ihostExprs = new Vector JavaDoc();
141     String JavaDoc wrappeeExpr;
142     String JavaDoc wrappeeClassExpr;
143     String JavaDoc wrappeeMethodExpr;
144     String JavaDoc hostExpr;
145     String JavaDoc wrappingClassName;
146     String JavaDoc methodName;
147     Object JavaDoc[] methodArgs;
148     String JavaDoc exceptionHandler;
149     boolean one2one = true;
150     boolean allInstances = false;
151     boolean allHosts = false;
152     boolean allClasses = false;
153     AspectComponent aspectComponent = null;
154
155     /** Class of the wrapper */
156     ClassItem wrapperClass;
157     /** The wrapping method */
158     //MethodItem wrappingMethod;
159

160     static String JavaDoc[] wrappeeKeywords = new String JavaDoc[] {
161         "ALL"
162     };
163     static String JavaDoc[] classKeywords = new String JavaDoc[] {
164         "ALL",
165         "COLLECTIONS"
166     };
167     static String JavaDoc[] methodKeywords = new String JavaDoc[] {
168         "ALL",
169         "STATICS",
170         "CONSTRUCTORS",
171         "MODIFIERS",
172         "REFACCESSORS",
173         "COLACCESSORS",
174         "ACCESSORS",
175         "COLSETTERS",
176         "FIELDSETTERS",
177         "REFSETTERS",
178         "SETTERS", /* args=a list of fields or tags */
179         "WRITERS", /* args=a list of fields or tags */
180         "COLGETTERS",
181         "FIELDGETTERS",
182         "REFGETTERS",
183         "GETTERS", /* args=a list of fields or tags */
184         "ADDERS", /* no args | args=list of collection or tags */
185         "REMOVERS" /* no args | args=list of collection or tags */
186     };
187     static String JavaDoc[] hostKeywords = new String JavaDoc[] {
188         "ALL"
189     };
190
191     /**
192      * Returns a readable description of the pointcut. */

193
194     public String JavaDoc toString() {
195         return "pointcut {"+wrappingClassName+","+
196             methodName+
197             "}->{"+wrappeeExpr+","+wrappeeClassExpr+","+
198             wrappeeMethodExpr+","+hostExpr+"}";
199     }
200
201     Wrapper commonWrapper = null;
202     Wrapper initWrapper = null;
203
204     /**
205      * Instantiates a new pointcut with the given characterisitics.
206      *
207      * @param aspectComponent the aspect component this pointcut
208      * belongs to
209      * @param wrappeeExpr the wrappee definition that matches the names
210      * as defined by the naming aspect
211      * @param wrappeeClassExpr the wrappee class expression (matches
212      * the fully qualified class name)
213      * @param wrappeeMethodExpr the wrappee method expression (matches
214      * the full method name as defined by the rtti)
215      * @param initWrapper the instance of the wrapper used by this
216      * pointcut, if null a new one is automatically created depending
217      * on the one2one flag
218      * @param wrappingClassName the name of the wrapper class
219      * @param methodName the name of the aspect component
220      * method to upcall when a mathing object is used
221      * @param methodArgs the argument values for this method
222      * method to upcall when a matching object is used
223      * @param hostExpr a regular expression that macthes the hosts
224      * where the pointcut has to be applied (if null or empty string,
225      * default is ".*" which means that the pointcut will be applied on
226      * all the hosts)
227      * @param one2one true if each new wrapped instance corresponds to
228      * one different wrapper (it has no effect if
229      * <code>initWrapper</code> is not null
230      */

231     public MethodPointcut(AspectComponent aspectComponent,
232                           String JavaDoc wrappeeExpr,
233                           String JavaDoc wrappeeClassExpr,
234                           String JavaDoc wrappeeMethodExpr,
235                           Wrapper initWrapper,
236                           String JavaDoc wrappingClassName,
237                           String JavaDoc methodName,
238                           Object JavaDoc[] methodArgs,
239                           String JavaDoc hostExpr,
240                           String JavaDoc exceptionHandler,
241                           boolean one2one) {
242
243         this.aspectComponent = aspectComponent;
244         this.wrappeeExpr = wrappeeExpr;
245         this.wrappeeClassExpr = wrappeeClassExpr;
246         this.wrappeeMethodExpr = wrappeeMethodExpr;
247         this.hostExpr = hostExpr;
248         this.initWrapper = initWrapper;
249         this.wrappingClassName = wrappingClassName;
250         this.methodName = methodName;
251         this.methodArgs = methodArgs;
252         this.exceptionHandler = exceptionHandler;
253         this.one2one = one2one;
254
255         parseExpr("wrappee class expression", null, null,
256                   wrappeeClassExpr, classKeywords,
257                   wrappeeClassExprs, iwrappeeClassExprs);
258         wrappeeClassRegexps = buildRegexps(wrappeeClassExprs);
259       
260         parseExpr("host expression", null, null,
261                   hostExpr, hostKeywords,
262                   hostExprs, ihostExprs);
263         hostRegexps = buildRegexps(hostExprs);
264
265         if (wrappeeExpr.equals("ALL") || wrappeeExpr.equals(".*")) {
266             allInstances = true;
267         }
268         if (hostExpr.equals("ALL") || hostExpr.equals(".*")) {
269             allHosts = true;
270         }
271         if (wrappeeClassExpr.equals("ALL") || wrappeeClassExpr.equals(".*")) {
272             allClasses = true;
273         }
274
275         if (!allInstances) {
276             parseExpr("wrappee expression", null, null,
277                       wrappeeExpr, wrappeeKeywords,
278                       wrappeeExprs, iwrappeeExprs);
279
280             wrappeeRegexps = buildRegexps(wrappeeExprs);
281         }
282
283         loggerCreate.debug(aspectComponent+" new pointcut "+this);
284     }
285
286     /**
287      * Build a vector of regular expressions
288      * @param patterns a collection of strings
289      * @return a vector of the size of patterns filled with RE.
290      */

291     Vector JavaDoc buildRegexps(Vector JavaDoc patterns) {
292         Vector JavaDoc result = new Vector JavaDoc(patterns.size());
293         Iterator JavaDoc i = patterns.iterator();
294         while (i.hasNext()) {
295             String JavaDoc pattern = (String JavaDoc)i.next();
296             try {
297                 result.add(buildRegexp(pattern));
298             } catch(REException e) {
299                 logger.error("invalid regexp \""+pattern+"\":"+e);
300             }
301         }
302         return result;
303     }
304
305     public static RE buildRegexp(String JavaDoc pattern) throws REException {
306         return new RE(Strings.replace(pattern,"$","\\$"),0,
307                       RESyntax.RE_SYNTAX_EMACS);
308     }
309
310     /**
311      * Instanciate the wrapper, and initialize wrapperClass
312      * @parapm wrappee unused
313      */

314     Wrapper buildWrapper(Wrappee wrappee) {
315         try {
316             if (wrapperClass==null) {
317                 wrapperClass = ClassRepository.get().getClass(wrappingClassName);
318             }
319             if (methodArgs!=null) {
320                 if (wrapperClass.isInner())
321                     return (Wrapper)wrapperClass.newInstance(
322                         ExtArrays.add(0,aspectComponent,methodArgs));
323                 else
324                     return (Wrapper)wrapperClass.newInstance(
325                         ExtArrays.add(0,aspectComponent,methodArgs));
326             } else {
327                 if (wrapperClass.isInner())
328                     return (Wrapper)wrapperClass.newInstance(
329                         new Object JavaDoc[] {aspectComponent,aspectComponent});
330                 else
331                     return (Wrapper)wrapperClass.newInstance(
332                         new Object JavaDoc[] {aspectComponent});
333             }
334         } catch(Exception JavaDoc e) {
335             logger.error("buildWrapper failed for "+wrappee,e);
336         }
337         return null;
338     }
339
340     /**
341      * Applies this pointcut to the given wrappee.
342      *
343      * <p>The pointcut application consists in wrapping the wrappee
344      * accordingly to the pointcut description. Note that in JAC, the
345      * pointcut is usually applied on a per-component basis, and when
346      * the component (wrappee) is used for the first time.
347      *
348      * @param wrappee the component the pointcut is applied to
349      * @param cl class the pointcut is applied to
350      */

351   
352     public synchronized void applyTo(Wrappee wrappee, ClassItem cl) {
353         
354         // REGRESSION: CANNOT HANDLE CLONES FOR THE MOMENT
355
//if( wrappee!=null && wrappee.isCloned() ) {
356
// Log.trace("pointcut","do not apply aspects on clones");
357
// return;
358
//}
359

360         logger.info("apply "+this+" on "+wrappee+" - "+cl);
361         
362
363         if (!isClassMatching(wrappee,cl)) { return; }
364         Logger classLogger = Logger.getLogger("pointcut."+cl);
365         if (classLogger.isDebugEnabled())
366             classLogger.debug("class is matching");
367         
368         if (!isHostMatching(wrappee,cl)) { return; }
369         if (classLogger.isDebugEnabled())
370             classLogger.debug("host is matching");
371         
372         if (!isNameMatching(wrappee,cl)) return;
373         if (classLogger.isDebugEnabled())
374             classLogger.debug("name is matching");
375         
376         // upcalls the method if exist
377
if (methodName!=null) {
378             try {
379                 classLogger.debug(
380                     "Upcalling "+aspectComponent.getClass().getName()+
381                     "."+methodName+"("+Arrays.asList(methodArgs)+")");
382                 Object JavaDoc[] args = ExtArrays.add(0,wrappee,methodArgs);
383                 ClassRepository.get().getClass(aspectComponent.getClass())
384                     .invoke(aspectComponent, methodName, args);
385             } catch(Exception JavaDoc e) {
386                 logger.error("Upcalling failed",e);
387             }
388         }
389
390         // stops if no wrapping infos if given
391
if (initWrapper==null
392             && wrappingClassName==null)
393             return;
394         Collection JavaDoc methodsToWrap =
395             wrappee!=null ? getMatchingMethodsFor(wrappee,cl) : getMatchingStaticMethodsFor(cl);
396         Wrapper wrapper = null;
397         if (initWrapper!=null) {
398             wrapper = commonWrapper = initWrapper;
399             wrapperClass = ClassRepository.get().getClass(wrapper);
400         } else {
401             if (one2one) {
402                 wrapper = buildWrapper(wrappee);
403             } else {
404                 if (commonWrapper==null)
405                     commonWrapper = buildWrapper(wrappee);
406                 wrapper = commonWrapper;
407             }
408         }
409       
410         //if (wrappingMethod==null) {
411
// wrappingMethod = wrapperClass.getMethod(wrappingMethodName);
412
//}
413

414         if (methodsToWrap!=null && methodsToWrap.size()>0) {
415             classLogger.debug(
416                 "applying "+wrappingClassName+
417                 " on "+cl.getName()+" ("+
418                 NameRepository.get().getName(wrappee)+")");
419             classLogger.debug("methods to wrap="+methodsToWrap);
420         }
421
422         loggerWrappers.debug("new pointcut: wrapper="+wrapper+" methods to wrap="+methodsToWrap);
423
424         // wrap the methods
425
//Log.trace("pointcut.wrap","exception handlers for "+wrapper.getClass()+": "+eh);
426
Iterator JavaDoc it = methodsToWrap.iterator();
427         boolean wrapped = false;
428         while (it.hasNext()) {
429             AbstractMethodItem method = (AbstractMethodItem)it.next();
430             classLogger.debug(
431                 "Wrapping "+method.getLongName()+" with "+wrappingClassName+
432                 " on "+wrappee+" - "+cl.getName());
433             //wrapped = wrapped || Wrapping.wrapMethod(wrappee,wrapper,method);
434
if (Wrapping.wrapMethod(wrappee,wrapper,method) && !wrapped) {
435                 // postponing this at after the loop seems to have
436
// strange effects on static methods wrapping
437
Wrapping.wrap(wrappee,cl,wrapper);
438                 wrapped = true;
439             }
440
441             // install exeption handler if needed
442
if (exceptionHandler!=null) {
443                 Wrapping.addExceptionHandler(wrappee,wrapper,
444                                              exceptionHandler,method);
445             }
446         }
447         loggerWrappers.debug("wrapped = "+wrapped);
448         if (methodsToWrap.size()==0 && one2one) {
449             Wrapping.wrap(wrappee,cl,wrapper);
450         }
451     }
452
453     /* Cache of matching methods (ClassItem -> Vector of AbstractMethodItem)*/
454     Hashtable JavaDoc cache = new Hashtable JavaDoc();
455
456     /* Cache of matching static methods (ClassItem -> Vector of AbstractMethodItem)*/
457     Hashtable JavaDoc staticsCache = new Hashtable JavaDoc();
458
459     /**
460      * Gets the methods of the wrappee that are modified by this
461      * pointcut.
462      *
463      * @param wrappee the component to test
464      * @param cli the class of the wrappee
465      * @return a vector containing the matching method items
466      */

467     protected Collection JavaDoc getMatchingMethodsFor(Wrappee wrappee, ClassItem cli) {
468         //Log.trace("pointcut.match."+cli,"getting matching methods for "+
469
// cli.getName()+"("+wrappee+") "+wrappeeMethodExpr);
470
String JavaDoc name = null;
471         if (wrappee!=null) {
472             name = cli.getName();
473         }
474         Collection JavaDoc result = (Collection JavaDoc)cache.get(name);
475         if (result==null) {
476             result = parseMethodExpr(wrappee,cli,wrappeeMethodExpr);
477             cache.put(name,result);
478             //Log.trace("pointcut.match."+cli,wrappeeMethodExpr+" -> "+result);
479
} else {
480             //Log.trace("pointcut.match."+cli,2,"methods cache hit for "+
481
// cli.getName()+"("+wrappee+")");
482
}
483         return result;
484     }
485
486     protected Collection JavaDoc getMatchingStaticMethodsFor(ClassItem cli) {
487         //Log.trace("pointcut.match."+cli,
488
// "getting static matching methods for "+cli.getName());
489
String JavaDoc name = cli.getName();
490         Collection JavaDoc result = (Collection JavaDoc)staticsCache.get(name);
491         if (result==null) {
492             //Log.trace("pointcut.match."+cli,"method expr="+wrappeeMethodExpr);
493
result = parseMethodExpr(null,cli,wrappeeMethodExpr);
494             staticsCache.put(name,result);
495         } else {
496             //Log.trace("pointcut.match."+cli,2,
497
// "methods cache hit for "+cli.getName());
498
}
499         return result;
500     }
501
502     /**
503      * @param wrappee the object the pointcut applies to
504      * @param cli the class the pointcut applies to (in case
505      * wrappee==null, for static methods)
506      * @param expr the pointcut expression
507      * @return A set of method matching the pointcut for the wrappee or class
508      */

509     public Collection JavaDoc parseMethodExpr(Wrappee wrappee, ClassItem cli, String JavaDoc expr) {
510         //Log.trace("pointcut.parse","parseMethodExpr "+expr+" for "+cli);
511
String JavaDoc[] exprs = Strings.split(expr,"&&");
512         Collection JavaDoc result = new HashSet JavaDoc();
513
514         if (wrappee==null) {
515             result.addAll(cli.getAllStaticMethods());
516         } else {
517             result.addAll(cli.getAllInstanceMethods());
518             result.addAll(cli.getConstructors());
519         }
520         for (int i=0; i<exprs.length; i++) {
521             String JavaDoc curExpr;
522             boolean inv = false;
523             exprs[i] = exprs[i].trim();
524             if (exprs[i].charAt(0)=='!') {
525                 inv = true;
526                 curExpr = exprs[i].substring(1).trim();
527             } else {
528                 curExpr = exprs[i];
529             }
530
531             String JavaDoc[] subExprs = Strings.split(curExpr,"||");
532             HashSet JavaDoc subExprResult = new HashSet JavaDoc();
533             for(int j=0; j<subExprs.length; j++) {
534                 String JavaDoc curSubExpr = subExprs[j].trim();
535                 filterMethodKeywords(wrappee,cli,curSubExpr,inv,result,subExprResult);
536             }
537             //System.out.println((inv?"!":"")+curExpr+" -> "+subExprResult);
538
result = subExprResult;
539         }
540         return result;
541     }
542
543     /**
544      * Adds methods from source that match an expression to a collection
545      *
546      * @param wrappee object to match with
547      * @param cli class to macth with, used if wrappee==null
548      * @param expr the expression to match
549      * @param inv wether to keep or reject matching methods
550      * @param source method items to chose from
551      * @param dest collection to add the matching methods to
552      */

553     protected void filterMethodKeywords(Object JavaDoc wrappee, ClassItem cli,
554                                         String JavaDoc expr, boolean inv,
555                                         Collection JavaDoc source, Collection JavaDoc dest) {
556
557         //System.out.println("EXPR="+(inv?"!":"")+expr+", CLI="+cli);
558
String JavaDoc keyword = null;
559         for (int i=0; i<methodKeywords.length && keyword==null; i++) {
560             if (expr.startsWith(methodKeywords[i])) {
561                 keyword = methodKeywords[i];
562                 //System.out.println(" KEYWORD="+keyword);
563
List JavaDoc parameters = null;
564             
565                 Iterator JavaDoc it = source.iterator();
566                 boolean add = false;
567                 while (it.hasNext()) {
568                     AbstractMethodItem method = (AbstractMethodItem)it.next();
569                     //System.out.println(" TESTING="+method);
570
add = false;
571                     if (keyword.equals("ALL")) {
572                         add = !inv;
573                     } else if (keyword.equals("MODIFIERS")) {
574                         if (parameters==null)
575                             parameters = parseParameters(expr.substring(keyword.length()),cli);
576                         add = (isWriter(method,parameters) ||
577                                isAdder(method,parameters) ||
578                                isRemover(method,parameters) ||
579                                isCollectionModifier(method,parameters))
580                             ^ inv;
581                     } else if (keyword.equals("ACCESSORS")) {
582                         add = method.isAccessor() ^ inv;
583                     } else if (keyword.equals("REMOVERS")) {
584                         if (parameters==null)
585                             parameters = parseParameters(expr.substring(keyword.length()),cli);
586                         add = isRemover(method,parameters) ^ inv;
587                     } else if (keyword.equals("ADDERS")) {
588                         if (parameters==null)
589                             parameters = parseParameters(expr.substring(keyword.length()),cli);
590                         add = isAdder(method,parameters) ^ inv;
591                     } else if (keyword.equals("SETTERS")) {
592                         if (parameters==null)
593                             parameters = parseParameters(expr.substring(keyword.length()),cli);
594                         add = isSetter(method,parameters) ^ inv;
595                     } else if (keyword.equals("STATICS")) {
596                         add = method.isStatic() ^ inv;
597                     } else if (keyword.equals("CONSTRUCTORS")) {
598                         add = (method instanceof ConstructorItem) ^ inv;
599                     } else if (keyword.equals("COLGETTERS")) {
600                         add = method.isCollectionGetter() ^ inv;
601                     } else if (keyword.equals("COLACCESSORS")) {
602                         if (parameters==null)
603                             parameters = parseParameters(expr.substring(keyword.length()),cli);
604                         add = isCollectionAccessor(method,parameters) ^ inv;
605                     } else if (keyword.equals("FIELDGETTERS")) {
606                         add = method.isFieldGetter() ^ inv;
607                     } else if (keyword.equals("REFGETTERS")) {
608                         add = method.isReferenceGetter() ^ inv;
609                     } else if (keyword.equals("REFACCESSORS")) {
610                         if (parameters==null)
611                             parameters = parseParameters(expr.substring(keyword.length()),cli);
612                         add = isReferenceAccessor(method,parameters) ^ inv;
613                     } else if (keyword.equals("COLSETTERS")) {
614                         add = method.isCollectionSetter() ^ inv;
615                     } else if (keyword.equals("FIELDSETTERS")) {
616                         add = method.isFieldSetter() ^ inv;
617                     } else if (keyword.equals("REFSETTERS")) {
618                         add = method.isReferenceSetter() ^ inv;
619                     } else if (keyword.equals("WRITERS")) {
620                         if (parameters==null)
621                             parameters = parseParameters(expr.substring(keyword.length()),cli);
622                         add = isWriter(method,parameters) ^ inv;
623                     } else if (keyword.equals("GETTERS")) {
624                         if (parameters==null)
625                             parameters = parseParameters(expr.substring(keyword.length()),cli);
626                         add = isGetter(method,parameters) ^ inv;
627                     }
628                     if (add) {
629                         dest.add(method.getConcreteMethod());
630                         it.remove();
631                     }
632                 }
633             }
634         }
635
636         // if no keyword was found, use regular expression matching
637
if (keyword==null) {
638             try {
639                 /*
640                   System.out.println("regexp matching for "+
641                   (inv?"!":"")+expr+" -> "+
642                   wrappingMethodName+" on "+wrappee);
643                   System.out.println("Methods = "+source);
644                 */

645                 RE re = new RE(Strings.replace(expr,"$","\\$"),0,
646                                RESyntax.RE_SYNTAX_EMACS);
647                 Iterator JavaDoc it = source.iterator();
648                 while (it.hasNext()) {
649                     AbstractMethodItem method = (AbstractMethodItem)it.next();
650                     if (re.isMatch(method.getFullName()) ^ inv) {
651                         dest.add(method);
652                         //System.out.println(" -> ADDED "+method);
653
}
654                 }
655                 //System.out.println("Matching methods = "+dest);
656
} catch (Exception JavaDoc e) {
657                 logger.error("filterMethodKeywords"+e);
658             }
659         }
660
661         //Log.trace("pointcut."+cli.getName(),expr+" MATCH "+result);
662
}
663
664     /**
665      * Tells wether a method is an adder of one a set of collections
666      * @param method the method to test
667      * @param collections the collection items. If null, it matches any
668      * collection.
669      * @return true if method is an adder of one of the collections
670      */

671     static boolean isAdder(AbstractMethodItem method, Collection JavaDoc collections) {
672         if (!method.isAdder()) {
673             return false;
674         } else {
675             if (collections==null) {
676                 return true;
677             } else {
678                 CollectionItem[] added = method.getAddedCollections();
679                 if (added!=null) {
680                     for (int i=0; i<added.length; i++) {
681                         if (collections.contains(added[i]))
682                             return true;
683                     }
684                 }
685                 return false;
686             }
687         }
688     }
689
690     /**
691      * Tells wether a method is a remover of one a set of collections
692      * @param method the method to test
693      * @param collections the collection items. If null, it matches any
694      * collection.
695      * @return true if method is a remover of one of the collections
696      */

697     static boolean isRemover(AbstractMethodItem method, Collection JavaDoc collections) {
698         if (!method.isRemover()) {
699             return false;
700         } else {
701             if (collections==null) {
702                 return true;
703             } else {
704                 CollectionItem[] removed = method.getRemovedCollections();
705                 if (removed!=null) {
706                     for (int i=0; i<removed.length; i++) {
707                         if (collections.contains(removed[i]))
708                             return true;
709                     }
710                 }
711                 return false;
712             }
713         }
714     }
715
716
717     /**
718      * Tells wether a method is a writer of one a set of fields
719      * @param method the method to test
720      * @param fields the field items. If null, it matches any
721      * field
722      * @return true if method is a writer of one of the fields
723      */

724     static boolean isWriter(AbstractMethodItem method, Collection JavaDoc fields) {
725         if (!method.isWriter()) {
726             return false;
727         } else {
728             if (fields==null) {
729                 return true;
730             } else {
731                 FieldItem[] written = method.getWrittenFields();
732                 if (written!=null) {
733                     for (int i=0; i<written.length; i++) {
734                         if (fields.contains(written[i]))
735                             return true;
736                     }
737                 }
738                 return false;
739             }
740         }
741     }
742
743     /**
744      * Tells wether a method is the setter of one of a set of fields
745      * @param method the method to test
746      * @param fields the field items. If null, it matches any
747      * field
748      * @return true if method is the setter of one of the fields
749      */

750     static boolean isSetter(AbstractMethodItem method, Collection JavaDoc fields) {
751         FieldItem setField = method.getSetField();
752         return (fields==null && setField!=null) ||
753             (fields!=null && fields.contains(setField));
754     }
755
756
757     /**
758      * Tells wether a method is the getter of one of a set of fields
759      * @param method the method to test
760      * @param fields the field items. If null, it matches any
761      * field
762      * @return true if method is the getter of one of the fields
763      */

764     static boolean isGetter(AbstractMethodItem method, Collection JavaDoc fields) {
765         if (method instanceof MethodItem) {
766             FieldItem setField = ((MethodItem)method).getReturnedField();
767             return (fields==null && setField!=null) ||
768                 (fields!=null && fields.contains(setField));
769         } else {
770             return false;
771         }
772     }
773
774     /**
775      * Tells wether a method is a refaccessor of one a set of references
776      * @param method the method to test
777      * @param collections the reference items. If null, it matches any
778      * reference.
779      * @return true if method is a reference accessor of one of the references
780      */

781     static boolean isReferenceAccessor(AbstractMethodItem method, Collection JavaDoc references) {
782         if (!method.isReferenceAccessor()) {
783             return false;
784         } else {
785             if (references==null) {
786                 return true;
787             } else {
788                 FieldItem[] refs = method.getAccessedReferences();
789                 if (refs!=null) {
790                     for (int i=0; i<refs.length; i++) {
791                         if (references.contains(refs[i]))
792                             return true;
793                     }
794                 }
795                 return false;
796             }
797         }
798     }
799
800     static boolean isCollectionAccessor(AbstractMethodItem method, Collection JavaDoc collections) {
801         if (!method.isCollectionAccessor()) {
802             return false;
803         } else {
804             if (collections==null) {
805                 return true;
806             } else {
807                 CollectionItem[] accessedCollections = method.getAccessedCollections();
808                 if (accessedCollections!=null) {
809                     for (int i=0; i<accessedCollections.length; i++) {
810                         if (collections.contains(accessedCollections[i]))
811                             return true;
812                     }
813                 }
814                 return false;
815             }
816         }
817     }
818
819     static boolean isCollectionModifier(AbstractMethodItem method, Collection JavaDoc collections) {
820         if (!method.hasModifiedCollections()) {
821             return false;
822         } else {
823             if (collections==null) {
824                 return true;
825             } else {
826                 CollectionItem[] modifiedCollections = method.getModifiedCollections();
827                 if (modifiedCollections!=null) {
828                     for (int i=0; i<modifiedCollections.length; i++) {
829                         if (collections.contains(modifiedCollections[i]))
830                             return true;
831                     }
832                 }
833                 return false;
834             }
835         }
836     }
837
838     Hashtable JavaDoc classCache = new Hashtable JavaDoc();
839
840     /**
841      * Tests if the given component class is modified (in a way or
842      * another) by this pointcut.
843      *
844      * @param wrappee the component to test
845      * @return true if the class matches */

846
847     public boolean isClassMatching(Wrappee wrappee, ClassItem cl) {
848         if (allClasses)
849             return true;
850         /*
851           Log.trace("pointcut.class",2,
852           "getting matching class for "+
853           cl+"("+NameRepository.get().getName(wrappee)+") for "+
854           wrappeeClassExpr+"/"+wrappingClassName+"."+wrappingMethodName);
855         */

856         String JavaDoc className = null;
857         if (cl==null) {
858             cl = ClassRepository.get().getClass(wrappee);
859         }
860         className = cl.getName();
861
862         Boolean JavaDoc match = (Boolean JavaDoc)classCache.get(className);
863         if (match==null) {
864             Iterator JavaDoc it = wrappeeClassRegexps.iterator();
865             Iterator JavaDoc iti = iwrappeeClassExprs.iterator();
866             try {
867                 match = Boolean.TRUE;
868                 while (it.hasNext()) {
869                     RE regexp = (RE)it.next();
870                     boolean inv = ((Boolean JavaDoc) iti.next()).booleanValue();
871                     /*
872                       Log.trace("pointcut.class",2,
873                       "isClassMatching: comparing "+className+" with "+
874                       regexp+" (inv="+inv+")");
875                     */

876                     if (cl.isSubClassOf(regexp) ^ inv) {
877                         /*
878                           Log.trace("pointcut.class","Class "+className+" does not match "+
879                           (inv?"":"!")+regexp);
880                         */

881                         match = Boolean.FALSE;
882                         break;
883                     }
884                 }
885             } catch(Exception JavaDoc e) {
886                 e.printStackTrace();
887             }
888             classCache.put(className,match);
889             /*
890               if (match.booleanValue())
891               Log.trace("pointcut.class","Class "+className+" matches "+
892               wrappeeClassExprs+iwrappeeClassExprs);
893             */

894         } else {
895             /*
896               Log.trace("pointcut.class",2,"class cache hit for "+
897               cl+"("+NameRepository.get().getName(wrappee)+") -> "+match);
898             */

899         }
900         return match.booleanValue();
901     }
902
903     /**
904      * Tests if the given component is modified (in a way or
905      * another) by this pointcut.
906      *
907      * <p>Contrary to the class-based matching, this matching works on
908      * a per-component basis and not on a per-class basis. This is
909      * posible by using the naming aspect of the system (assuming it is
910      * there). If the naming aspect appears not to be woven, then all
911      * the components should mathes here.
912      *
913      * @param wrappee the component to test
914      * @return true if the name matches
915      */

916     public boolean isNameMatching(Wrappee wrappee, ClassItem cl) {
917         if (allInstances) return true;
918         if (wrappee==null) return true;
919         String JavaDoc name = NameRepository.get().getName(wrappee);
920         if (name == null) {
921             //logger.info("Name "+name+" does not match "+wrappeeExprs);
922
return false;
923         }
924         return isNameMatching(wrappee,name);
925     }
926
927     public boolean isNameMatching(Wrappee wrappee,String JavaDoc name) {
928         loggerName.debug("isNameMatching "+name+","+wrappee);
929         Iterator JavaDoc it = wrappeeRegexps.iterator();
930         Iterator JavaDoc it2 = wrappeeExprs.iterator();
931         Iterator JavaDoc iti = iwrappeeExprs.iterator();
932         try {
933             while (it.hasNext()) {
934                 String JavaDoc s = (String JavaDoc)it2.next();
935                 if (isPathExpression(s)) {
936                     boolean result = isInPath(wrappee,s);
937                     /*
938                       if (result)
939                       logger.info("Name "+name+" matches "+wrappeeExprs);
940                       else
941                       logger.info("Name "+name+" does not match "+wrappeeExprs);
942                     */

943                     return result;
944                 }
945                 RE regexp = (RE)it.next();
946                 boolean inv = ((Boolean JavaDoc)iti.next()).booleanValue();
947                 /*
948                   Log.trace("wrap","isNameMatching: comparing "+name+" with "+
949                   regexp+" (inv="+inv);
950                 */

951                 if (regexp.isMatch(name) ^ inv) {
952                     /*
953                       logger.info("Name "+name+" does not match "+
954                       (inv?"":"!")+s);
955                     */

956                     return false;
957                 }
958             }
959         } catch (Exception JavaDoc e) {
960             e.printStackTrace();
961         }
962         return true;
963     }
964
965     /**
966      * Tells if this expression is a path expression (of the form o/r/o...).
967      *
968      * @param expr the expression to check
969      * @return true is a path expression */

970
971     public static boolean isPathExpression(String JavaDoc expr) {
972         if (expr.indexOf('/') == -1) {
973             return false;
974         } else {
975             return true;
976         }
977     }
978
979     /**
980      * Tells if this object is reachable for the given object path
981      * expression.
982      *
983      * @param candidate the candidate object
984      * @param pathExpr the path expression
985      * @return boolean true if reachable */

986
987     public static boolean isInPath(Object JavaDoc candidate, String JavaDoc pathExpr) {
988         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(pathExpr,"/");
989         String JavaDoc root = st.nextToken();
990         Collection JavaDoc accessible = NameRepository.getObjects(root);
991         try {
992             while (st.hasMoreTokens()) {
993                 loggerPath.debug("intermediate accessibles are "+accessible);
994                 String JavaDoc relExpr = st.nextToken();
995                 accessible = getRelatedObjects(accessible, relExpr);
996                 String JavaDoc filterExpr = st.nextToken();
997                 accessible = filterObjects(accessible, filterExpr);
998             }
999         } catch( Exception JavaDoc e ) {
1000            loggerPath.error("malformed path expression: "+pathExpr,e);
1001            return false;
1002        }
1003        loggerPath.debug("checking if "+candidate+
1004                         " matches the path expression "+pathExpr+
1005                         ", accessible objects are: "+accessible);
1006        return accessible.contains( candidate );
1007    }
1008
1009    /**
1010     * Gets all the objects that are related to a set of objects through
1011     * relations that match the given expression.
1012     *
1013     * @param initial the initial objects
1014     * @param relationExpr the expression that matches the relations
1015     * @return a set of accessible objects */

1016
1017    public static Collection JavaDoc getRelatedObjects(Collection JavaDoc initial,
1018                                               String JavaDoc relationExpr) {
1019        loggerPath.debug("getRelatedObjects "+relationExpr);
1020        Iterator JavaDoc it = initial.iterator();
1021        ClassRepository cr = ClassRepository.get();
1022        Vector JavaDoc res = new Vector JavaDoc();
1023        while( it.hasNext() ) {
1024            Object JavaDoc o = it.next();
1025            ClassItem cli = cr.getClass(o.getClass());
1026            loggerPath.debug("getting matching relations");
1027            Collection JavaDoc rels = cli.getMatchingRelations(relationExpr);
1028            loggerPath.debug("matching relations are "+rels);
1029            Iterator JavaDoc itRels = rels.iterator();
1030            while( itRels.hasNext() ) {
1031                FieldItem fi = (FieldItem) itRels.next();
1032                if( fi.isReference() ) {
1033                    Object JavaDoc ref = fi.get(o);
1034                    if( ref != null ) {
1035                        loggerPath.debug("adding referenced object "+ref);
1036                        res.add(ref);
1037                    }
1038                } else if (fi instanceof CollectionItem) {
1039                    Collection JavaDoc cref = ((CollectionItem)fi).getActualCollection(o);
1040                    if (cref != null) {
1041                        loggerPath.debug("adding objects in collection");
1042                        res.addAll(cref);
1043                    }
1044                }
1045                loggerPath.debug("performing next relation");
1046            }
1047        }
1048        return res;
1049    }
1050
1051    /**
1052     * Filters a collection of objects regarding an expression.
1053     *
1054     * @param initial the collection to filter
1055     * @param filter the filtering expression
1056     * @return a filtered collection that contains only the objects
1057     * that match the expression */

1058
1059    public static Collection JavaDoc filterObjects(Collection JavaDoc initial,
1060                                           String JavaDoc filter) {
1061        loggerPath.debug("filterObjects "+initial+"/"+filter);
1062        NameRepository nr = (NameRepository)NameRepository.get();
1063        Vector JavaDoc res = new Vector JavaDoc();
1064        try {
1065            // path expression allows the user to denote objects
1066
// with their index in the collection
1067
Integer JavaDoc index = new Integer JavaDoc(filter);
1068            res.add(((Vector JavaDoc)initial).get(index.intValue()));
1069            return res;
1070        } catch (Exception JavaDoc e) {}
1071
1072        if (initial==null)
1073            return res;
1074        Iterator JavaDoc it = initial.iterator();
1075        try {
1076            RE regexp = new RE(filter, 0, RESyntax.RE_SYNTAX_EMACS);
1077            while(it.hasNext()) {
1078                Object JavaDoc o = it.next();
1079                String JavaDoc name = nr.getName(o);
1080                if (name!=null && regexp.isMatch(name)) {
1081                    res.add(o);
1082                }
1083            }
1084        } catch( Exception JavaDoc e ) {
1085            e.printStackTrace();
1086        }
1087        return res;
1088    }
1089
1090    Hashtable JavaDoc hostCache = new Hashtable JavaDoc();
1091
1092    /**
1093     * Tests if the given component is modified (in a way or
1094     * another) by this pointcut.
1095     *
1096     * <p>Contrary to the class-based matching, this matching works on
1097     * a per-component basis and not on a per-class basis. This is
1098     * posible by using the naming aspect of the system (assuming it is
1099     * there). If the naming aspect appears not to be woven, then all
1100     * the components should mathes here.
1101     *
1102     * @param wrappee the component to test
1103     * @param cl the class, in case of static method
1104     * @return true if the name matches
1105     */

1106    public boolean isHostMatching(Wrappee wrappee, ClassItem cl) {
1107        if (allHosts) { return true; }
1108        String JavaDoc name = "";
1109        try {
1110            Class JavaDoc distd = Class.forName("org.objectweb.jac.core.dist.Distd");
1111            name = (String JavaDoc)distd.getMethod("getLocalContainerName",ExtArrays.emptyClassArray)
1112                .invoke(null,ExtArrays.emptyObjectArray);
1113        } catch (Exception JavaDoc e) {
1114            e.printStackTrace();
1115            return false;
1116        }
1117        if (name == null) {
1118            loggerHost.debug("Host "+name+" does not match "+
1119                      hostExprs+ihostExprs);
1120            return false;
1121        }
1122
1123        Boolean JavaDoc match = (Boolean JavaDoc)hostCache.get(name);
1124        if (match==null) {
1125            Iterator JavaDoc it = hostRegexps.iterator();
1126            Iterator JavaDoc iti = ihostExprs.iterator();
1127            match = Boolean.TRUE;
1128            try {
1129                while(it.hasNext()) {
1130                    RE regexp = (RE)it.next();
1131                    boolean inv = ((Boolean JavaDoc) iti.next()).booleanValue();
1132                    loggerHost.debug("isHostMatching: comparing "+name+" with "+
1133                              regexp+" (inv="+inv);
1134                    if (regexp.isMatch(name) ^ inv) {
1135                        loggerHost.debug("Host "+name+" does not match "+
1136                                         (inv?"":"!")+regexp);
1137                        match = Boolean.FALSE;
1138                        break;
1139                    }
1140                }
1141            } catch( Exception JavaDoc e ) {
1142                e.printStackTrace();
1143            }
1144            hostCache.put(name,match);
1145        }
1146        loggerHost.debug("Host \""+name+"\" is "+(match.booleanValue()?"":" not ")+
1147                         "matching "+hostExprs+ihostExprs);
1148        return match.booleanValue();
1149    }
1150
1151    /**
1152     * Tests if the given method item is modified (in a way or
1153     * another) by this pointcut.
1154     *
1155     * @param method the method to test
1156     * @return true if the method matches
1157     */

1158    public boolean isMethodMatching(AbstractMethodItem method) {
1159        Iterator JavaDoc it = wrappeeMethodRegexps.iterator();
1160        Iterator JavaDoc iti = iwrappeeMethodExprs.iterator();
1161        String JavaDoc name = method.getFullName();
1162
1163        while (it.hasNext()) {
1164            RE regexp = (RE)it.next();
1165            boolean inv = ((Boolean JavaDoc)iti.next()).booleanValue();
1166            if (regexp.isMatch(name) ^ inv) {
1167                return false;
1168            }
1169        }
1170        return true;
1171    }
1172
1173    FieldItem parameterToField(ClassItem cli, Object JavaDoc parameter) {
1174        if (parameter instanceof FieldItem)
1175            return (FieldItem)parameter;
1176        else if (parameter instanceof String JavaDoc) {
1177            return cli.getField((String JavaDoc)parameter);
1178        }
1179        else {
1180            logger.warn("Unknown parameter type "+
1181                        parameter.getClass().getName());
1182            return null;
1183        }
1184    }
1185
1186    boolean isNoneParameter(Object JavaDoc parameter) {
1187        if (parameter instanceof String JavaDoc &&
1188            "#NONE#".equals((String JavaDoc)parameter)) {
1189            return true;
1190        }
1191        return false;
1192    }
1193
1194    protected String JavaDoc parseKeyword(Wrappee wrappee, ClassItem cli,
1195                                  String JavaDoc keywordExpr,
1196                                  List JavaDoc parameters) {
1197
1198        loggerKeywords.debug("parseKeyword("+wrappee+","+cli+","+
1199                  keywordExpr+","+parameters+")");
1200        String JavaDoc result = "";
1201
1202        // replace attributes (<>) with the actual member values
1203
parameters = replaceTags(parameters,cli);
1204
1205        if (keywordExpr.equals("ALL")) {
1206            loggerKeywords.debug("found ALL keyword");
1207            result = ".*";
1208
1209        } else if (keywordExpr.equals("COLLECTIONS")) {
1210            loggerKeywords.debug("found COLLECTIONS keyword");
1211            result = "org.objectweb.jac.lib.java.util.*";
1212
1213        }
1214
1215        loggerKeywords.debug("parsed keyword "+keywordExpr+" => "+result);
1216
1217        return result;
1218
1219    }
1220
1221    static String JavaDoc quoteString(String JavaDoc string) {
1222        return Strings.replace(string,"[]","\\[\\]");
1223    }
1224
1225}
1226
Popular Tags