View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.expression.regexp;
9   
10  import org.codehaus.aspectwerkz.expression.ExpressionException;
11  import org.codehaus.aspectwerkz.expression.SubtypePatternType;
12  import org.codehaus.aspectwerkz.util.Strings;
13  import org.codehaus.aspectwerkz.reflect.ClassInfo;
14  import org.codehaus.aspectwerkz.proxy.Proxy;
15  
16  import java.io.ObjectInputStream;
17  
18  /***
19   * Implements the regular expression pattern matcher for types.
20   *
21   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
22   */
23  public class TypePattern extends Pattern {
24  
25      /***
26       * The fully qualified type name.
27       */
28      protected transient com.karneim.util.collection.regex.Pattern m_typeNamePattern;
29  
30      /***
31       * The pattern as a string.
32       */
33      protected String m_pattern;
34  
35      /***
36       * The subtype pattern type.
37       */
38      private SubtypePatternType m_subtypePatternType;
39  
40      /***
41       * Private constructor.
42       *
43       * @param pattern            the pattern
44       * @param subtypePatternType the subtype pattern type
45       */
46      TypePattern(final String pattern, final SubtypePatternType subtypePatternType) {
47          m_pattern = pattern;
48          m_subtypePatternType = subtypePatternType;
49          escape(m_pattern);
50      }
51  
52      /***
53       * Matches a type name.
54       *
55       * @param typeName the name of the type
56       * @return true if we have a matche
57       */
58      public boolean matches(String typeName) {
59          int awProxySuffixStart = typeName.indexOf(Proxy.PROXY_SUFFIX_START);
60          if (awProxySuffixStart > 0) {
61              typeName = typeName.substring(0, awProxySuffixStart);
62          } else {
63              int cglibFastClassSuffixStarg = typeName.indexOf("$$FastClassByCGLIB$$");
64              if (cglibFastClassSuffixStarg > 0) {
65                  // always filter away cglib fast class classes
66                  return false;
67              }
68              int cglibEnhancerSuffixStart = typeName.indexOf("$$EnhancerByCGLIB$$");
69              if (cglibEnhancerSuffixStart > 0) {
70                  typeName = typeName.substring(0, cglibEnhancerSuffixStart);
71              }
72          }
73          if (typeName == null) {
74              return false;
75          }
76          if (typeName.equals("")) {
77              return false;
78          }
79          return m_typeNamePattern.contains(typeName);
80      }
81  
82      /***
83       * Matches a type.
84       *
85       * @param classInfo the info of the class
86       * @return
87       */
88      public boolean matchType(final ClassInfo classInfo) {
89          SubtypePatternType type = getSubtypePatternType();
90          if (type.equals(SubtypePatternType.MATCH_ON_ALL_METHODS)) {
91              return matchSuperClasses(classInfo);
92          } else if (type.equals(SubtypePatternType.MATCH_ON_BASE_TYPE_METHODS_ONLY)) {
93              // TODO: matching on methods ONLY in base type needs to be completed
94              // TODO: needs to work together with the method and field matching somehow
95              return matchSuperClasses(classInfo);
96          } else {
97              return matches(classInfo.getName());
98          }
99      }
100 
101     /***
102      * Tries to finds a parse at some superclass in the hierarchy. <p/>Only checks for a class parse to allow early
103      * filtering. <p/>Recursive.
104      *
105      * @param classInfo the class info
106      * @return boolean
107      */
108     public boolean matchSuperClasses(final ClassInfo classInfo) {
109         if ((classInfo == null)) {
110             return false;
111         }
112 
113         // parse the class/super class
114         if (matches(classInfo.getName())) {
115             return true;
116         } else {
117             // parse the interfaces for the class
118             if (matchInterfaces(classInfo.getInterfaces(), classInfo)) {
119                 return true;
120             }
121 
122             // no parse; getClass the next superclass
123             return matchSuperClasses(classInfo.getSuperclass());
124         }
125     }
126 
127     /***
128      * Tries to finds a parse at some interface in the hierarchy. <p/>Only checks for a class parse to allow early
129      * filtering. <p/>Recursive.
130      *
131      * @param interfaces the interfaces
132      * @param classInfo  the class info
133      * @return boolean
134      */
135     public boolean matchInterfaces(final ClassInfo[] interfaces, final ClassInfo classInfo) {
136         if ((interfaces.length == 0) || (classInfo == null)) {
137             return false;
138         }
139         for (int i = 0; i < interfaces.length; i++) {
140             ClassInfo anInterface = interfaces[i];
141             if (matches(anInterface.getName())) {
142                 return true;
143             } else {
144                 if (matchInterfaces(anInterface.getInterfaces(), classInfo)) {
145                     return true;
146                 } else {
147                     continue;
148                 }
149             }
150         }
151         return false;
152     }
153 
154     /***
155      * Returns the subtype pattern type
156      *
157      * @return boolean
158      */
159     public SubtypePatternType getSubtypePatternType() {
160         return m_subtypePatternType;
161     }
162 
163     /***
164      * Checks if the pattern matches all types.
165      *
166      * @return boolean
167      */
168     public boolean isEagerWildCard() {
169         return m_pattern.equals(EAGER_WILDCARD);
170     }
171 
172     /***
173      * Returns the pattern as a string.
174      *
175      * @return the pattern
176      */
177     public String getPattern() {
178         return m_pattern;
179     }
180 
181     /***
182      * Escapes the type pattern.
183      *
184      * @param pattern the method pattern
185      */
186     protected void escape(final String pattern) {
187         String typeName = pattern;
188         if (ABBREVIATIONS.containsKey(pattern)) {
189             typeName = (String) ABBREVIATIONS.get(pattern);
190         }
191         try {
192             if (typeName.equals(REGULAR_WILDCARD) || typeName.equals(EAGER_WILDCARD)) {
193                 typeName = "[a-zA-Z0-9_$.//[//]]+";
194             } else {
195                 // CAUTION: order matters
196                 typeName = Strings.replaceSubString(typeName, "[", "//[");
197                 typeName = Strings.replaceSubString(typeName, "]", "//]");
198                 typeName = Strings.replaceSubString(typeName, "..", "[a-zA-Z0-9_$.]+");
199                 typeName = Strings.replaceSubString(typeName, ".", "//.");
200                 typeName = Strings.replaceSubString(typeName, "*", "[a-zA-Z0-9_$//[//]]*");
201             }
202             m_typeNamePattern = new com.karneim.util.collection.regex.Pattern(typeName);
203         } catch (Throwable e) {
204             throw new ExpressionException("type pattern is not well formed: " + pattern, e);
205         }
206     }
207 
208     /***
209      * Provides custom deserialization.
210      *
211      * @param stream the object input stream containing the serialized object
212      * @throws Exception in case of failure
213      */
214     private void readObject(final ObjectInputStream stream) throws Exception {
215         ObjectInputStream.GetField fields = stream.readFields();
216         m_pattern = (String) fields.get("m_pattern", null);
217         escape(m_pattern);
218     }
219 
220     public int hashCode() {
221         int result = 17;
222         result = (37 * result) + hashCodeOrZeroIfNull(m_pattern);
223         result = (37 * result) + hashCodeOrZeroIfNull(m_typeNamePattern);
224         return result;
225     }
226 
227     protected static int hashCodeOrZeroIfNull(final Object o) {
228         if (null == o) {
229             return 19;
230         }
231         return o.hashCode();
232     }
233 
234     public boolean equals(final Object o) {
235         if (this == o) {
236             return true;
237         }
238         if (!(o instanceof TypePattern)) {
239             return false;
240         }
241         final TypePattern obj = (TypePattern) o;
242         return areEqualsOrBothNull(obj.m_pattern, this.m_pattern)
243                && areEqualsOrBothNull(obj.m_typeNamePattern, this.m_typeNamePattern);
244     }
245 
246     protected static boolean areEqualsOrBothNull(final Object o1, final Object o2) {
247         if (null == o1) {
248             return (null == o2);
249         }
250         return o1.equals(o2);
251     }
252 }