• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.util;
18 
19 import com.android.ddmlib.Log;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.Enumeration;
24 import java.util.LinkedHashSet;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.jar.JarFile;
29 import java.util.regex.Pattern;
30 import java.util.zip.ZipEntry;
31 
32 /**
33  * Finds entries on classpath.
34  *
35  * <p>Adapted from vogar.target.ClassPathScanner</p>
36  */
37 public class ClassPathScanner {
38 
39     private static final String LOG_TAG = "ClassPathScanner";
40     private String[] mClassPath;
41 
42     /**
43      * A filter for classpath entry paths
44      * <p/>
45      * Patterned after {@link java.io.FileFilter}
46      */
47     public static interface IClassPathFilter {
48         /**
49          * Tests whether or not the specified abstract pathname should be included in a class path
50          * entry list.
51          *
52          * @param pathName the relative path of the class path entry
53          */
accept(String pathName)54         boolean accept(String pathName);
55 
56         /**
57          * An optional converter for a class path entry path names.
58          *
59          * @param pathName the relative path of the class path entry, in format "foo/path/file.ext".
60          * @return the pathName converted into context specific format
61          */
62 
transform(String pathName)63         String transform(String pathName);
64     }
65 
66     /**
67      * A {@link IClassPathFilter} that filters and transforms java class names.
68      */
69     public static class ClassNameFilter implements IClassPathFilter {
70         private static final String DOT_CLASS = ".class";
71 
72         /**
73          * {@inheritDoc}
74          */
75         @Override
accept(String pathName)76         public boolean accept(String pathName) {
77             return pathName.endsWith(DOT_CLASS);
78         }
79 
80         /**
81          * {@inheritDoc}
82          */
83         @Override
transform(String pathName)84         public String transform(String pathName) {
85             String className = pathName.substring(0, pathName.length() - DOT_CLASS.length());
86             className = className.replace('/', '.');
87             return className;
88         }
89 
90     }
91 
92     /**
93      * A {@link ClassNameFilter} that rejects inner classes
94      */
95     public static class ExternalClassNameFilter extends  ClassNameFilter {
96         /**
97          * {@inheritDoc}
98          */
99         @Override
accept(String pathName)100         public boolean accept(String pathName) {
101             return super.accept(pathName) && !pathName.contains("$");
102         }
103     }
104 
ClassPathScanner()105     public ClassPathScanner() {
106         mClassPath = getClassPath();
107     }
108 
109     /**
110      * Gets the names of all entries contained in given jar file, that match given filter
111      * @throws IOException
112      */
getEntriesFromJar(File plainFile, IClassPathFilter filter)113     public Set<String> getEntriesFromJar(File plainFile, IClassPathFilter filter)
114             throws IOException {
115         Set<String> entryNames = new LinkedHashSet<String>();
116         JarFile jarFile = new JarFile(plainFile);
117         for (Enumeration<? extends ZipEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
118             String entryName = e.nextElement().getName();
119             if (filter.accept(entryName)) {
120                 entryNames.add(filter.transform(entryName));
121             }
122             entryName = null;
123         }
124         jarFile.close();
125         return entryNames;
126     }
127 
128     /**
129      * Gets the names of all entries contained in given class path directory, that match given
130      * filter
131      * @throws IOException
132      */
getEntriesFromDir(File classPathDir, IClassPathFilter filter)133     public Set<String> getEntriesFromDir(File classPathDir, IClassPathFilter filter)
134             throws IOException {
135         Set<String> entryNames = new LinkedHashSet<String>();
136         getEntriesFromDir(classPathDir, entryNames, new LinkedList<String>(), filter);
137         return entryNames;
138     }
139 
140     /**
141      * Recursively adds the names of all entries contained in given class path directory,
142      * that match given filter.
143      *
144      * @param dir the directory to scan
145      * @param entries the {@link Set} of class path entry names to add to
146      * @param rootPath the relative path of <var>dir</var> from class path element root
147      * @param filter the {@link IClassPathFilter} to use
148      * @throws IOException
149      */
getEntriesFromDir(File dir, Set<String> entries, List<String> rootPath, IClassPathFilter filter)150     private void getEntriesFromDir(File dir, Set<String> entries, List<String> rootPath,
151             IClassPathFilter filter) throws IOException {
152         File[] childFiles = dir.listFiles();
153         if (childFiles == null) {
154             Log.w(LOG_TAG, String.format("Directory %s in classPath is not readable, skipping",
155                     dir.getAbsolutePath()));
156             return;
157         }
158         for (File childFile : childFiles) {
159             if (childFile.isDirectory()) {
160                 rootPath.add(childFile.getName() + "/");
161                 getEntriesFromDir(childFile, entries, rootPath, filter);
162                 // pop off the path element for this directory
163                 rootPath.remove(rootPath.size() - 1);
164             } else if (childFile.isFile()) {
165                 // construct relative path of this file
166                 String classPathEntryName = constructPath(rootPath, childFile.getName());
167                 if (filter.accept(classPathEntryName)) {
168                     entries.add(filter.transform(classPathEntryName));
169                 }
170             } else {
171                 Log.d(LOG_TAG, String.format("file %s in classPath is not recognized, skipping",
172                         dir.getAbsolutePath()));
173             }
174         }
175     }
176 
177     /**
178      * Construct a relative class path path for the given class path file
179      *
180      * @param rootPath the root path in {@link List} form
181      * @param fileName the file name
182      * @return the relative classpath path
183      */
constructPath(List<String> rootPath, String fileName)184     private String constructPath(List<String> rootPath, String fileName) {
185         StringBuilder pathBuilder = new StringBuilder();
186         for (String element : rootPath) {
187             pathBuilder.append(element);
188         }
189         pathBuilder.append(fileName);
190         return pathBuilder.toString();
191     }
192 
193     /**
194      * Retrieves set of classpath entries that match given {@link IClassPathFilter}
195      */
getClassPathEntries(IClassPathFilter filter)196     public Set<String> getClassPathEntries(IClassPathFilter filter) {
197         Set<String> entryNames = new LinkedHashSet<String>();
198         for (String classPathElement : mClassPath) {
199             File classPathFile = new File(classPathElement);
200             try {
201                 if (classPathFile.isFile() && classPathElement.endsWith(".jar")) {
202                     entryNames.addAll(getEntriesFromJar(classPathFile, filter));
203                 } else if (classPathFile.isDirectory()) {
204                     entryNames.addAll(getEntriesFromDir(classPathFile, filter));
205                 } else {
206                     Log.w(LOG_TAG, String.format(
207                             "class path entry %s does not exist or is not recognized, skipping",
208                             classPathElement));
209                 }
210             } catch (IOException e) {
211                 Log.w(LOG_TAG, String.format("Failed to read class path entry %s. Reason: %s",
212                         classPathElement, e.toString()));
213             }
214         }
215         return entryNames;
216     }
217 
218     /**
219      * Gets the class path from the System Property "java.class.path" and splits
220      * it up into the individual elements.
221      */
getClassPath()222     public static String[] getClassPath() {
223         String classPath = System.getProperty("java.class.path");
224         return classPath.split(Pattern.quote(File.pathSeparator));
225     }
226 }
227