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 { 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 DirectoryReader(path.toPath()); 55 } else if (path.getName().endsWith(".jar")) { 56 return new JarFileReader(path.toPath()); 57 } else { 58 return new ClassFileReader(path.toPath()); 59 } 60 } 61 62 protected final Path path; 63 protected final String baseFileName; 64 private ClassFileReader(Path path) { 65 this.path = path; 66 this.baseFileName = path.getFileName().toString(); 67 } 68 69 public String getFileName() { 70 return path.getFileName().toString(); 71 } 72 73 public ClassFile getClassFile(String classname) { 74 String fn = classname.replace('.', File.separatorChar) + ".class"; 75 if (!fn.equals(baseFileName)) { 76 return null; 77 } 78 79 try (InputStream is = Files.newInputStream(path)) { 80 return ClassFile.read(is); 81 } catch (IOException | ConstantPoolException e) { 82 throw new ClassFileError(e); 83 } 84 } 85 86 public Iterable<ClassFile> getClassFiles() throws IOException { 87 return new Iterable<ClassFile>() { 88 public Iterator<ClassFile> iterator() { 89 return new FileIterator(); 90 } 91 }; 92 } 93 94 class FileIterator implements Iterator<ClassFile> { 95 int count; 96 FileIterator() { 97 this.count = 0; 98 } 99 public boolean hasNext() { 100 return count == 0 && baseFileName.endsWith(".class"); 101 } 102 103 public ClassFile next() { 104 if (!hasNext()) { 105 throw new NoSuchElementException(); 106 } 107 108 try (InputStream is = Files.newInputStream(path)) { 109 count++; 110 return ClassFile.read(is); 111 } catch (IOException | ConstantPoolException e) { 112 throw new ClassFileError(e); 113 } 114 } 115 116 public void remove() { 117 throw new UnsupportedOperationException("Not supported yet."); 118 } 119 } 120 121 public String toString() { 122 return path.toString(); 123 } 124 125 private static class DirectoryReader extends ClassFileReader { 126 DirectoryReader(Path path) throws IOException { 127 super(path); 128 } 129 130 @Override 131 public ClassFile getClassFile(String classname) { 132 String fn = classname.replace('.', '/') + ".class"; 133 Path p = path.resolve(fn); 134 if (!p.toFile().exists()) { 135 return null; 136 } 137 try (InputStream is = Files.newInputStream(p)) { 138 return ClassFile.read(is); 139 } catch (IOException | ConstantPoolException e) { 140 throw new ClassFileError(e); 141 } 142 } 143 144 public Iterable<ClassFile> getClassFiles() throws IOException { 145 final Iterator<ClassFile> iter = new DirectoryIterator(); 146 return new Iterable<ClassFile>() { 147 public Iterator<ClassFile> iterator() { 148 return iter; 149 } 150 }; 151 } 152 153 private List<Path> walkTree(Path dir) throws IOException { 154 final List<Path> files = new ArrayList<>(); 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 return files; 165 } 166 167 class DirectoryIterator implements Iterator<ClassFile> { 168 private List<Path> entries; 169 private int index = 0; 170 DirectoryIterator() throws IOException { 171 entries = walkTree(path); 172 index = 0; 173 } 174 175 public boolean hasNext() { 176 return index != entries.size(); 177 } 178 179 public ClassFile next() { 180 if (!hasNext()) { 181 throw new NoSuchElementException(); 182 } 183 Path path = entries.get(index++); 184 try (InputStream is = Files.newInputStream(path)) { 185 return ClassFile.read(is); 186 } catch (IOException | ConstantPoolException e) { 187 throw new ClassFileError(e); 188 } 189 } 190 191 public void remove() { 192 throw new UnsupportedOperationException("Not supported yet."); 193 } 194 } 195 } 196 197 private static class JarFileReader extends ClassFileReader { 198 final JarFile jarfile; 199 JarFileReader(Path path) throws IOException { 200 super(path); 201 this.jarfile = new JarFile(path.toFile()); 202 } 203 204 public ClassFile getClassFile(String classname) { 205 String fn = classname.replace('.', '/') + ".class"; 206 JarEntry e = jarfile.getJarEntry(fn); 207 if (e == null) { 208 return null; 209 } 210 211 try (InputStream is = jarfile.getInputStream(e)) { 212 return ClassFile.read(is); 213 } catch (IOException | ConstantPoolException ex) { 214 throw new ClassFileError(ex); 215 } 216 } 217 218 public Iterable<ClassFile> getClassFiles() throws IOException { 219 final Iterator<ClassFile> iter = new JarFileIterator(); 220 return new Iterable<ClassFile>() { 221 public Iterator<ClassFile> iterator() { 222 return iter; 223 } 224 }; 225 } 226 227 class JarFileIterator implements Iterator<ClassFile> { 228 private Enumeration<JarEntry> entries; 229 private JarEntry nextEntry; 230 JarFileIterator() { 231 this.entries = jarfile.entries(); 232 while (entries.hasMoreElements()) { 233 JarEntry e = entries.nextElement(); 234 String name = e.getName(); 235 if (name.endsWith(".class")) { 236 this.nextEntry = e; 237 break; 238 } 239 } 240 } 241 242 public boolean hasNext() { 243 return nextEntry != null; 244 } 245 246 public ClassFile next() { 247 if (!hasNext()) { 248 throw new NoSuchElementException(); 249 } 250 251 ClassFile cf; 252 try { 253 cf = ClassFile.read(jarfile.getInputStream(nextEntry)); 254 } catch (IOException | ConstantPoolException ex) { 255 throw new ClassFileError(ex); 256 } 257 JarEntry entry = nextEntry; 258 nextEntry = null; 259 while (entries.hasMoreElements()) { 260 JarEntry e = entries.nextElement(); 261 String name = e.getName(); 262 if (name.endsWith(".class")) { 263 nextEntry = e; 264 break; 265 } 266 } 267 return cf; 268 } 269 270 public void remove() { 271 throw new UnsupportedOperationException("Not supported yet."); 272 } 273 } 274 } 275 }