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 }