/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.expression.regexp;
import org.codehaus.aspectwerkz.expression.ExpressionException;
import org.codehaus.aspectwerkz.expression.SubtypePatternType;
import org.codehaus.aspectwerkz.proxy.ProxySubclassingStrategy;
import org.codehaus.aspectwerkz.reflect.ClassInfo;
import org.codehaus.aspectwerkz.util.Strings;
import java.io.ObjectInputStream;
/**
* Implements the regular expression pattern matcher for types.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
*/
public class TypePattern extends Pattern {
/**
* The fully qualified type name.
*/
protected transient com.karneim.util.collection.regex.Pattern m_typeNamePattern;
/**
* The pattern as a string.
*/
protected String m_pattern;
/**
* The subtype pattern type.
*/
private SubtypePatternType m_subtypePatternType;
/**
* Private constructor.
*
* @param pattern the pattern
* @param subtypePatternType the subtype pattern type
*/
TypePattern(final String pattern, final SubtypePatternType subtypePatternType) {
m_pattern = pattern;
m_subtypePatternType = subtypePatternType;
escape(m_pattern);
}
/**
* Matches a type name.
*
* @param typeName the name of the type
* @return true if we have a matche
*/
public boolean matches(String typeName) {
// regular match
if (m_typeNamePattern.contains(typeName)) {
return true;
}
// fallback on subclassing proxy match and Cglib extension
int awProxySuffixStart = typeName.indexOf(ProxySubclassingStrategy.PROXY_SUFFIX_START);
if (awProxySuffixStart > 0) {
typeName = typeName.substring(0, awProxySuffixStart);
} else {
int cglibFastClassSuffixStarg = typeName.indexOf("$$FastClassByCGLIB$$");
if (cglibFastClassSuffixStarg > 0) {
// always filter away cglib fast class classes
return false;
}
int cglibEnhancerSuffixStart = typeName.indexOf("$$EnhancerByCGLIB$$");
if (cglibEnhancerSuffixStart > 0) {
typeName = typeName.substring(0, cglibEnhancerSuffixStart);
}
}
if (typeName == null) {
return false;
}
if (typeName.equals("")) {
return false;
}
return m_typeNamePattern.contains(typeName);
}
/**
* Matches a type.
*
* @param classInfo the info of the class
* @return
*/
public boolean matchType(final ClassInfo classInfo) {
SubtypePatternType type = getSubtypePatternType();
if (type.equals(SubtypePatternType.MATCH_ON_ALL_METHODS)) {
return matchSuperClasses(classInfo);
} else if (type.equals(SubtypePatternType.MATCH_ON_BASE_TYPE_METHODS_ONLY)) {
// TODO: matching on methods ONLY in base type needs to be completed
// TODO: needs to work together with the method and field matching somehow
return matchSuperClasses(classInfo);
} else {
return matches(classInfo.getName());
}
}
/**
* Tries to finds a parse at some superclass in the hierarchy. <p/>Only checks for a class parse to allow early
* filtering. <p/>Recursive.
*
* @param classInfo the class info
* @return boolean
*/
public boolean matchSuperClasses(final ClassInfo classInfo) {
if ((classInfo == null)) {
return false;
}
// parse the class/super class
if (matches(classInfo.getName())) {
return true;
} else {
// parse the interfaces for the class
if (matchInterfaces(classInfo.getInterfaces(), classInfo)) {
return true;
}
// no parse; getClass the next superclass
return matchSuperClasses(classInfo.getSuperclass());
}
}
/**
* Tries to finds a parse at some interface in the hierarchy. <p/>Only checks for a class parse to allow early
* filtering. <p/>Recursive.
*
* @param interfaces the interfaces
* @param classInfo the class info
* @return boolean
*/
public boolean matchInterfaces(final ClassInfo[] interfaces, final ClassInfo classInfo) {
if ((interfaces.length == 0) || (classInfo == null)) {
return false;
}
for (int i = 0; i < interfaces.length; i++) {
ClassInfo anInterface = interfaces[i];
if (matches(anInterface.getName())) {
return true;
} else {
if (matchInterfaces(anInterface.getInterfaces(), classInfo)) {
return true;
} else {
continue;
}
}
}
return false;
}
/**
* Returns the subtype pattern type
*
* @return boolean
*/
public SubtypePatternType getSubtypePatternType() {
return m_subtypePatternType;
}
/**
* Checks if the pattern matches all types.
*
* @return boolean
*/
public boolean isEagerWildCard() {
return m_pattern.equals(EAGER_WILDCARD);
}
/**
* Returns the pattern as a string.
*
* @return the pattern
*/
public String getPattern() {
return m_pattern;
}
/**
* Escapes the type pattern.
*
* @param pattern the method pattern
*/
protected void escape(final String pattern) {
String typeName = pattern;
if (ABBREVIATIONS.containsKey(pattern)) {
typeName = (String) ABBREVIATIONS.get(pattern);
}
try {
if (typeName.equals(REGULAR_WILDCARD) || typeName.equals(EAGER_WILDCARD)) {
typeName = "[a-zA-Z0-9_$.\\[\\]]+";
} else {
// CAUTION: order matters
typeName = Strings.replaceSubString(typeName, "[", "\\[");
typeName = Strings.replaceSubString(typeName, "]", "\\]");
typeName = Strings.replaceSubString(typeName, "..", "[a-zA-Z0-9_$.]+");
typeName = Strings.replaceSubString(typeName, ".", "\\.");
typeName = Strings.replaceSubString(typeName, "*", "[a-zA-Z0-9_$\\[\\]]*");
}
m_typeNamePattern = new com.karneim.util.collection.regex.Pattern(typeName);
} catch (Throwable e) {
throw new ExpressionException("type pattern is not well formed: " + pattern, e);
}
}
/**
* Provides custom deserialization.
*
* @param stream the object input stream containing the serialized object
* @throws Exception in case of failure
*/
private void readObject(final ObjectInputStream stream) throws Exception {
ObjectInputStream.GetField fields = stream.readFields();
m_pattern = (String) fields.get("m_pattern", null);
escape(m_pattern);
}
public int hashCode() {
int result = 17;
result = (37 * result) + hashCodeOrZeroIfNull(m_pattern);
result = (37 * result) + hashCodeOrZeroIfNull(m_typeNamePattern);
return result;
}
protected static int hashCodeOrZeroIfNull(final Object o) {
if (null == o) {
return 19;
}
return o.hashCode();
}
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TypePattern)) {
return false;
}
final TypePattern obj = (TypePattern) o;
return areEqualsOrBothNull(obj.m_pattern, this.m_pattern)
&& areEqualsOrBothNull(obj.m_typeNamePattern, this.m_typeNamePattern);
}
protected static boolean areEqualsOrBothNull(final Object o1, final Object o2) {
if (null == o1) {
return (null == o2);
}
return o1.equals(o2);
}
}