Package net.sourceforge.javautil.common.classloader

Source Code of net.sourceforge.javautil.common.classloader.ClassLoaderScanner$DirectoryIterator

package net.sourceforge.javautil.common.classloader;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import net.sourceforge.javautil.common.ClassNameUtil;
import net.sourceforge.javautil.common.ReflectionUtil;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.IVirtualArtifact;
import net.sourceforge.javautil.common.io.IVirtualDirectory;
import net.sourceforge.javautil.common.io.VirtualDirectoryVisitorContext;
import net.sourceforge.javautil.common.io.IVirtualFile;
import net.sourceforge.javautil.common.io.IVirtualPath;
import net.sourceforge.javautil.common.io.VirtualArtifactURLStreamHandler.VirtualArtifactURLConnection;
import net.sourceforge.javautil.common.io.impl.ArtifactCollector;
import net.sourceforge.javautil.common.io.impl.SimplePath;
import net.sourceforge.javautil.common.io.impl.SystemDirectory;
import net.sourceforge.javautil.common.io.impl.SystemFile;
import net.sourceforge.javautil.common.io.impl.ZippedDirectory;
import net.sourceforge.javautil.common.proxy.CollectionSourceProxy;
import net.sourceforge.javautil.common.proxy.CollectionTargetProxy;
import net.sourceforge.javautil.common.visitor.IVisitable;
import net.sourceforge.javautil.common.visitor.IVisitorContext;

/**
* This will allow for standardized and customizable scanning of the class loader
* and related resources.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: ClassLoaderScanner.java 2554 2010-11-12 03:37:00Z ponderator $
*/
public class ClassLoaderScanner implements IVisitable<ClassLoaderVisitor> {
 
  /**
   * @param <T> The type of object generated by the factory
   * @param resourceName The resource for java archive detection
   * @param patterns The patterns (patterns can start with a - to indicate exclusion)
   * @return A list, possibly empty, of detected resources and generated objects
   */
  public static <T> List<T> detect (String resourceName, IResourceObjectFactory<T> factory, String... patterns) {
    ClassLoaderScanner scanner = new ClassLoaderScanner();
    scanner.setDirectoryResourceName(resourceName);
    for (String pattern : patterns) {
      if (pattern.startsWith("-")) {
        scanner.addExclusion(Pattern.compile(pattern.substring(1)));
      } else {
        if (pattern.startsWith("+")) pattern = pattern.substring(1);
        scanner.addInclusion(Pattern.compile(pattern));
      }
    }
   
    List<T> generated = new ArrayList<T>();
    for (ClassLoaderResource rsc : scanner.getResources()) {
      generated.add( factory.generate(rsc) );
    }
    return generated;
  }
 
  /**
   * @param <T> The type the resources must extend
   * @param resourceName The resource for java archive detection
   * @param iface The type's class
   * @param patterns The patterns (patterns can start with a - to indicate exclusion)
   * @return A proxy that will target instances of all types found, or null if none could be located
   */
  public static <T> List<T> detect (String resourceName, Class<T> iface, String... patterns) {
    ClassLoaderScanner scanner = new ClassLoaderScanner();
    scanner.setDirectoryResourceName(resourceName);
    for (String pattern : patterns) {
      if (pattern.startsWith("-")) {
        scanner.addExclusion(Pattern.compile(pattern.substring(1)));
      } else {
        if (pattern.startsWith("+")) pattern = pattern.substring(1);
        scanner.addInclusion(Pattern.compile(pattern));
      }
    }

    return scanner.getClasses(iface).getInstances(iface);
  }
 
  /**
   * This will load the class specified with the current thread-context class loader.
   *
   * @see #getResource(Class)
   */
  public static ClassLoaderResource getClassResource (String className) { return getClassResource(ReflectionUtil.getClass(className)); }
 
  /**
   * @param clazz The clazz
   * @return A resource wrapper for accessing the origin of the class
   */
  public static ClassLoaderResource getClassResource (Class clazz) {
    URL url = clazz.getResource("/" + ClassNameUtil.toRelativeClassPath(clazz.getName()));
    IVirtualDirectory archive = createFor(url, ClassNameUtil.toRelativeClassPath(clazz.getName()));
    return new ClassLoaderResource(clazz.getClassLoader(), new SimplePath(ClassNameUtil.toRelativeClassPath(clazz.getName())), archive);
  }

  /**
   * @param name The resource name
   * @return A resource wrapper for accessing the origin of the resource, or null if no resource could be found
   */
  public static ClassLoaderResource getResource (String name) {
    URL url = Thread.currentThread().getContextClassLoader().getResource(name);
    if (url == null) return null;
    IVirtualDirectory archive = createFor(url, name);
    return new ClassLoaderResource(Thread.currentThread().getContextClassLoader(), new SimplePath(name), archive);
  }
 
  /**
   * @param url The URL for which to create a parent directory
   * @param resourceName The resource name that was found in relation to the URL
   * @return The virtual directory implementation
   */
  public static IVirtualDirectory createFor (URL url, String resourceName) {
    try {
      String urlPath = url.getFile();
      urlPath = URLDecoder.decode(urlPath, "UTF-8");
     
      if (resourceName != null)
        urlPath = urlPath.substring(0, urlPath.length() - resourceName.length());
     
      if ( urlPath.startsWith("vas:") || urlPath.startsWith("virtual:") ) {
        try {
          URLConnection connection = new URL(urlPath).openConnection();
          if (connection instanceof VirtualArtifactURLConnection) {
            IVirtualArtifact artifact = ((VirtualArtifactURLConnection)connection).getArtifact();
            if (artifact instanceof IVirtualDirectory) return (IVirtualDirectory) artifact;
          }
        } catch (IOException e) {
          throw ThrowableManagerRegistry.caught(e);
        }
      }
     
      if ( urlPath.startsWith("file:") ) urlPath = urlPath.substring(5);
      if ( urlPath.indexOf('!')>0 ) urlPath = urlPath.substring(0, urlPath.indexOf('!'));
     
      File file = new File(urlPath);
      return file.isDirectory() ? new SystemDirectory(file) : new ZippedDirectory(new SystemFile(file));
    } catch (UnsupportedEncodingException e) {
      throw ThrowableManagerRegistry.caught(e);
    }
  }
 
  /**
   * @param <T> The type of proxy
   * @param resourceName The resource for java archive detection
   * @param iface The type's class
   * @param patterns The patterns (patterns can start with a - to indicate exclusion)
   * @return A proxy that will target instances of all types found, or null if none could be located
   */
  public static <T> T createAutoDetectedCompositeProxy (String resourceName, Class<T> iface, String... patterns) {
    List<T> instances = detect(resourceName, iface, patterns);
   
    if (Comparable.class.isAssignableFrom(iface)) Collections.sort((List<? extends Comparable>)instances);
   
    return instances.size() == 0 ? null : CollectionSourceProxy.create(iface, instances);
  }
 
  protected final ClassLoader loader;
 
  protected String directoryResourceName;
  protected List<Pattern> inclusion = new ArrayList<Pattern>();
  protected List<Pattern> exclusion = new ArrayList<Pattern>();
  protected List<Pattern> inclusionPaths = new ArrayList<Pattern>();
  protected List<Pattern> exclusionPaths = new ArrayList<Pattern>();
 
  protected Boolean collectionDefault;
 
  public ClassLoaderScanner() { this(Thread.currentThread().getContextClassLoader()); }
  public ClassLoaderScanner(ClassLoader loader) {
    this.loader = loader;
  }
 
  public <I extends ClassLoaderVisitor> I accept(I visitor) {
    new ClassLoaderVisitorContext(this).visit(visitor, loader); return visitor;
  }

  /**
   * This can be used to override the default logic for artifact collection which
   * assumes that if there is at least one inclusion pattern, then artifacts are
   * by default excluded, whereas if there are only exclusion patterns then artifacts
   * are collected by default.
   *
   * @return True if artifacts are to be collected by default, false if not, or null if default logic is to be followed
   */
  public Boolean getCollectionDefault() { return collectionDefault; }
  public void setCollectionDefault(Boolean collectionDefault) { this.collectionDefault = collectionDefault; }

  /**
   * @return The resource name used to filter which class archives are searched, otherwise null if all should be searched
   */
  public String getDirectoryResourceName() { return directoryResourceName; }
  public ClassLoaderScanner setDirectoryResourceName(String directoryResourceName) { this.directoryResourceName = directoryResourceName; return this; }

  /**
   * @param inclusion The pattern specifying resources to include
   * @return This for chaining
   */
  public ClassLoaderScanner addInclusion (Pattern inclusion) { this.inclusion.add(inclusion); return this; }
 
  /**
   * @param exclusion The pattern specifying resources to exclude
   * @return This for chaining
   */
  public ClassLoaderScanner addExclusion (Pattern exclusion) { this.exclusion.add(exclusion); return this; }
 
  /**
   * @param inclusion The pattern specifying resources to include
   * @return This for chaining
   */
  public ClassLoaderScanner addPathInclusion (Pattern inclusion) { this.inclusionPaths.add(inclusion); return this; }
 
  /**
   * @param exclusion The pattern specifying resources to exclude
   * @return This for chaining
   */
  public ClassLoaderScanner addPathExclusion (Pattern exclusion) { this.exclusionPaths.add(exclusion); return this; }

  /**
   * @return The first resource found according to the current criteria, or null if none found
   */
  public ClassLoaderResource getFirstResource () {
    return this.accept( new ClassLoaderVisitor<ClassLoader>(true) ).getFirstResource();
  }
 
  /**
   * @return The resources found according to the current criteria, maybe an empty list
   */
  public List<ClassLoaderResource> getResources () {
    return this.accept( new ClassLoaderVisitor<ClassLoader>(false) ).getResources();
  }
 
  /**
   * @param archive The archive to search
   * @return The resources that were detected in the specified archive
   */
  public List<ClassLoaderResource> getResources (IVirtualDirectory archive) {
    ClassLoaderVisitor visitor = new ClassLoaderVisitor(false);
    this.visitResources(archive, visitor);
    return visitor.getResources();
  }
 
  /**
   * @param archive The archive to search
   * @param visitor The visitor that will visit each resource
   */
  public void visitResources (IVirtualDirectory archive, ClassLoaderVisitor<?> visitor) {
    this.visitResources(archive, new ClassLoaderVisitorContext(this), visitor);
  }
 
  /**
   * @param archive The archive for which resources are desired
   * @param ctx The context in which to visit the resources
   * @param visitor The visitor that will visit each resource
   *
   * @see #getArchives()
   */
  public void visitResources (final IVirtualDirectory archive, final ClassLoaderVisitorContext ctx, final ClassLoaderVisitor<?> visitor) {
    archive.accept( this.createCollector(ctx, visitor, archive) );
  }
 
  /**
   * This will call {@link #getResources()} from which it will extract the requested classes.
   *
   * @see #getClasses(List, Class...)
   */
  public ClassLoaderClassGroup getClasses (Class... types) { return this.getClasses(this.getResources(), types); }
 
  /**
   * @param types The types of classes that need to be extracted. If a requested type is not in the
   * returned hash, it is because no classes of that type were found. The types should be declared in
   * less specific order.
   *
   * @return The map of super class <-> found concrete/extended classes
   */
  public ClassLoaderClassGroup getClasses (List<ClassLoaderResource> rscs, Class... types) {
    ClassLoaderClassGroup group = new ClassLoaderClassGroup();

    for (int r=0; r<rscs.size(); r++) {
      ClassLoaderResource rsc = rscs.get(r);
      if (rsc.isClassFile()) {
        try {
          Class clazz = rsc.loadClass();
          for (Class type : types) if (type.isAssignableFrom(clazz)) {
            if (!group.classes.containsKey(type)) { group.classes.put(type, new ArrayList<Class<?>>()); }
            group.classes.get(type).add(clazz);
            break;
          }
        } catch (ClassNotFoundException e) {
          // Ignore
        }
      }
    }
   
    return group;
  }
 
  /**
   * Facility method for extracting a list of a single type.
   *
   * @see #getClasses(List, Class...)
   */
  public <T> List<Class<T>> getClassList (List<ClassLoaderResource> rscs, Class<T> type) {
    return getClasses(rscs, type).getClasses(type);
  }
 
  /**
   * This will be called by the {@link ClassLoaderVisitorContext}.
   *
   * @return An iterator for providing access to {@link IVirtualDirectory} implementations for accessing the internal resources
   */
  public Iterator<? extends IVirtualDirectory> getArchives (ClassLoader cl) {
    try {
      String name = this.directoryResourceName == null ? "/" : this.directoryResourceName;
      return new DirectoryIterator( cl.getResources( name ), name );
    } catch (IOException e) {
      throw ThrowableManagerRegistry.caught(e);
    }
  }
 
  /**
   * @return The archives that would be searched
   *
   * @see #getDirectoryResourceName()
   */
  public Iterator<? extends IVirtualDirectory> getArchives () {
    return this.getArchives(loader);
  }
 
  /**
   * This will be called by the {@link ClassLoaderVisitorContext}.
   *
   * @param directory The directory for which to create a collector
   * @return The collector to use for visiting the directory
   *
   * @see #getCollectionDefault()
   */
  public ArtifactCollector<IVirtualFile> createCollector (final ClassLoaderVisitorContext clvctx, final ClassLoaderVisitor<?> visitor, IVirtualDirectory directory) {
    ArtifactCollector<IVirtualFile> collector = new ArtifactCollector<IVirtualFile>() {

      @Override public CollectResult collect(VirtualDirectoryVisitorContext ctx) {
        CollectResult result = super.collect(ctx);
        if (result == CollectResult.INCLUDED || isCollectionDefault()) {
          clvctx.setVisited(new ClassLoaderResource(loader, ctx.getVisitable().getRelativePath(ctx.getVisited()), ctx.getVisitable()));
          visitor.visit(clvctx);
          if (clvctx.isSkipped()) ctx.skip();
          else if (clvctx.isAborted()) ctx.abort();
        }
        return result;
      }
     
    }.setIncludeDirectories(false);
    for (Pattern inclusion : this.inclusion) collector.addInclusionPatternFilter(inclusion);
    for (Pattern exclusion : this.exclusion) collector.addExclusionPatternFilter(exclusion);
    for (Pattern inclusion : this.inclusionPaths) collector.addInclusionPathPatternFilter(inclusion);
    for (Pattern exclusion : this.exclusionPaths) collector.addExclusionPathPatternFilter(exclusion);
   
    collector.setCollectionDefault( this.collectionDefault == null ?
      this.inclusion.size() == 0 && this.inclusionPaths.size() == 0 : this.collectionDefault );
   
    return collector;
  }
 
  /**
   * This will iterator over URL's for which {@link IVirtualDirectory} implementations
   * can be generated, but only creating them when needed.
   *
   * @author elponderador
   * @author $Author: ponderator $
   * @version $Id: ClassLoaderScanner.java 2554 2010-11-12 03:37:00Z ponderator $
   */
  private class DirectoryIterator implements Iterator<IVirtualDirectory> {
   
    protected final Enumeration<URL> urls;
    protected final String resourceName;

    public DirectoryIterator(Enumeration<URL> urls, String resourceName) {
      this.urls = urls;
      this.resourceName = resourceName;
    }

    public boolean hasNext() { return urls.hasMoreElements(); }

    public IVirtualDirectory next() { return createFor(urls.nextElement(), this.resourceName); }

    public void remove() { throw new UnsupportedOperationException(); }
   
  }
 
}
TOP

Related Classes of net.sourceforge.javautil.common.classloader.ClassLoaderScanner$DirectoryIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.