1 /* 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.tools.jdeps; 26 27 import com.sun.tools.classfile.ClassFile; 28 import com.sun.tools.classfile.ConstantPoolException; 29 import com.sun.tools.classfile.Dependencies.ClassFileError; 30 import java.io.*; 31 import java.nio.file.FileVisitResult; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.SimpleFileVisitor; 35 import java.nio.file.attribute.BasicFileAttributes; 36 import java.util.*; 37 import java.util.jar.JarEntry; 38 import java.util.jar.JarFile; 39 40 /** 41 * ClassFileReader reads ClassFile(s) of a given path that can be 42 * a .class file, a directory, or a JAR file. 43 */ 44 public class ClassFileReader implements Iterator<ClassFile> { 45 /** 46 * Returns a ClassFileReader instance of a given path. 47 */ 48 public static ClassFileReader newInstance(File path) throws IOException { 49 if (!path.exists()) { 50 throw new FileNotFoundException(path.getAbsolutePath()); 51 } 52 53 if (path.isDirectory()) { 54 return new DirectoryArchive(path.toPath()); 55 } else if (path.getName().endsWith(".jar")) { 56 return new JarFileArchive(path.toPath()); 57 } else { 58 return new ClassFileReader(path.toPath()); 59 } 60 } 61 62 protected final Path path; 63 protected int classCount; 64 private ClassFileReader(Path path) throws IOException { 65 this.path = path; 66 if (path.toFile().getName().endsWith(".class")) { 67 this.classCount = 0; 68 } else { 69 this.classCount = -1; 70 } 71 } 72 73 public String getFileName() { 74 return path.toFile().getName(); 75 } 76 77 public ClassFile getClassFile(String classname) { 78 String fn = classname.replace('.', File.separatorChar) + ".class"; 79 if (!fn.equals(getFileName())) { 80 return null; 81 } 82 83 try (InputStream is = Files.newInputStream(path)) { 84 return ClassFile.read(is); 85 } catch (IOException | ConstantPoolException e) { 86 throw new ClassFileError(e); 87 } 88 } 89 90 public Iterable<ClassFile> getClassFiles() { 91 final Iterator<ClassFile> iter = this; 92 return new Iterable<ClassFile>() { 93 public Iterator<ClassFile> iterator() { 94 return iter; 95 } 96 }; 97 } 98 99 @Override 100 public boolean hasNext() { 101 return classCount == 0; 102 } 103 104 @Override 105 public ClassFile next() { 106 if (!hasNext()) { 107 throw new NoSuchElementException(); 108 } 109 110 try (InputStream is = Files.newInputStream(path)) { 111 classCount++; 112 return ClassFile.read(is); 113 } catch (IOException | ConstantPoolException e) { 114 throw new ClassFileError(e); 115 } 116 } 117 118 @Override 119 public void remove() { 120 throw new UnsupportedOperationException("Not supported yet."); 121 } 122 123 @Override 124 public String toString() { 125 return path.toString(); 126 } 127 128 private static class DirectoryArchive extends ClassFileReader { 129 final List<Path> classes; 130 private int index; 131 132 DirectoryArchive(Path path) throws IOException { 133 super(path); 134 classes = walkTree(path); 135 index = 0; 136 } 137 138 @Override 139 public ClassFile getClassFile(String classname) { 140 String fn = classname.replace('.', '/') + ".class"; 141 Path p = path.resolve(fn); 142 if (!p.toFile().exists()) { 143 return null; 144 } 145 try (InputStream is = Files.newInputStream(p)) { 146 return ClassFile.read(is); 147 } catch (IOException | ConstantPoolException e) { 148 throw new ClassFileError(e); 149 } 150 } 151 152 List<Path> walkTree(Path dir) throws IOException { 153 final List<Path> files = new ArrayList<>(); 154 155 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { 156 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 157 throws IOException { 158 if (file.toFile().getName().endsWith(".class")) { 159 files.add(file); 160 } 161 return FileVisitResult.CONTINUE; 162 } 163 164 public FileVisitResult postVisitDirectory(Path dir, IOException e) 165 throws IOException { 166 if (e == null) { 167 return FileVisitResult.CONTINUE; 168 } else { 169 // directory iteration failed 170 throw e; 171 } 172 } 173 }); 174 return files; 175 } 176 177 @Override 178 public boolean hasNext() { 179 return index != classes.size(); 180 } 181 182 @Override 183 public ClassFile next() { 184 if (!hasNext()) { 185 throw new NoSuchElementException(); 186 } 187 Path path = classes.get(index++); 188 try (InputStream is = Files.newInputStream(path)) { 189 classCount++; 190 return ClassFile.read(is); 191 } catch (IOException | ConstantPoolException e) { 192 throw new ClassFileError(e); 193 } 194 } 195 } 196 197 private static class JarFileArchive extends ClassFileReader { 198 final JarFile jarfile; 199 final Enumeration<JarEntry> entries; 200 JarEntry nextEntry; 201 202 JarFileArchive(Path path) throws IOException { 203 super(path); 204 this.jarfile = new JarFile(path.toFile()); 205 this.entries = jarfile.entries(); 206 while (entries.hasMoreElements()) { 207 JarEntry e = entries.nextElement(); 208 String name = e.getName(); 209 if (name.endsWith(".class")) { 210 nextEntry = e; 211 break; 212 } 213 } 214 } 215 216 @Override 217 public ClassFile getClassFile(String classname) { 218 String fn = classname.replace('.', '/') + ".class"; 219 JarEntry e = jarfile.getJarEntry(fn); 220 if (e == null) { 221 return null; 222 } 223 224 try (InputStream is = jarfile.getInputStream(e)) { 225 classCount++; 226 return ClassFile.read(is); 227 } catch (IOException | ConstantPoolException ex) { 228 throw new ClassFileError(ex); 229 } 230 } 231 232 @Override 233 public boolean hasNext() { 234 return nextEntry != null; 235 } 236 237 @Override 238 public ClassFile next() { 239 if (!hasNext()) { 240 throw new NoSuchElementException(); 241 } 242 243 ClassFile cf; 244 try { 245 cf = ClassFile.read(jarfile.getInputStream(nextEntry)); 246 } catch (IOException | ConstantPoolException ex) { 247 throw new ClassFileError(ex); 248 } 249 JarEntry entry = nextEntry; 250 nextEntry = null; 251 while (entries.hasMoreElements()) { 252 JarEntry e = entries.nextElement(); 253 String name = e.getName(); 254 if (name.endsWith(".class")) { 255 nextEntry = e; 256 break; 257 } 258 } 259 return cf; 260 } 261 } 262 }