001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.java.bytecode.loader;
021
022import java.io.Closeable;
023import java.io.File;
024import java.io.IOException;
025import java.net.URL;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.Enumeration;
029import java.util.List;
030
031import com.google.common.collect.Iterators;
032
033/**
034 * Class loader, which is able to load classes from a list of JAR files and directories.
035 */
036public class SquidClassLoader extends ClassLoader implements Closeable {
037
038  private final List<Loader> loaders;
039
040  public SquidClassLoader(Collection<File> files) throws IOException {
041    super(null);
042    loaders = new ArrayList<Loader>();
043    for (File file : files) {
044      if (file.exists()) {
045        if (file.isDirectory()) {
046          loaders.add(new FileSystemLoader(file));
047        } else {
048          loaders.add(new JarLoader(file));
049        }
050      }
051    }
052  }
053
054  @Override
055  protected Class<?> findClass(String name) throws ClassNotFoundException {
056    byte[] classBytes = loadClassBytes(name);
057    if (classBytes == null) {
058      throw new ClassNotFoundException(name);
059    }
060    // TODO Godin: definePackage ?
061    return defineClass(name, classBytes, 0, classBytes.length);
062  }
063
064  private byte[] loadClassBytes(String name) {
065    String resourceName = name.replace('.', '/') + ".class";
066    for (Loader loader : loaders) {
067      byte[] classBytes = loader.loadBytes(resourceName);
068      if (classBytes != null) {
069        return classBytes;
070      }
071    }
072    return null;
073  }
074
075  @Override
076  public URL findResource(String name) {
077    for (Loader loader : loaders) {
078      URL url = loader.findResource(name);
079      if (url != null) {
080        return url;
081      }
082    }
083    return null;
084  }
085
086  @Override
087  protected Enumeration<URL> findResources(String name) throws IOException {
088    List<URL> result = new ArrayList<URL>();
089    for (Loader loader : loaders) {
090      URL url = loader.findResource(name);
091      if (url != null) {
092        result.add(url);
093      }
094    }
095    return Iterators.asEnumeration(result.iterator());
096  }
097
098  /**
099   * Closes this class loader, so that it can no longer be used to load new classes or resources.
100   * Any classes or resources that are already loaded, are still accessible.
101   * 
102   * If class loader is already closed, then invoking this method has no effect.
103   */
104  public void close() {
105    for (Loader loader : loaders) {
106      loader.close();
107    }
108  }
109
110}