/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.util;
import java.io.Reader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Map;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Enumeration;
import java.util.jar.*;
/**
* Given a list of Jar files, suggest missing analysis factories.
*
* @version $Id: SuggestMissingFactories.java 701485 2008-10-03 18:43:57Z ryan $
*/
public class SuggestMissingFactories {
public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchMethodException {
final File[] files = new File[args.length];
for (int i = 0; i < args.length; i++) {
files[i] = new File(args[i]);
}
final FindClasses finder = new FindClasses(files);
final ClassLoader cl = finder.getClassLoader();
final Class TOKENSTREAM
= cl.loadClass("org.apache.lucene.analysis.TokenStream");
final Class TOKENIZER
= cl.loadClass("org.apache.lucene.analysis.Tokenizer");
final Class TOKENFILTER
= cl.loadClass("org.apache.lucene.analysis.TokenFilter");
final Class TOKENIZERFACTORY
= cl.loadClass("org.apache.solr.analysis.TokenizerFactory");
final Class TOKENFILTERFACTORY
= cl.loadClass("org.apache.solr.analysis.TokenFilterFactory");
final HashSet<Class> result
= new HashSet<Class>(finder.findExtends(TOKENIZER));
result.addAll(finder.findExtends(TOKENFILTER));
result.removeAll(finder.findMethodReturns
(finder.findExtends(TOKENIZERFACTORY),
"create",
Reader.class).values());
result.removeAll(finder.findMethodReturns
(finder.findExtends(TOKENFILTERFACTORY),
"create",
TOKENSTREAM).values());
for (final Class c : result) {
System.out.println(c.getName());
}
}
}
/**
* Takes in a clazz name and a jar and finds
* all classes in that jar that extend clazz.
*/
class FindClasses {
/**
* Simple command line test method
*/
public static void main(String[] args)
throws ClassNotFoundException, IOException, NoSuchMethodException {
FindClasses finder = new FindClasses(new File(args[1]));
ClassLoader cl = finder.getClassLoader();
Class clazz = cl.loadClass(args[0]);
if (args.length == 2) {
System.out.println("Finding all extenders of " + clazz.getName());
for (Class c : finder.findExtends(clazz)) {
System.out.println(c.getName());
}
} else {
String methName = args[2];
System.out.println("Finding all extenders of " + clazz.getName() +
" with method: " + methName);
Class[] methArgs = new Class[args.length-3];
for (int i = 3; i < args.length; i++) {
methArgs[i-3] = cl.loadClass(args[i]);
}
Map<Class,Class> map = finder.findMethodReturns
(finder.findExtends(clazz),methName, methArgs);
for (Class key : map.keySet()) {
System.out.println(key.getName() + " => " + map.get(key).getName());
}
}
}
private JarFile[] jarFiles;
private ClassLoader cl;
public FindClasses(File... jars) throws IOException {
jarFiles = new JarFile[jars.length];
URL[] urls = new URL[jars.length];
try {
for (int i =0; i < jars.length; i++) {
jarFiles[i] = new JarFile(jars[i]);
urls[i] = jars[i].toURI().toURL();
}
} catch (MalformedURLException e) {
throw new RuntimeException
("WTF, how can JarFile.toURL() be malformed?", e);
}
this.cl = new URLClassLoader(urls, this.getClass().getClassLoader());
}
/**
* returns a class loader that includes the jar used to
* construct this instance
*/
public ClassLoader getClassLoader() {
return this.cl;
}
/**
* Find useful concrete (ie: not anonymous, not abstract, not an interface)
* classes that extend clazz
*/
public Collection<Class> findExtends(Class<?> clazz)
throws ClassNotFoundException {
HashSet<Class> results = new HashSet<Class>();
for (JarFile jarFile : jarFiles) {
for (Enumeration<JarEntry> e = jarFile.entries();
e.hasMoreElements() ;) {
String n = e.nextElement().getName();
if (n.endsWith(".class")) {
String cn = n.replace("/",".").substring(0,n.length()-6);
Class<?> target;
try {
target = cl.loadClass(cn);
} catch (NoClassDefFoundError e1) {
throw new ClassNotFoundException
("Can't load: " + cn, e1);
}
if (clazz.isAssignableFrom(target)
&& !target.isAnonymousClass()) {
int mods = target.getModifiers();
if (!(Modifier.isAbstract(mods) ||
Modifier.isInterface(mods))) {
results.add(target);
}
}
}
}
}
return results;
}
/**
* Given a collection of classes, returns a Map containing the
* subset of those classes that impliment the method specified,
* where the value in the map is the return type of the method
*/
public Map<Class,Class> findMethodReturns(Collection<Class> clazzes,
String methodName,
Class... parameterTypes)
throws NoSuchMethodException{
HashMap<Class,Class> results = new HashMap<Class,Class>();
for (Class clazz : clazzes) {
try {
Method m = clazz.getMethod(methodName, parameterTypes);
results.put(clazz, m.getReturnType());
} catch (NoSuchMethodException e) {
/* :NOOP: we expect this and skip clazz */
}
}
return results;
}
}