View Javadoc

1   //////////////////////////////////////////////////////////////////////////////
2   // Clirr: compares two versions of a java library for binary compatibility
3   // Copyright (C) 2003 - 2005  Lars Kühne
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  //////////////////////////////////////////////////////////////////////////////
19  package net.sf.clirr.core;
20  
21  import org.apache.bcel.classfile.JavaClass;
22  
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  
26  /***
27   * Given a JavaClass object, determines whether or not it is "selected",
28   * based on its class or package. This is used to select subsets of the
29   * classes available in a classpath for comparison or testing purposes.
30   *
31   * @author Simon Kitching
32   */
33  public final class ClassSelector implements ClassFilter
34  {
35      /*** Class for implementing an enumeration. */
36      public static final class Mode
37      {
38          private Mode()
39          {
40          }
41      }
42  
43      /*** positive selection. */
44      public static final Mode MODE_IF = new Mode();
45      /*** negative selection. */
46      public static final Mode MODE_UNLESS = new Mode();
47  
48      private Mode mode;
49  
50      privateong> ArrayList packages = new ArrayList();
51      privateong> ArrayList packageTrees = new ArrayList();
52      private ArrayList classes = new ArrayList();
53  
54      /***
55       * Create a selector.
56       * <p>
57       * When mode is MODE_IF then a class is "selected" if-and-only-if
58       * the class matches one of the criteria defined via the addXXX methods.
59       * In other words, the criteria specify which classes are included
60       * (selected) in the resulting class set.
61       * <p>
62       * When mode is MODE_UNLESS, then a class is "selected" unless the class
63       * matches one of the criteria defined via the addXXX methods. In other
64       * words, the criteria specify which classes are excluded from the
65       * resulting class set.
66       */
67      public ClassSelector(Mode mode)
68      {
69          this.mode = mode;
70      }
71  
72      /***
73       * Matches any class which is in the named package.
74       */
75      publicong> void addPackage(String packageName)
76      {
77          packages.add(packageName);
78      }
79  
80      /***
81       * Matches any class which is in the named package or any subpackage of it.
82       */
83      publicong> void addPackageTree(String packageName)
84      {
85          packages.add(packageName);
86      }
87  
88      /***
89       * Matches the class with exactly this name, plus any of its inner classes.
90       */
91      public void addClass(String classname)
92      {
93          classes.add(classname);
94      }
95  
96      /***
97       * Return true if this class is one selected by the criteria stored
98       * in this object.
99       */
100     public boolean isSelected(JavaClass clazz)
101     {
102         if (isAnonymousInnerClass(clazz))
103         {
104             return false;
105         }
106 
107         boolean matches = matchesCriteria(clazz);
108         if (mode == MODE_IF)
109         {
110             return matches;
111         }
112         else // mode == MODE_UNLESS
113         {
114             return !matches;
115         }
116     }
117 
118     /***
119      * Return true if this class is an anonymous inner class.
120      * Not even developers working on a package would be interested
121      * in API changes in these classes...
122      */
123     private boolean isAnonymousInnerClass(JavaClass clazz)
124     {
125         String name = clazz.getClassName();
126         int dollarPos = name.indexOf('$');
127         if (dollarPos == -1)
128         {
129             return false;
130         }
131 
132         for (int i = dollarPos + 1; i < name.length(); ++i)
133         {
134             if (!Character.isDigit(name.charAt(i)))
135             {
136                 return false;
137             }
138         }
139 
140         // ok, we have a class name which contains a dollar sign, and
141         // every subsequent character is a digit.
142         return true;
143     }
144 
145     /***
146      * Return true if this class matches one of the criteria stored
147      * in this object.
148      */
149     private boolean matchesCriteria(JavaClass clazz)
150     {
151         String packageName = clazz.getPackageName();
152         if</strong> (packages.contains(packageName))
153         {
154             return true;
155         }
156 
157         for (Iterator i = packageTrees.iterator(); i.hasNext();)
158         {
159             String entry = (String) i.next();
160             >if (packageName.startsWith(entry))
161             {
162                 rong>if (packageName.length() == entry.length())
163                 {
164                     // they are exactly equal
165                     return true;
166                 }
167 
168                 rong>if (packageName.charAt(entry.length()) == '.')
169                 {
170                     return true;
171                 }
172 
173                 // else packagename is like "com.acmegadgets" and entryname
174                 // is like "com.acme", which is not a match, so keep looking.
175             }
176         }
177 
178         String className = clazz.getClassName();
179         for (Iterator i = classes.iterator(); i.hasNext();)
180         {
181             String entry = (String) i.next();
182 
183             if (className.startsWith(entry))
184             {
185                 if (className.length() == entry.length())
186                 {
187                     // they are exactly equal
188                     return true;
189                 }
190 
191                 if (className.charAt(entry.length()) == '$')
192                 {
193                     // this is an inner class of the named class
194                     return true;
195                 }
196             }
197         }
198 
199         return false;
200     }
201 }
202