content: /**
   * 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;
    }
  }
  
  