Source Code Cross Referenced for AspectJAdviceParameterNameDiscoverer.java in  » J2EE » spring-framework-2.0.6 » org » springframework » aop » aspectj » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » spring framework 2.0.6 » org.springframework.aop.aspectj 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright 2002-2007 the original author or authors.
003:         *
004:         * Licensed under the Apache License, Version 2.0 (the "License");
005:         * you may not use this file except in compliance with the License.
006:         * You may obtain a copy of the License at
007:         *
008:         *      http://www.apache.org/licenses/LICENSE-2.0
009:         *
010:         * Unless required by applicable law or agreed to in writing, software
011:         * distributed under the License is distributed on an "AS IS" BASIS,
012:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013:         * See the License for the specific language governing permissions and
014:         * limitations under the License.
015:         */
016:
017:        package org.springframework.aop.aspectj;
018:
019:        import java.lang.reflect.Constructor;
020:        import java.lang.reflect.Method;
021:        import java.util.ArrayList;
022:        import java.util.HashSet;
023:        import java.util.Iterator;
024:        import java.util.List;
025:        import java.util.Set;
026:
027:        import org.aspectj.lang.JoinPoint;
028:        import org.aspectj.lang.ProceedingJoinPoint;
029:        import org.aspectj.weaver.tools.PointcutParser;
030:        import org.aspectj.weaver.tools.PointcutPrimitive;
031:
032:        import org.springframework.core.ParameterNameDiscoverer;
033:        import org.springframework.util.ClassUtils;
034:        import org.springframework.util.StringUtils;
035:
036:        /**
037:         * {@link ParameterNameDiscoverer} implementation that tries to deduce parameter names
038:         * for an advice method from the pointcut expression, returning, and throwing clauses.
039:         * If an unambiguous interpretation is not available, it returns <code>null</code>.
040:         *
041:         * <p>This class interprets arguments in the following way:
042:         * <ol>
043:         * <li>If the first parameter of the method is of type {@link JoinPoint}
044:         * or {@link ProceedingJoinPoint}, it is assumed to be for passing
045:         * <code>thisJoinPoint</code> to the advice, and the parameter name will
046:         * be assigned the value <code>"thisJoinPoint"</code>.</li>
047:         * <li>If the first parameter of the method is of type
048:         * <code>JoinPoint.StaticPart</code>, it is assumed to be for passing
049:         * <code>"thisJoinPointStaticPart"</code> to the advice, and the parameter name
050:         * will be assigned the value <code>"thisJoinPointStaticPart"</code>.</li>
051:         * <li>If a {@link #setThrowingName(String) throwingName} has been set, and
052:         * there are no unbound arguments of type <code>Throwable+</code>, then an
053:         * {@link IllegalArgumentException} is raised. If there is more than one
054:         * unbound argument of type <code>Throwable+</code>, then an
055:         * {@link AmbiguousBindingException} is raised. If there is exactly one
056:         * unbound argument of type <code>Throwable+</code>, then the corresponding
057:         * parameter name is assigned the value &lt;throwingName&gt;.</li>
058:         * <li>If there remain unbound arguments, then the pointcut expression is
059:         * examined. Let <code>a</code> be the number of annotation-based pointcut
060:         * expressions (&#64;annotation, &#64;this, &#64;target, &#64;args,
061:         * &#64;within, &#64;withincode) that are used in binding form. Usage in
062:         * binding form has itself to be deduced: if the expression inside the
063:         * pointcut is a single string literal that meets Java variable name
064:         * conventions it is assumed to be a variable name. If <code>a</code> is
065:         * zero we proceed to the next stage. If <code>a</code> &gt; 1 then an
066:         * <code>AmbiguousBindingException</code> is raised. If <code>a</code> == 1,
067:         * and there are no unbound arguments of type <code>Annotation+</code>,
068:         * then an <code>IllegalArgumentException</code> is raised. if there is
069:         * exactly one such argument, then the corresponding parameter name is
070:         * assigned the value from the pointcut expression.</li>
071:         * <li>If a returningName has been set, and there are no unbound arguments
072:         * then an <code>IllegalArgumentException</code> is raised. If there is
073:         * more than one unbound argument then an
074:         * <code>AmbiguousBindingException</code> is raised. If there is exactly
075:         * one unbound argument then the corresponding parameter name is assigned
076:         * the value &lt;returningName&gt;.</li>
077:         * <li>If there remain unbound arguments, then the pointcut expression is
078:         * examined once more for <code>this</code>, <code>target</code>, and
079:         * <code>args</code> pointcut expressions used in the binding form (binding
080:         * forms are deduced as described for the annotation based pointcuts). If
081:         * there remains more than one unbound argument of a primitive type (which
082:         * can only be bound in <code>args</code>) then an
083:         * <code>AmbiguousBindingException</code> is raised. If there is exactly
084:         * one argument of a primitive type, then if exactly one <code>args</code>
085:         * bound variable was found, we assign the corresponding parameter name
086:         * the variable name. If there were no <code>args</code> bound variables
087:         * found an <code>IllegalStateException</code> is raised. If there are
088:         * multiple <code>args</code> bound variables, an
089:         * <code>AmbiguousBindingException</code> is raised. At this point, if
090:         * there remains more than one unbound argument we raise an
091:         * <code>AmbiguousBindingException</code>. If there are no unbound arguments
092:         * remaining, we are done. If there is exactly one unbound argument
093:         * remaining, and only one candidate variable name unbound from
094:         * <code>this</code>, <code>target</code>, or <code>args</code>, it is
095:         * assigned as the corresponding parameter name. If there are multiple
096:         * possibilities, an <code>AmbiguousBindingException</code> is raised.</li>
097:         * </ol>
098:         *
099:         * <p>The behavior on raising an <code>IllegalArgumentException</code> or
100:         * <code>AmbiguousBindingException</code> is configurable to allow this discoverer
101:         * to be used as part of a chain-of-responsibility. By default the condition will
102:         * be logged and the <code>getParameterNames(..)</code> method will simply return
103:         * <code>null</code>. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
104:         * property is set to <code>true</code>, the conditions will be thrown as
105:         * <code>IllegalArgumentException</code> and <code>AmbiguousBindingException</code>,
106:         * respectively.
107:         *
108:         * <p>Was that perfectly clear? ;)
109:         *
110:         * <p>Short version: If an unambiguous binding can be deduced, then it is.
111:         * If the advice requirements cannot possibly be satisfied, then <code>null</code>
112:         * is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
113:         * property to <code>true</code>, descriptive exceptions will be thrown instead of
114:         * returning <code>null</code> in the case that the parameter names cannot be discovered.
115:         *
116:         * @author Adrian Colyer
117:         * @since 2.0
118:         */
119:        public class AspectJAdviceParameterNameDiscoverer implements 
120:                ParameterNameDiscoverer {
121:
122:            private static final String ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
123:
124:            private static final String THIS_JOIN_POINT = "thisJoinPoint";
125:            private static final String THIS_JOIN_POINT_STATIC_PART = "thisJoinPointStaticPart";
126:
127:            // Steps in the binding algorithm...
128:            private static final int STEP_JOIN_POINT_BINDING = 1;
129:            private static final int STEP_THROWING_BINDING = 2;
130:            private static final int STEP_ANNOTATION_BINDING = 3;
131:            private static final int STEP_RETURNING_BINDING = 4;
132:            private static final int STEP_PRIMITIVE_ARGS_BINDING = 5;
133:            private static final int STEP_THIS_TARGET_ARGS_BINDING = 6;
134:            private static final int STEP_REFERENCE_PCUT_BINDING = 7;
135:            private static final int STEP_FINISHED = 8;
136:
137:            private static final Set singleValuedAnnotationPcds = new HashSet();
138:            private static final Set nonReferencePointcutTokens = new HashSet();
139:
140:            private static Class annotationClass;
141:
142:            static {
143:                singleValuedAnnotationPcds.add("@this");
144:                singleValuedAnnotationPcds.add("@target");
145:                singleValuedAnnotationPcds.add("@within");
146:                singleValuedAnnotationPcds.add("@withincode");
147:                singleValuedAnnotationPcds.add("@annotation");
148:
149:                Set pointcutPrimitives = PointcutParser
150:                        .getAllSupportedPointcutPrimitives();
151:                for (Iterator iterator = pointcutPrimitives.iterator(); iterator
152:                        .hasNext();) {
153:                    PointcutPrimitive primitive = (PointcutPrimitive) iterator
154:                            .next();
155:                    nonReferencePointcutTokens.add(primitive.getName());
156:                }
157:                nonReferencePointcutTokens.add("&&");
158:                nonReferencePointcutTokens.add("!");
159:                nonReferencePointcutTokens.add("||");
160:                nonReferencePointcutTokens.add("and");
161:                nonReferencePointcutTokens.add("or");
162:                nonReferencePointcutTokens.add("not");
163:
164:                try {
165:                    annotationClass = ClassUtils.forName(ANNOTATION_CLASS_NAME,
166:                            AspectJAdviceParameterNameDiscoverer.class
167:                                    .getClassLoader());
168:                } catch (ClassNotFoundException ex) {
169:                    // Running on < JDK 1.5, this is OK...
170:                    annotationClass = null;
171:                }
172:            }
173:
174:            private boolean raiseExceptions;
175:
176:            /**
177:             * If the advice is afterReturning, and binds the return value, this is the parameter name used.
178:             */
179:            private String returningName;
180:
181:            /**
182:             * If the advice is afterThrowing, and binds the thrown value, this is the parameter name used.
183:             */
184:            private String throwingName;
185:
186:            /**
187:             * The pointcut expression associated with the advice, as a simple String.
188:             */
189:            private String pointcutExpression;
190:
191:            private Class[] argumentTypes;
192:
193:            private String[] parameterNameBindings;
194:
195:            private int numberOfRemainingUnboundArguments;
196:
197:            private int algorithmicStep = STEP_JOIN_POINT_BINDING;
198:
199:            /**
200:             * Create a new discoverer that attempts to discover parameter names
201:             * from the given pointcut expression.
202:             */
203:            public AspectJAdviceParameterNameDiscoverer(
204:                    String pointcutExpression) {
205:                this .pointcutExpression = pointcutExpression;
206:            }
207:
208:            /**
209:             * Indicate whether {@link IllegalArgumentException} and {@link AmbiguousBindingException}
210:             * must be thrown as appropriate in the case of failing to deduce advice parameter names.
211:             * @param raiseExceptions <code>true</code> if exceptions are to be thrown
212:             */
213:            public void setRaiseExceptions(boolean raiseExceptions) {
214:                this .raiseExceptions = raiseExceptions;
215:            }
216:
217:            /**
218:             * If <code>afterReturning</code> advice binds the return value, the
219:             * returning variable name must be specified.
220:             * @param returningName the name of the returning variable
221:             */
222:            public void setReturningName(String returningName) {
223:                this .returningName = returningName;
224:            }
225:
226:            /**
227:             * If <code>afterThrowing</code> advice binds the thrown value, the
228:             * throwing variable name must be specified.
229:             * @param throwingName the name of the throwing variable
230:             */
231:            public void setThrowingName(String throwingName) {
232:                this .throwingName = throwingName;
233:            }
234:
235:            /**
236:             * Deduce the parameter names for an advice method.
237:             * <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
238:             * for this class for details of the algorithm used.
239:             * @param method the target {@link Method}
240:             * @return the parameter names
241:             */
242:            public String[] getParameterNames(Method method) {
243:                this .argumentTypes = method.getParameterTypes();
244:                this .numberOfRemainingUnboundArguments = this .argumentTypes.length;
245:                this .parameterNameBindings = new String[this .numberOfRemainingUnboundArguments];
246:                this .algorithmicStep = STEP_JOIN_POINT_BINDING;
247:
248:                int minimumNumberUnboundArgs = 0;
249:                if (this .returningName != null) {
250:                    minimumNumberUnboundArgs++;
251:                }
252:                if (this .throwingName != null) {
253:                    minimumNumberUnboundArgs++;
254:                }
255:                if (this .numberOfRemainingUnboundArguments < minimumNumberUnboundArgs) {
256:                    throw new IllegalStateException(
257:                            "Not enough arguments in method to satisfy binding of returning and throwing variables");
258:                }
259:
260:                try {
261:                    while ((this .numberOfRemainingUnboundArguments > 0)
262:                            && (this .algorithmicStep < STEP_FINISHED)) {
263:                        switch (this .algorithmicStep++) {
264:                        case STEP_JOIN_POINT_BINDING:
265:                            if (!maybeBindThisJoinPoint()) {
266:                                maybeBindThisJoinPointStaticPart();
267:                            }
268:                            break;
269:                        case STEP_THROWING_BINDING:
270:                            maybeBindThrowingVariable();
271:                            break;
272:                        case STEP_ANNOTATION_BINDING:
273:                            maybeBindAnnotationsFromPointcutExpression();
274:                            break;
275:                        case STEP_RETURNING_BINDING:
276:                            maybeBindReturningVariable();
277:                            break;
278:                        case STEP_PRIMITIVE_ARGS_BINDING:
279:                            maybeBindPrimitiveArgsFromPointcutExpression();
280:                            break;
281:                        case STEP_THIS_TARGET_ARGS_BINDING:
282:                            maybeBindThisOrTargetOrArgsFromPointcutExpression();
283:                            break;
284:                        case STEP_REFERENCE_PCUT_BINDING:
285:                            maybeBindReferencePointcutParameter();
286:                            break;
287:                        default:
288:                            throw new IllegalStateException(
289:                                    "Unknown algorithmic step: "
290:                                            + (this .algorithmicStep - 1));
291:                        }
292:                    }
293:                } catch (AmbiguousBindingException ambigEx) {
294:                    if (this .raiseExceptions) {
295:                        throw ambigEx;
296:                    } else {
297:                        return null;
298:                    }
299:                } catch (IllegalArgumentException ex) {
300:                    if (this .raiseExceptions) {
301:                        throw ex;
302:                    } else {
303:                        return null;
304:                    }
305:                }
306:
307:                if (this .numberOfRemainingUnboundArguments == 0) {
308:                    return this .parameterNameBindings;
309:                } else {
310:                    if (this .raiseExceptions) {
311:                        throw new IllegalStateException(
312:                                "Failed to bind all argument names: "
313:                                        + this .numberOfRemainingUnboundArguments
314:                                        + " argument(s) could not be bound");
315:                    } else {
316:                        // convention for failing is to return null, allowing participation in a chain of responsibility
317:                        return null;
318:                    }
319:                }
320:            }
321:
322:            /**
323:             * An advice method can never be a constructor in Spring.
324:             * @return <code>null</code>
325:             * @throws UnsupportedOperationException if
326:             * {@link #setRaiseExceptions(boolean) raiseExceptions} has been set to <code>true</code>
327:             */
328:            public String[] getParameterNames(Constructor ctor) {
329:                if (this .raiseExceptions) {
330:                    throw new UnsupportedOperationException(
331:                            "An advice method can never be a constructor");
332:                } else {
333:                    // we return null rather than throw an exception so that we behave well
334:                    // in a chain-of-responsibility.
335:                    return null;
336:                }
337:            }
338:
339:            private void bindParameterName(int index, String name) {
340:                this .parameterNameBindings[index] = name;
341:                this .numberOfRemainingUnboundArguments--;
342:            }
343:
344:            /**
345:             * If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
346:             * parameter name and return true, else return false.
347:             */
348:            private boolean maybeBindThisJoinPoint() {
349:                if ((this .argumentTypes[0] == JoinPoint.class)
350:                        || (this .argumentTypes[0] == ProceedingJoinPoint.class)) {
351:                    bindParameterName(0, THIS_JOIN_POINT);
352:                    return true;
353:                } else {
354:                    return false;
355:                }
356:            }
357:
358:            private void maybeBindThisJoinPointStaticPart() {
359:                if (this .argumentTypes[0] == JoinPoint.StaticPart.class) {
360:                    bindParameterName(0, THIS_JOIN_POINT_STATIC_PART);
361:                }
362:            }
363:
364:            /**
365:             * If a throwing name was specified and there is exactly one choice remaining
366:             * (argument that is a subtype of Throwable) then bind it.
367:             */
368:            private void maybeBindThrowingVariable() {
369:                if (this .throwingName == null) {
370:                    return;
371:                }
372:
373:                // So there is binding work to do...
374:                int throwableIndex = -1;
375:                for (int i = 0; i < this .argumentTypes.length; i++) {
376:                    if (isUnbound(i) && isSubtypeOf(Throwable.class, i)) {
377:                        if (throwableIndex == -1) {
378:                            throwableIndex = i;
379:                        } else {
380:                            // Second candidate we've found - ambiguous binding
381:                            throw new AmbiguousBindingException(
382:                                    "Binding of throwing parameter '"
383:                                            + this .throwingName
384:                                            + "' is ambiguous: could be bound to argument "
385:                                            + throwableIndex + " or argument "
386:                                            + i);
387:                        }
388:                    }
389:                }
390:
391:                if (throwableIndex == -1) {
392:                    throw new IllegalStateException(
393:                            "Binding of throwing parameter '"
394:                                    + this .throwingName
395:                                    + "' could not be completed as no available arguments are a subtype of Throwable");
396:                } else {
397:                    bindParameterName(throwableIndex, this .throwingName);
398:                }
399:            }
400:
401:            /**
402:             * If a returning variable was specified and there is only one choice remaining, bind it.
403:             */
404:            private void maybeBindReturningVariable() {
405:                if (this .numberOfRemainingUnboundArguments == 0) {
406:                    throw new IllegalStateException(
407:                            "Algorithm assumes that there must be at least one unbound parameter on entry to this method");
408:                }
409:
410:                if (this .returningName != null) {
411:                    if (this .numberOfRemainingUnboundArguments > 1) {
412:                        throw new AmbiguousBindingException(
413:                                "Binding of returning parameter '"
414:                                        + this .returningName
415:                                        + "' is ambiguous, there are "
416:                                        + this .numberOfRemainingUnboundArguments
417:                                        + " candidates.");
418:                    }
419:
420:                    // We're all set... find the unbound parameter, and bind it.
421:                    for (int i = 0; i < this .parameterNameBindings.length; i++) {
422:                        if (this .parameterNameBindings[i] == null) {
423:                            bindParameterName(i, this .returningName);
424:                            break;
425:                        }
426:                    }
427:                }
428:            }
429:
430:            /**
431:             * Parse the string pointcut expression looking for:
432:             * &#64;this, &#64;target, &#64;args, &#64;within, &#64;withincode, &#64;annotation.
433:             * If we find one of these pointcut expressions, try and extract a candidate variable
434:             * name (or variable names, in the case of args).
435:             * <p>Some more support from AspectJ in doing this exercise would be nice... :)
436:             */
437:            private void maybeBindAnnotationsFromPointcutExpression() {
438:                List varNames = new ArrayList();
439:                String[] tokens = StringUtils.tokenizeToStringArray(
440:                        this .pointcutExpression, " ");
441:                for (int i = 0; i < tokens.length; i++) {
442:                    String toMatch = tokens[i];
443:                    int firstParenIndex = toMatch.indexOf("(");
444:                    if (firstParenIndex != -1) {
445:                        toMatch = toMatch.substring(0, firstParenIndex);
446:                    }
447:                    if (singleValuedAnnotationPcds.contains(toMatch)) {
448:                        PointcutBody body = getPointcutBody(tokens, i);
449:                        i += body.numTokensConsumed;
450:                        String varName = maybeExtractVariableName(body.text);
451:                        if (varName != null) {
452:                            varNames.add(varName);
453:                        }
454:                    } else if (tokens[i].startsWith("@args(")
455:                            || tokens[i].equals("@args")) {
456:                        PointcutBody body = getPointcutBody(tokens, i);
457:                        i += body.numTokensConsumed;
458:                        maybeExtractVariableNamesFromArgs(body.text, varNames);
459:                    }
460:                }
461:
462:                bindAnnotationsFromVarNames(varNames);
463:            }
464:
465:            /**
466:             * Match the given list of extracted variable names to argument slots.
467:             */
468:            private void bindAnnotationsFromVarNames(List varNames) {
469:                if (!varNames.isEmpty()) {
470:                    // we have work to do...
471:                    int numAnnotationSlots = countNumberOfUnboundAnnotationArguments();
472:                    if (numAnnotationSlots > 1) {
473:                        throw new AmbiguousBindingException("Found "
474:                                + varNames.size()
475:                                + " potential annotation variable(s), and "
476:                                + numAnnotationSlots
477:                                + " potential argument slots");
478:                    } else if (numAnnotationSlots == 1) {
479:                        if (varNames.size() == 1) {
480:                            // it's a match
481:                            findAndBind(annotationClass, (String) varNames
482:                                    .get(0));
483:                        } else {
484:                            // multiple candidate vars, but only one slot
485:                            throw new IllegalArgumentException(
486:                                    "Found "
487:                                            + varNames.size()
488:                                            + " candidate annotation binding variables"
489:                                            + " but only one potential argument binding slot");
490:                        }
491:                    } else {
492:                        // no slots so presume those candidate vars were actually type names
493:                    }
494:                }
495:            }
496:
497:            /*
498:             * If the token starts meets Java identifier conventions, it's in.
499:             */
500:            private String maybeExtractVariableName(String candidateToken) {
501:                if (candidateToken == null || candidateToken.equals("")) {
502:                    return null;
503:                }
504:                if (Character.isJavaIdentifierStart(candidateToken.charAt(0))
505:                        && Character.isLowerCase(candidateToken.charAt(0))) {
506:                    char[] tokenChars = candidateToken.toCharArray();
507:                    for (int i = 0; i < tokenChars.length; i++) {
508:                        if (!Character.isJavaIdentifierPart(tokenChars[i])) {
509:                            return null;
510:                        }
511:                    }
512:                    return candidateToken;
513:                } else {
514:                    return null;
515:                }
516:            }
517:
518:            /**
519:             * Given an args pointcut body (could be <code>args</code> or <code>at_args</code>),
520:             * add any candidate variable names to the given list.
521:             */
522:            private void maybeExtractVariableNamesFromArgs(String argsSpec,
523:                    List varNames) {
524:                if (argsSpec == null) {
525:                    return;
526:                }
527:
528:                String[] tokens = StringUtils.tokenizeToStringArray(argsSpec,
529:                        ",");
530:                for (int i = 0; i < tokens.length; i++) {
531:                    tokens[i] = StringUtils.trimWhitespace(tokens[i]);
532:                    String varName = maybeExtractVariableName(tokens[i]);
533:                    if (varName != null) {
534:                        varNames.add(varName);
535:                    }
536:                }
537:            }
538:
539:            /**
540:             * Parse the string pointcut expression looking for this(), target() and args() expressions.
541:             * If we find one, try and extract a candidate variable name and bind it.
542:             */
543:            private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
544:                if (this .numberOfRemainingUnboundArguments > 1) {
545:                    throw new AmbiguousBindingException(
546:                            "Still "
547:                                    + this .numberOfRemainingUnboundArguments
548:                                    + " unbound args at this(),target(),args() binding stage, with no way to determine between them");
549:                }
550:
551:                List varNames = new ArrayList();
552:                String[] tokens = StringUtils.tokenizeToStringArray(
553:                        this .pointcutExpression, " ");
554:                for (int i = 0; i < tokens.length; i++) {
555:                    if (tokens[i].equals("this")
556:                            || tokens[i].startsWith("this(")
557:                            || tokens[i].equals("target")
558:                            || tokens[i].startsWith("target(")) {
559:                        PointcutBody body = getPointcutBody(tokens, i);
560:                        i += body.numTokensConsumed;
561:                        String varName = maybeExtractVariableName(body.text);
562:                        if (varName != null) {
563:                            varNames.add(varName);
564:                        }
565:                    } else if (tokens[i].equals("args")
566:                            || tokens[i].startsWith("args(")) {
567:                        PointcutBody body = getPointcutBody(tokens, i);
568:                        i += body.numTokensConsumed;
569:                        List candidateVarNames = new ArrayList();
570:                        maybeExtractVariableNamesFromArgs(body.text,
571:                                candidateVarNames);
572:                        // we may have found some var names that were bound in previous primitive args binding step,
573:                        // filter them out...
574:                        for (Iterator iter = candidateVarNames.iterator(); iter
575:                                .hasNext();) {
576:                            String varName = (String) iter.next();
577:                            if (!alreadyBound(varName)) {
578:                                varNames.add(varName);
579:                            }
580:                        }
581:                    }
582:                }
583:
584:                if (varNames.size() > 1) {
585:                    throw new AmbiguousBindingException(
586:                            "Found "
587:                                    + varNames.size()
588:                                    + " candidate this(), target() or args() variables but only one unbound argument slot");
589:                } else if (varNames.size() == 1) {
590:                    for (int j = 0; j < this .parameterNameBindings.length; j++) {
591:                        if (isUnbound(j)) {
592:                            bindParameterName(j, (String) varNames.get(0));
593:                            break;
594:                        }
595:                    }
596:                }
597:                // else varNames.size must be 0 and we have nothing to bind.
598:            }
599:
600:            private void maybeBindReferencePointcutParameter() {
601:                if (this .numberOfRemainingUnboundArguments > 1) {
602:                    throw new AmbiguousBindingException(
603:                            "Still "
604:                                    + this .numberOfRemainingUnboundArguments
605:                                    + " unbound args at reference pointcut binding stage, with no way to determine between them");
606:                }
607:
608:                List varNames = new ArrayList();
609:                String[] tokens = StringUtils.tokenizeToStringArray(
610:                        this .pointcutExpression, " ");
611:                for (int i = 0; i < tokens.length; i++) {
612:                    String toMatch = tokens[i];
613:                    if (toMatch.startsWith("!")) {
614:                        toMatch = toMatch.substring(1);
615:                    }
616:                    int firstParenIndex = toMatch.indexOf("(");
617:                    if (firstParenIndex != -1) {
618:                        toMatch = toMatch.substring(0, firstParenIndex);
619:                    } else {
620:                        if (tokens.length < i + 2) {
621:                            // no "(" and nothing following
622:                            continue;
623:                        } else {
624:                            String nextToken = tokens[i + 1];
625:                            if (nextToken.charAt(0) != '(') {
626:                                // next token is not "(" either, can't be a pc...
627:                                continue;
628:                            }
629:                        }
630:
631:                    }
632:
633:                    // eat the body
634:                    PointcutBody body = getPointcutBody(tokens, i);
635:                    i += body.numTokensConsumed;
636:
637:                    if (!nonReferencePointcutTokens.contains(toMatch)) {
638:                        // then it could be a reference pointcut
639:                        String varName = maybeExtractVariableName(body.text);
640:                        if (varName != null) {
641:                            varNames.add(varName);
642:                        }
643:                    }
644:                }
645:
646:                if (varNames.size() > 1) {
647:                    throw new AmbiguousBindingException(
648:                            "Found "
649:                                    + varNames.size()
650:                                    + " candidate reference pointcut variables but only one unbound argument slot");
651:                } else if (varNames.size() == 1) {
652:                    for (int j = 0; j < this .parameterNameBindings.length; j++) {
653:                        if (isUnbound(j)) {
654:                            bindParameterName(j, (String) varNames.get(0));
655:                            break;
656:                        }
657:                    }
658:                }
659:                // else varNames.size must be 0 and we have nothing to bind.
660:            }
661:
662:            /*
663:             * We've found the start of a binding pointcut at the given index into the
664:             * token array. Now we need to extract the pointcut body and return it.
665:             */
666:            private PointcutBody getPointcutBody(String[] tokens, int startIndex) {
667:                int numTokensConsumed = 0;
668:                String currentToken = tokens[startIndex];
669:                int bodyStart = currentToken.indexOf('(');
670:                if (currentToken.charAt(currentToken.length() - 1) == ')') {
671:                    // It's an all in one... get the text between the first (and the last)
672:                    return new PointcutBody(0, currentToken.substring(
673:                            bodyStart + 1, currentToken.length() - 1));
674:                } else {
675:                    StringBuffer sb = new StringBuffer();
676:                    if (bodyStart >= 0
677:                            && bodyStart != (currentToken.length() - 1)) {
678:                        sb.append(currentToken.substring(bodyStart + 1));
679:                        sb.append(" ");
680:                    }
681:                    numTokensConsumed++;
682:                    int currentIndex = startIndex + numTokensConsumed;
683:                    while (currentIndex < tokens.length) {
684:                        if (tokens[currentIndex].equals("(")) {
685:                            currentIndex++;
686:                            continue;
687:                        }
688:
689:                        if (tokens[currentIndex].endsWith(")")) {
690:                            sb.append(tokens[currentIndex].substring(0,
691:                                    tokens[currentIndex].length() - 1));
692:                            return new PointcutBody(numTokensConsumed, sb
693:                                    .toString().trim());
694:                        }
695:
696:                        String toAppend = tokens[currentIndex];
697:                        if (toAppend.startsWith("(")) {
698:                            toAppend = toAppend.substring(1);
699:                        }
700:                        sb.append(toAppend);
701:                        sb.append(" ");
702:                        currentIndex++;
703:                        numTokensConsumed++;
704:                    }
705:
706:                }
707:
708:                // We looked and failed...
709:                return new PointcutBody(numTokensConsumed, null);
710:            }
711:
712:            /**
713:             * Match up args against unbound arguments of primitive types
714:             */
715:            private void maybeBindPrimitiveArgsFromPointcutExpression() {
716:                int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments();
717:                if (numUnboundPrimitives > 1) {
718:                    throw new AmbiguousBindingException(
719:                            "Found '"
720:                                    + numUnboundPrimitives
721:                                    + "' unbound primitive arguments with no way to distinguish between them.");
722:                }
723:                if (numUnboundPrimitives == 1) {
724:                    // Look for arg variable and bind it if we find exactly one...
725:                    List varNames = new ArrayList();
726:                    String[] tokens = StringUtils.tokenizeToStringArray(
727:                            this .pointcutExpression, " ");
728:                    for (int i = 0; i < tokens.length; i++) {
729:                        if (tokens[i].equals("args")
730:                                || tokens[i].startsWith("args(")) {
731:                            PointcutBody body = getPointcutBody(tokens, i);
732:                            i += body.numTokensConsumed;
733:                            maybeExtractVariableNamesFromArgs(body.text,
734:                                    varNames);
735:                        }
736:                    }
737:                    if (varNames.size() > 1) {
738:                        throw new AmbiguousBindingException(
739:                                "Found "
740:                                        + varNames.size()
741:                                        + " candidate variable names but only one candidate binding slot when matching primitive args");
742:                    } else if (varNames.size() == 1) {
743:                        // 1 primitive arg, and one candidate...
744:                        for (int i = 0; i < this .argumentTypes.length; i++) {
745:                            if (isUnbound(i)
746:                                    && this .argumentTypes[i].isPrimitive()) {
747:                                bindParameterName(i, (String) varNames.get(0));
748:                                break;
749:                            }
750:                        }
751:                    }
752:                }
753:            }
754:
755:            /*
756:             * Return true if the parameter name binding for the given parameter
757:             * index has not yet been assigned.
758:             */
759:            private boolean isUnbound(int i) {
760:                return this .parameterNameBindings[i] == null;
761:            }
762:
763:            private boolean alreadyBound(String varName) {
764:                for (int i = 0; i < this .parameterNameBindings.length; i++) {
765:                    if (!isUnbound(i)
766:                            && varName.equals(this .parameterNameBindings[i])) {
767:                        return true;
768:                    }
769:                }
770:                return false;
771:            }
772:
773:            /*
774:             * Return <code>true</code> if the given argument type is a subclass
775:             * of the given supertype.
776:             */
777:            private boolean isSubtypeOf(Class super type, int argumentNumber) {
778:                return super type
779:                        .isAssignableFrom(this .argumentTypes[argumentNumber]);
780:            }
781:
782:            private int countNumberOfUnboundAnnotationArguments() {
783:                if (annotationClass == null) {
784:                    // We're running on a JDK < 1.5
785:                    return 0;
786:                }
787:
788:                int count = 0;
789:                for (int i = 0; i < this .argumentTypes.length; i++) {
790:                    if (isUnbound(i) && isSubtypeOf(annotationClass, i)) {
791:                        count++;
792:                    }
793:                }
794:                return count;
795:            }
796:
797:            private int countNumberOfUnboundPrimitiveArguments() {
798:                int count = 0;
799:                for (int i = 0; i < this .argumentTypes.length; i++) {
800:                    if (isUnbound(i) && this .argumentTypes[i].isPrimitive()) {
801:                        count++;
802:                    }
803:                }
804:                return count;
805:            }
806:
807:            /*
808:             * Find the argument index with the given type, and bind the given
809:             * <code>varName</code> in that position.
810:             */
811:            private void findAndBind(Class argumentType, String varName) {
812:                for (int i = 0; i < this .argumentTypes.length; i++) {
813:                    if (isUnbound(i) && isSubtypeOf(argumentType, i)) {
814:                        bindParameterName(i, varName);
815:                        return;
816:                    }
817:                }
818:                throw new IllegalStateException(
819:                        "Expected to find an unbound argument of type '"
820:                                + argumentType.getName() + "'");
821:            }
822:
823:            /**
824:             * Simple struct to hold the extracted text from a pointcut body, together
825:             * with the number of tokens consumed in extracting it.
826:             */
827:            private static class PointcutBody {
828:
829:                private int numTokensConsumed;
830:
831:                private String text;
832:
833:                public PointcutBody(int tokens, String text) {
834:                    this .numTokensConsumed = tokens;
835:                    this .text = text;
836:                }
837:            }
838:
839:            /**
840:             * Thrown in response to an ambiguous binding being detected when
841:             * trying to resolve a method's parameter names.
842:             */
843:            public static class AmbiguousBindingException extends
844:                    RuntimeException {
845:
846:                /**
847:                 * Construct a new AmbiguousBindingException with the specified message.
848:                 * @param msg the detail message
849:                 */
850:                public AmbiguousBindingException(String msg) {
851:                    super(msg);
852:                }
853:            }
854:
855:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.