root/trunk/src/java/org/jcoderz/commons/tracing/AspectPattern.java

Revision 1628, 15.8 kB (checked in by amandel, 2 years ago)

Add work in progress tracing components.
- Extended TracingProxy? that allows different Traces to be used
- TracingInjector? that allows aspect like injection of the tracing aspect.

Please note that this is still work in progress.

  • Property svn:mime-type set to text/plain
Line 
1/*
2 * $Id: ArraysUtil.java 1011 2008-06-16 17:57:36Z amandel $
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.commons.tracing;
34
35
36import java.util.Arrays;
37import java.util.Iterator;
38import java.util.regex.Pattern;
39
40import org.jcoderz.commons.ArgumentMalformedException;
41import org.objectweb.asm.Opcodes;
42import org.objectweb.asm.Type;
43
44
45/**
46 * This class allows to match aspectJ like patterns with the java
47 * internal class representation.
48 * <p>
49 * The following limitations / specialties should be considered:
50 * <ul>
51 * <li>the '+' is not supported so you can not check for classes
52 * implementing a certain interface.</li>
53 * <li><code>throws</code> is not supported as a part of the matching
54 * signature.</li>
55 * <li>The pattern is based on the information you can find at <a
56 * href="http://aspectwerkz.codehaus.org/definition_issues.html">
57 * aspectwerkz</a></li>
58 * <li>The method name <code>new</code> can be used to refer to the
59 * constructor (<code>&lt;init></code>) and to the class (
60 * <code>&lt;clinit></code>) initialization.</li>
61 * </ul>
62 * TODO: Javadoc
63 * TODO: Support '+' for inheritance
64 *
65 * @author Andreas Mandel
66 */
67public class AspectPattern
68{
69    private final String mPattern;
70
71    private final String mClassRegex;
72
73    private final String mMethodRegex;
74
75    private String mReturnPattern;
76
77    /** The tracer class that should be called if the pattern matches. */
78    private String mTracerClass;
79
80    private int mModifiers;
81
82    private Pattern mClassMatcher;
83
84    private Pattern mMethodMatcher;
85
86    /**
87     * Creates new aspect pattern matcher. The input is translated into
88     * regular expressions which are compiled and stored in this
89     * AspectPattern.
90     *
91     * @param in the aspectJ like pattern.
92     */
93    public AspectPattern (String in)
94    {
95        mPattern = in;
96        int pos = 0;
97        pos = getTracerClass(0);
98        pos = getModifiers(pos);
99        pos = getReturnType(pos);
100        final int paraPos = mPattern.indexOf('(', pos);
101        final int methodPos = mPattern.substring(pos, paraPos).lastIndexOf('.')
102            + pos;
103        // TODO Support +!
104        mClassRegex = convertToPattern(mPattern.substring(pos, methodPos));
105        final String methodName = convertToMethod(mPattern.substring(
106            methodPos + 1, paraPos));
107        final String arguments = convertToArgumets(mPattern.substring(
108            paraPos + 1, mPattern.length() - 1));
109        mMethodRegex = methodName + "\\(" + arguments + "\\)" + mReturnPattern;
110    }
111
112    /**
113     * Parse the optional name of the tracer class to be used.
114     * As a side effect {@link #mTracerClass} is set.
115     * @param i the pos where to start parsing
116     * @return the pos after the tracer class name.
117     */
118    private int getTracerClass (int i)
119    {
120        int pos = i;
121        int end = i;
122        while (pos < mPattern.length()
123            && Character.isWhitespace(mPattern.charAt(pos)))
124        {
125            pos++;
126        }
127        if (pos < mPattern.length()
128            && mPattern.charAt(pos) == '(')
129        {
130            end = mPattern.indexOf(')', pos);
131            if (end == -1)
132            {
133                throw new ArgumentMalformedException(
134                    "pattern", mPattern,
135                    "Opening '(' for tracing class name is not closed.");
136            }
137            mTracerClass = mPattern.substring(pos + 1, end);
138            end += 1;
139            while (end < mPattern.length()
140                && Character.isWhitespace(mPattern.charAt(end)))
141            {
142                end++;
143            }
144        }
145        return end;
146    }
147
148    /**
149     * Tests if the given class with the given access flags matches this
150     * pattern.
151     *
152     * @param acc the access flags.
153     * @param className name of the class in internal representation.
154     * @param arguments argument string in internal representation.
155     * @return true if the given parameters match this matcher.
156     */
157    public boolean matches (int acc, String className, String arguments)
158    {
159        return ((acc & mModifiers) == mModifiers
160            && className.matches(mClassRegex)
161            && arguments.matches(mMethodRegex));
162    }
163
164    /**
165     * @param substring
166     * @return
167     */
168    private String convertToArgumets (String substring)
169    {
170        final String[] args = substring.split(",");
171        final StringBuffer pattern = new StringBuffer();
172        final Iterator i = Arrays.asList(args).iterator();
173        while (i.hasNext())
174        {
175            final String arg = (String) i.next();
176            if (!org.jcoderz.commons.util.StringUtil.isEmptyOrNull(arg))
177            {
178                final Type argType = tryBasicType(arg);
179                if (argType == null)
180                {
181                    if ("*".equals(arg))
182                    {
183                        pattern.append("\\[*([VZCBSIFJD]|L[^;]+;)");
184                    }
185                    else if ("..".equals(arg))
186                    {
187                        pattern.append(".*");
188                    }
189                    else
190                    {
191                        pattern.append(convertToArgumentPattern(arg));
192                    }
193                }
194                else
195                {
196                    pattern.append(argType.getDescriptor());
197                }
198            }
199        }
200        return pattern.toString();
201    }
202
203    /**
204     * @param method
205     * @return
206     */
207    private String convertToMethod (String method)
208    {
209        String result;
210        // NEW!!!
211        if ("new".equals(method))
212        {
213            if ((mModifiers & Opcodes.ACC_STATIC) != 0)
214            {
215                result = "<init>";
216            }
217            else
218            {
219                result = "<clinit>";
220            }
221        }
222        else
223        {
224            result = method.replaceAll("\\*", ".*");
225        }
226        return result;
227    }
228
229    /**
230     * @param pos
231     * @return
232     */
233    private int getReturnType (int pos)
234    {
235        final int result = (mPattern + " ").indexOf(' ', pos) + 1;
236        final String typeString = mPattern.substring(pos, result - 1);
237        final Type returnType = tryBasicType(typeString);
238        if (returnType == null)
239        {
240            mReturnPattern = convertToArgumentPattern(typeString);
241        }
242        else
243        {
244            mReturnPattern = returnType.getDescriptor();
245        }
246        return result;
247    }
248
249    /**
250     * @param typeString
251     * @return
252     */
253    private String convertToPattern (String typeString)
254    {
255        String resultPattern = typeString;
256        if (org.jcoderz.commons.util.StringUtil.isNullOrEmpty(typeString))
257        {
258            resultPattern = "";
259        }
260        else if ("*".equals(typeString))
261        {
262            resultPattern = "[^/]*"; // any
263        }
264        else
265        {
266            resultPattern = resultPattern.replaceAll("\\.", "/");
267            resultPattern = resultPattern.replaceAll("\\*", "[^\\/]*");
268            resultPattern = resultPattern.replaceAll("//", ".*");
269            // NO 'java.lang / java.util Magic if package is given or
270            // wildcards are used.
271            if (resultPattern.indexOf('/') < 0
272                && resultPattern.indexOf('*') < 0)
273            {
274                final StringBuffer pattern = new StringBuffer("(java/util/");
275                pattern.append(resultPattern);
276                pattern.append('|');
277                pattern.append("java/lang/");
278                pattern.append(resultPattern);
279                pattern.append('|');
280                pattern.append(resultPattern);
281                pattern.append(')');
282                resultPattern = pattern.toString();
283            }
284        }
285        return resultPattern;
286    }
287
288    /**
289     * @param typeString
290     * @return
291     */
292    private String convertToArgumentPattern (String typeString)
293    {
294        String resultPattern = typeString;
295        if (org.jcoderz.commons.util.StringUtil.isNullOrEmpty(typeString))
296        {
297            resultPattern = "";
298        }
299        else if ("*".equals(typeString))
300        {
301            resultPattern = "\\[*([VZCBSIFJD]|L[^;]+;)";
302        }
303        else
304        {
305            String arrayPrefix = "";
306            // take care for arrays!!!
307            while (resultPattern.endsWith("[]"))
308            {
309                arrayPrefix += "\\[";
310                resultPattern
311                    = resultPattern.substring(0, resultPattern.length()
312                        - "[]".length());
313            }
314            resultPattern = resultPattern.replaceAll("\\.", "/");
315            resultPattern = resultPattern.replaceAll("\\*", "[^\\/]*");
316            resultPattern = resultPattern.replaceAll("//", ".*");
317            // NO 'java.lang / java.util Magic if package is given or
318            // wildcards are used.
319            if (resultPattern.indexOf('/') >= 0
320                || resultPattern.indexOf('*') >= 0)
321            {
322                final StringBuffer pattern = new StringBuffer(arrayPrefix);
323                pattern.append('L');
324                pattern.append(resultPattern);
325                pattern.append(';');
326                resultPattern = pattern.toString();
327            }
328            else
329            {
330                final StringBuffer pattern = new StringBuffer("(");
331                pattern.append(arrayPrefix);
332                pattern.append("Ljava/util/");
333                pattern.append(resultPattern);
334                pattern.append(';');
335                pattern.append('|');
336                pattern.append(arrayPrefix);
337                pattern.append("Ljava/lang/");
338                pattern.append(resultPattern);
339                pattern.append(';');
340                pattern.append('|');
341                pattern.append(arrayPrefix);
342                pattern.append('L');
343                pattern.append(resultPattern);
344                pattern.append(";)");
345                resultPattern = pattern.toString();
346            }
347        }
348        return resultPattern;
349    }
350
351    /**
352     * @param typeString
353     * @return
354     */
355    private Type tryBasicType (String typeString)
356    {
357        final StringBuffer type = new StringBuffer();
358        String in = typeString;
359        while (in.endsWith("[]"))
360        {
361            type.append('[');
362            in = in.substring(0, in.length() - "[]".length());
363        }
364        if ("int".equals(in))
365        {
366            type.append('I');
367        }
368        else if ("void".equals(in))
369        {
370            type.append('V');
371        }
372        else if ("boolean".equals(in))
373        {
374            type.append('Z');
375        }
376        else if ("char".equals(in))
377        {
378            type.append('C');
379        }
380        else if ("short".equals(in))
381        {
382            type.append('S');
383        }
384        else if ("float".equals(in))
385        {
386            type.append('F');
387        }
388        else if ("long".equals(in))
389        {
390            type.append('J');
391        }
392        else if ("double".equals(in))
393        {
394            type.append('D');
395        }
396        else
397        {
398            type.setLength(0);
399        }
400        Type resultType = null;
401        if (type.length() != 0)
402        {
403            resultType = Type.getType(type.toString());
404            if (resultType.getSort() == Type.OBJECT)
405            {
406                resultType = null;
407            }
408        }
409        return resultType;
410    }
411
412    /**
413     * @param pos
414     * @return
415     */
416    private int getModifiers (int pos)
417    {
418        int result = pos;
419        int oldResult;
420        do
421        {
422            oldResult = result;
423            result = checkModifier("public ", Opcodes.ACC_PUBLIC, result);
424            result = checkModifier("private ", Opcodes.ACC_PRIVATE, result);
425            result = checkModifier("protected ", Opcodes.ACC_PROTECTED, result);
426            result = checkModifier("static ", Opcodes.ACC_STATIC, result);
427            result = checkModifier("final ", Opcodes.ACC_FINAL, result);
428            result
429                = checkModifier(
430                    "synchronized ", Opcodes.ACC_SYNCHRONIZED, result);
431            result
432                = checkModifier(
433                    "deprecated ", Opcodes.ACC_DEPRECATED, result);
434        }
435        while (result != oldResult);
436        return result;
437    }
438
439    /**
440     * @param modifier
441     * @param acc
442     * @param pos
443     * @return
444     */
445    private int checkModifier (String modifier, int acc, int pos)
446    {
447        int result = pos;
448        if (mPattern.startsWith(modifier, pos))
449        {
450            mModifiers |= acc;
451            result += modifier.length();
452        }
453        return result;
454    }
455
456    public int getModifiers ()
457    {
458        return mModifiers;
459    }
460
461    public String getMethodPattern ()
462    {
463        return mMethodRegex;
464    }
465
466    public String getClassRegex ()
467    {
468        return mClassRegex;
469    }
470
471    // HOWTO Handle inheritance?
472    /**
473     * @param internalClassname
474     * @return
475     */
476    public boolean matchClass (String internalClassname)
477    {
478        if (mClassMatcher == null)
479        {
480            mClassMatcher = Pattern.compile(mClassRegex);
481        }
482        return mClassMatcher.matcher(internalClassname).matches();
483    }
484
485    /**
486     * @param methodDesc
487     * @return
488     */
489    public boolean matchMethod (String methodDesc)
490    {
491        if (mMethodMatcher == null)
492        {
493            mMethodMatcher = Pattern.compile(mMethodRegex);
494        }
495        return mMethodMatcher.matcher(methodDesc).matches();
496    }
497
498    /** {@inheritDoc} */
499    public String toString ()
500    {
501        return mPattern + " = '" + AsmUtil.toString(mModifiers) + " "
502            + mClassRegex + "#" + mMethodRegex + "'";
503    }
504
505    /**
506     * @param access
507     * @return
508     */
509    public boolean matchAccess (int access)
510    {
511        return (access & mModifiers) == mModifiers;
512    }
513
514    String getMethodRegex ()
515    {
516        return mMethodRegex;
517    }
518
519    public String getTracerClass ()
520    {
521        return mTracerClass;
522    }
523}
Note: See TracBrowser for help on using the browser.