Home | History | Annotate | Download | only in util
      1 /**
      2  * Copyright 2007 Google Inc.
      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.tonicsystems.jarjar.util;
     18 
     19 import java.util.*;
     20 import java.util.zip.*;
     21 import java.io.*;
     22 import java.util.jar.*;
     23 
     24 public class ClassPathIterator implements Iterator<ClassPathEntry>
     25 {
     26     private static final FileFilter CLASS_FILTER = new FileFilter() {
     27         public boolean accept(File file) {
     28             return file.isDirectory() || isClass(file.getName());
     29         }
     30     };
     31 
     32     private static final FileFilter JAR_FILTER = new FileFilter() {
     33         public boolean accept(File file) {
     34             return hasExtension(file.getName(), ".jar");
     35         }
     36     };
     37 
     38     private final Iterator<File> files;
     39     private Iterator<ClassPathEntry> entries = Collections.<ClassPathEntry>emptyList().iterator();
     40     private ClassPathEntry next;
     41     private List<ZipFile> zips = new ArrayList<ZipFile>();
     42 
     43     public ClassPathIterator(String classPath) throws IOException {
     44         this(new File(System.getProperty("user.dir")), classPath, null);
     45     }
     46 
     47     public ClassPathIterator(File parent, String classPath, String delim) throws IOException {
     48         if (delim == null) {
     49             delim = System.getProperty("path.separator");
     50         }
     51         StringTokenizer st = new StringTokenizer(classPath, delim);
     52         List<File> fileList = new ArrayList<File>();
     53         while (st.hasMoreTokens()) {
     54             String part = (String)st.nextElement();
     55             boolean wildcard = false;
     56             if (part.endsWith("/*")) {
     57                 part = part.substring(0, part.length() - 1);
     58                 if (part.indexOf('*') >= 0)
     59                     throw new IllegalArgumentException("Multiple wildcards are not allowed: " + part);
     60                 wildcard = true;
     61             } else if (part.indexOf('*') >= 0) {
     62                 throw new IllegalArgumentException("Incorrect wildcard usage: " + part);
     63             }
     64 
     65             File file = new File(part);
     66             if (!file.isAbsolute())
     67                 file = new File(parent, part);
     68             if (!file.exists())
     69                 throw new IllegalArgumentException("File " + file + " does not exist");
     70 
     71             if (wildcard) {
     72                 if (!file.isDirectory())
     73                     throw new IllegalArgumentException("File " + file + " + is not a directory");
     74                 fileList.addAll(findFiles(file, JAR_FILTER, false, new ArrayList<File>()));
     75             } else {
     76                 fileList.add(file);
     77             }
     78         }
     79         this.files = fileList.iterator();
     80         advance();
     81     }
     82 
     83     public boolean hasNext() {
     84         return next != null;
     85     }
     86 
     87     /** Closes all zip files opened by this iterator. */
     88     public void close() throws IOException {
     89       next = null;
     90       for (ZipFile zip : zips) {
     91         zip.close();
     92       }
     93     }
     94 
     95     public void remove() {
     96         throw new UnsupportedOperationException();
     97     }
     98 
     99     public ClassPathEntry next() {
    100         if (!hasNext())
    101             throw new NoSuchElementException();
    102         ClassPathEntry result = next;
    103         try {
    104             advance();
    105         } catch (IOException e) {
    106             throw new RuntimeIOException(e);
    107         }
    108         return result;
    109     }
    110 
    111     private void advance() throws IOException {
    112         if (!entries.hasNext()) {
    113             if (!files.hasNext()) {
    114                 next = null;
    115                 return;
    116             }
    117             File file = files.next();
    118             if (hasExtension(file.getName(), ".jar")) {
    119                 ZipFile zip = new JarFile(file);
    120                 zips.add(zip);
    121                 entries = new ZipIterator(zip);
    122             } else if (hasExtension(file.getName(), ".zip")) {
    123                 ZipFile zip = new ZipFile(file);
    124                 zips.add(zip);
    125                 entries = new ZipIterator(zip);
    126             } else if (file.isDirectory()) {
    127                 entries = new FileIterator(file);
    128             } else {
    129                 throw new IllegalArgumentException("Do not know how to handle " + file);
    130             }
    131         }
    132 
    133         boolean foundClass = false;
    134         while (!foundClass && entries.hasNext()) {
    135           next = entries.next();
    136           foundClass = isClass(next.getName());
    137         }
    138         if (!foundClass) {
    139           advance();
    140         }
    141     }
    142 
    143     private static class ZipIterator implements Iterator<ClassPathEntry> {
    144       private final ZipFile zip;
    145       private final Enumeration<? extends ZipEntry> entries;
    146 
    147       ZipIterator(ZipFile zip) {
    148         this.zip = zip;
    149         this.entries = zip.entries();
    150       }
    151 
    152       public boolean hasNext() {
    153         return entries.hasMoreElements();
    154       }
    155 
    156       public void remove() {
    157         throw new UnsupportedOperationException();
    158       }
    159 
    160       public ClassPathEntry next() {
    161         final ZipEntry entry = entries.nextElement();
    162         return new ClassPathEntry() {
    163           public String getSource() {
    164             return zip.getName();
    165           }
    166 
    167           public String getName() {
    168             return entry.getName();
    169           }
    170 
    171           public InputStream openStream() throws IOException {
    172             return zip.getInputStream(entry);
    173           }
    174         };
    175       }
    176     }
    177 
    178     private static class FileIterator implements Iterator<ClassPathEntry> {
    179       private final File dir;
    180       private final Iterator<File> entries;
    181 
    182       FileIterator(File dir) {
    183         this.dir = dir;
    184         this.entries = findFiles(dir, CLASS_FILTER, true, new ArrayList<File>()).iterator();
    185       }
    186 
    187       public boolean hasNext() {
    188         return entries.hasNext();
    189       }
    190 
    191       public void remove() {
    192         throw new UnsupportedOperationException();
    193       }
    194 
    195       public ClassPathEntry next() {
    196         final File file = entries.next();
    197         return new ClassPathEntry() {
    198           public String getSource() throws IOException {
    199             return dir.getCanonicalPath();
    200           }
    201 
    202           public String getName() {
    203             return file.getName();
    204           }
    205 
    206           public InputStream openStream() throws IOException {
    207             return new BufferedInputStream(new FileInputStream(file));
    208           }
    209         };
    210       }
    211     }
    212 
    213     private static List<File> findFiles(File dir, FileFilter filter, boolean recurse, List<File> collect) {
    214         for (File file : dir.listFiles(filter)) {
    215             if (recurse && file.isDirectory()) {
    216                 findFiles(file, filter, recurse, collect);
    217             } else {
    218                 collect.add(file);
    219             }
    220         }
    221         return collect;
    222     }
    223 
    224     private static boolean isClass(String name) {
    225         return hasExtension(name, ".class");
    226     }
    227 
    228     private static boolean hasExtension(String name, String ext) {
    229         if (name.length() <  ext.length())
    230             return false;
    231         String actual = name.substring(name.length() - ext.length());
    232         return actual.equals(ext) || actual.equals(ext.toUpperCase());
    233     }
    234 }
    235