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