001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.expression.regexp;
008:
009: import org.codehaus.aspectwerkz.expression.ExpressionException;
010: import org.codehaus.aspectwerkz.expression.SubtypePatternType;
011: import org.codehaus.aspectwerkz.util.Strings;
012: import org.codehaus.aspectwerkz.reflect.ClassInfo;
013: import org.codehaus.aspectwerkz.proxy.Proxy;
014:
015: import java.io.ObjectInputStream;
016:
017: /**
018: * Implements the regular expression pattern matcher for types.
019: *
020: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
021: */
022: public class TypePattern extends Pattern {
023:
024: /**
025: * The fully qualified type name.
026: */
027: protected transient com.karneim.util.collection.regex.Pattern m_typeNamePattern;
028:
029: /**
030: * The pattern as a string.
031: */
032: protected String m_pattern;
033:
034: /**
035: * The subtype pattern type.
036: */
037: private SubtypePatternType m_subtypePatternType;
038:
039: /**
040: * Private constructor.
041: *
042: * @param pattern the pattern
043: * @param subtypePatternType the subtype pattern type
044: */
045: TypePattern(final String pattern,
046: final SubtypePatternType subtypePatternType) {
047: m_pattern = pattern;
048: m_subtypePatternType = subtypePatternType;
049: escape(m_pattern);
050: }
051:
052: /**
053: * Matches a type name.
054: *
055: * @param typeName the name of the type
056: * @return true if we have a matche
057: */
058: public boolean matches(String typeName) {
059: int awProxySuffixStart = typeName
060: .indexOf(Proxy.PROXY_SUFFIX_START);
061: if (awProxySuffixStart > 0) {
062: typeName = typeName.substring(0, awProxySuffixStart);
063: } else {
064: int cglibFastClassSuffixStarg = typeName
065: .indexOf("$$FastClassByCGLIB$$");
066: if (cglibFastClassSuffixStarg > 0) {
067: // always filter away cglib fast class classes
068: return false;
069: }
070: int cglibEnhancerSuffixStart = typeName
071: .indexOf("$$EnhancerByCGLIB$$");
072: if (cglibEnhancerSuffixStart > 0) {
073: typeName = typeName.substring(0,
074: cglibEnhancerSuffixStart);
075: }
076: }
077: if (typeName == null) {
078: return false;
079: }
080: if (typeName.equals("")) {
081: return false;
082: }
083: return m_typeNamePattern.contains(typeName);
084: }
085:
086: /**
087: * Matches a type.
088: *
089: * @param classInfo the info of the class
090: * @return
091: */
092: public boolean matchType(final ClassInfo classInfo) {
093: SubtypePatternType type = getSubtypePatternType();
094: if (type.equals(SubtypePatternType.MATCH_ON_ALL_METHODS)) {
095: return matchSuperClasses(classInfo);
096: } else if (type
097: .equals(SubtypePatternType.MATCH_ON_BASE_TYPE_METHODS_ONLY)) {
098: // TODO: matching on methods ONLY in base type needs to be completed
099: // TODO: needs to work together with the method and field matching somehow
100: return matchSuperClasses(classInfo);
101: } else {
102: return matches(classInfo.getName());
103: }
104: }
105:
106: /**
107: * Tries to finds a parse at some superclass in the hierarchy. <p/>Only checks for a class parse to allow early
108: * filtering. <p/>Recursive.
109: *
110: * @param classInfo the class info
111: * @return boolean
112: */
113: public boolean matchSuperClasses(final ClassInfo classInfo) {
114: if ((classInfo == null)) {
115: return false;
116: }
117:
118: // parse the class/super class
119: if (matches(classInfo.getName())) {
120: return true;
121: } else {
122: // parse the interfaces for the class
123: if (matchInterfaces(classInfo.getInterfaces(), classInfo)) {
124: return true;
125: }
126:
127: // no parse; getClass the next superclass
128: return matchSuperClasses(classInfo.getSuperclass());
129: }
130: }
131:
132: /**
133: * Tries to finds a parse at some interface in the hierarchy. <p/>Only checks for a class parse to allow early
134: * filtering. <p/>Recursive.
135: *
136: * @param interfaces the interfaces
137: * @param classInfo the class info
138: * @return boolean
139: */
140: public boolean matchInterfaces(final ClassInfo[] interfaces,
141: final ClassInfo classInfo) {
142: if ((interfaces.length == 0) || (classInfo == null)) {
143: return false;
144: }
145: for (int i = 0; i < interfaces.length; i++) {
146: ClassInfo anInterface = interfaces[i];
147: if (matches(anInterface.getName())) {
148: return true;
149: } else {
150: if (matchInterfaces(anInterface.getInterfaces(),
151: classInfo)) {
152: return true;
153: } else {
154: continue;
155: }
156: }
157: }
158: return false;
159: }
160:
161: /**
162: * Returns the subtype pattern type
163: *
164: * @return boolean
165: */
166: public SubtypePatternType getSubtypePatternType() {
167: return m_subtypePatternType;
168: }
169:
170: /**
171: * Checks if the pattern matches all types.
172: *
173: * @return boolean
174: */
175: public boolean isEagerWildCard() {
176: return m_pattern.equals(EAGER_WILDCARD);
177: }
178:
179: /**
180: * Returns the pattern as a string.
181: *
182: * @return the pattern
183: */
184: public String getPattern() {
185: return m_pattern;
186: }
187:
188: /**
189: * Escapes the type pattern.
190: *
191: * @param pattern the method pattern
192: */
193: protected void escape(final String pattern) {
194: String typeName = pattern;
195: if (ABBREVIATIONS.containsKey(pattern)) {
196: typeName = (String) ABBREVIATIONS.get(pattern);
197: }
198: try {
199: if (typeName.equals(REGULAR_WILDCARD)
200: || typeName.equals(EAGER_WILDCARD)) {
201: typeName = "[a-zA-Z0-9_$.\\[\\]]+";
202: } else {
203: // CAUTION: order matters
204: typeName = Strings.replaceSubString(typeName, "[",
205: "\\[");
206: typeName = Strings.replaceSubString(typeName, "]",
207: "\\]");
208: typeName = Strings.replaceSubString(typeName, "..",
209: "[a-zA-Z0-9_$.]+");
210: typeName = Strings.replaceSubString(typeName, ".",
211: "\\.");
212: typeName = Strings.replaceSubString(typeName, "*",
213: "[a-zA-Z0-9_$\\[\\]]*");
214: }
215: m_typeNamePattern = new com.karneim.util.collection.regex.Pattern(
216: typeName);
217: } catch (Throwable e) {
218: throw new ExpressionException(
219: "type pattern is not well formed: " + pattern, e);
220: }
221: }
222:
223: /**
224: * Provides custom deserialization.
225: *
226: * @param stream the object input stream containing the serialized object
227: * @throws Exception in case of failure
228: */
229: private void readObject(final ObjectInputStream stream)
230: throws Exception {
231: ObjectInputStream.GetField fields = stream.readFields();
232: m_pattern = (String) fields.get("m_pattern", null);
233: escape(m_pattern);
234: }
235:
236: public int hashCode() {
237: int result = 17;
238: result = (37 * result) + hashCodeOrZeroIfNull(m_pattern);
239: result = (37 * result)
240: + hashCodeOrZeroIfNull(m_typeNamePattern);
241: return result;
242: }
243:
244: protected static int hashCodeOrZeroIfNull(final Object o) {
245: if (null == o) {
246: return 19;
247: }
248: return o.hashCode();
249: }
250:
251: public boolean equals(final Object o) {
252: if (this == o) {
253: return true;
254: }
255: if (!(o instanceof TypePattern)) {
256: return false;
257: }
258: final TypePattern obj = (TypePattern) o;
259: return areEqualsOrBothNull(obj.m_pattern, this .m_pattern)
260: && areEqualsOrBothNull(obj.m_typeNamePattern,
261: this .m_typeNamePattern);
262: }
263:
264: protected static boolean areEqualsOrBothNull(final Object o1,
265: final Object o2) {
266: if (null == o1) {
267: return (null == o2);
268: }
269: return o1.equals(o2);
270: }
271: }
|