mchung@1472: /* mchung@1472: * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. mchung@1472: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mchung@1472: * mchung@1472: * This code is free software; you can redistribute it and/or modify it mchung@1472: * under the terms of the GNU General Public License version 2 only, as mchung@1472: * published by the Free Software Foundation. Oracle designates this mchung@1472: * particular file as subject to the "Classpath" exception as provided mchung@1472: * by Oracle in the LICENSE file that accompanied this code. mchung@1472: * mchung@1472: * This code is distributed in the hope that it will be useful, but WITHOUT mchung@1472: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mchung@1472: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mchung@1472: * version 2 for more details (a copy is included in the LICENSE file that mchung@1472: * accompanied this code). mchung@1472: * mchung@1472: * You should have received a copy of the GNU General Public License version mchung@1472: * 2 along with this work; if not, write to the Free Software Foundation, mchung@1472: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mchung@1472: * mchung@1472: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mchung@1472: * or visit www.oracle.com if you need additional information or have any mchung@1472: * questions. mchung@1472: */ mchung@1472: package com.sun.tools.jdeps; mchung@1472: mchung@1472: import com.sun.tools.classfile.ClassFile; mchung@1472: import com.sun.tools.classfile.ConstantPoolException; mchung@1472: import com.sun.tools.classfile.Dependencies.ClassFileError; mchung@1472: import java.io.*; mchung@1472: import java.nio.file.FileVisitResult; mchung@1472: import java.nio.file.Files; mchung@1472: import java.nio.file.Path; mchung@1472: import java.nio.file.SimpleFileVisitor; mchung@1472: import java.nio.file.attribute.BasicFileAttributes; mchung@1472: import java.util.*; mchung@1472: import java.util.jar.JarEntry; mchung@1472: import java.util.jar.JarFile; mchung@1472: mchung@1472: /** mchung@1472: * ClassFileReader reads ClassFile(s) of a given path that can be mchung@1472: * a .class file, a directory, or a JAR file. mchung@1472: */ mchung@1472: public class ClassFileReader { mchung@1472: /** mchung@1472: * Returns a ClassFileReader instance of a given path. mchung@1472: */ mchung@1472: public static ClassFileReader newInstance(File path) throws IOException { mchung@1472: if (!path.exists()) { mchung@1472: throw new FileNotFoundException(path.getAbsolutePath()); mchung@1472: } mchung@1472: mchung@1472: if (path.isDirectory()) { mchung@1472: return new DirectoryReader(path.toPath()); mchung@1472: } else if (path.getName().endsWith(".jar")) { mchung@1472: return new JarFileReader(path.toPath()); mchung@1472: } else { mchung@1472: return new ClassFileReader(path.toPath()); mchung@1472: } mchung@1472: } mchung@1472: mchung@1638: /** mchung@1638: * Returns a ClassFileReader instance of a given JarFile. mchung@1638: */ mchung@1638: public static ClassFileReader newInstance(Path path, JarFile jf) throws IOException { mchung@1638: return new JarFileReader(path, jf); mchung@1638: } mchung@1638: mchung@1472: protected final Path path; mchung@1472: protected final String baseFileName; mchung@1472: private ClassFileReader(Path path) { mchung@1472: this.path = path; mchung@1472: this.baseFileName = path.getFileName() != null mchung@1472: ? path.getFileName().toString() mchung@1472: : path.toString(); mchung@1472: } mchung@1472: mchung@1472: public String getFileName() { mchung@1472: return baseFileName; mchung@1472: } mchung@1472: mchung@1472: /** mchung@1472: * Returns the ClassFile matching the given binary name mchung@1472: * or a fully-qualified class name. mchung@1472: */ mchung@1472: public ClassFile getClassFile(String name) throws IOException { mchung@1472: if (name.indexOf('.') > 0) { mchung@1472: int i = name.lastIndexOf('.'); mchung@1472: String pathname = name.replace('.', File.separatorChar) + ".class"; mchung@1472: if (baseFileName.equals(pathname) || mchung@1472: baseFileName.equals(pathname.substring(0, i) + "$" + mchung@1472: pathname.substring(i+1, pathname.length()))) { mchung@1472: return readClassFile(path); mchung@1472: } mchung@1472: } else { mchung@1472: if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) { mchung@1472: return readClassFile(path); mchung@1472: } mchung@1472: } mchung@1472: return null; mchung@1472: } mchung@1472: mchung@1472: public Iterable getClassFiles() throws IOException { mchung@1472: return new Iterable() { mchung@1472: public Iterator iterator() { mchung@1472: return new FileIterator(); mchung@1472: } mchung@1472: }; mchung@1472: } mchung@1472: mchung@1472: protected ClassFile readClassFile(Path p) throws IOException { mchung@1472: InputStream is = null; mchung@1472: try { mchung@1472: is = Files.newInputStream(p); mchung@1472: return ClassFile.read(is); mchung@1472: } catch (ConstantPoolException e) { mchung@1472: throw new ClassFileError(e); mchung@1472: } finally { mchung@1472: if (is != null) { mchung@1472: is.close(); mchung@1472: } mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: class FileIterator implements Iterator { mchung@1472: int count; mchung@1472: FileIterator() { mchung@1472: this.count = 0; mchung@1472: } mchung@1472: public boolean hasNext() { mchung@1472: return count == 0 && baseFileName.endsWith(".class"); mchung@1472: } mchung@1472: mchung@1472: public ClassFile next() { mchung@1472: if (!hasNext()) { mchung@1472: throw new NoSuchElementException(); mchung@1472: } mchung@1472: try { mchung@1472: ClassFile cf = readClassFile(path); mchung@1472: count++; mchung@1472: return cf; mchung@1472: } catch (IOException e) { mchung@1472: throw new ClassFileError(e); mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: public void remove() { mchung@1472: throw new UnsupportedOperationException("Not supported yet."); mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: public String toString() { mchung@1472: return path.toString(); mchung@1472: } mchung@1472: mchung@1472: private static class DirectoryReader extends ClassFileReader { mchung@1472: DirectoryReader(Path path) throws IOException { mchung@1472: super(path); mchung@1472: } mchung@1472: mchung@1472: public ClassFile getClassFile(String name) throws IOException { mchung@1472: if (name.indexOf('.') > 0) { mchung@1472: int i = name.lastIndexOf('.'); mchung@1472: String pathname = name.replace('.', File.separatorChar) + ".class"; mchung@1472: Path p = path.resolve(pathname); mchung@1472: if (!p.toFile().exists()) { mchung@1472: p = path.resolve(pathname.substring(0, i) + "$" + mchung@1472: pathname.substring(i+1, pathname.length())); mchung@1472: } mchung@1472: if (p.toFile().exists()) { mchung@1472: return readClassFile(p); mchung@1472: } mchung@1472: } else { mchung@1472: Path p = path.resolve(name + ".class"); mchung@1472: if (p.toFile().exists()) { mchung@1472: return readClassFile(p); mchung@1472: } mchung@1472: } mchung@1472: return null; mchung@1472: } mchung@1472: mchung@1472: public Iterable getClassFiles() throws IOException { mchung@1472: final Iterator iter = new DirectoryIterator(); mchung@1472: return new Iterable() { mchung@1472: public Iterator iterator() { mchung@1472: return iter; mchung@1472: } mchung@1472: }; mchung@1472: } mchung@1472: mchung@1472: private List walkTree(Path dir) throws IOException { mchung@1472: final List files = new ArrayList(); mchung@1472: Files.walkFileTree(dir, new SimpleFileVisitor() { mchung@1472: public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) mchung@1472: throws IOException { mchung@1472: if (file.toFile().getName().endsWith(".class")) { mchung@1472: files.add(file); mchung@1472: } mchung@1472: return FileVisitResult.CONTINUE; mchung@1472: } mchung@1472: }); mchung@1472: return files; mchung@1472: } mchung@1472: mchung@1472: class DirectoryIterator implements Iterator { mchung@1472: private List entries; mchung@1472: private int index = 0; mchung@1472: DirectoryIterator() throws IOException { mchung@1472: entries = walkTree(path); mchung@1472: index = 0; mchung@1472: } mchung@1472: mchung@1472: public boolean hasNext() { mchung@1472: return index != entries.size(); mchung@1472: } mchung@1472: mchung@1472: public ClassFile next() { mchung@1472: if (!hasNext()) { mchung@1472: throw new NoSuchElementException(); mchung@1472: } mchung@1472: Path path = entries.get(index++); mchung@1472: try { mchung@1472: return readClassFile(path); mchung@1472: } catch (IOException e) { mchung@1472: throw new ClassFileError(e); mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: public void remove() { mchung@1472: throw new UnsupportedOperationException("Not supported yet."); mchung@1472: } mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: private static class JarFileReader extends ClassFileReader { mchung@1472: final JarFile jarfile; mchung@1472: JarFileReader(Path path) throws IOException { mchung@1638: this(path, new JarFile(path.toFile())); mchung@1638: } mchung@1638: JarFileReader(Path path, JarFile jf) throws IOException { mchung@1472: super(path); mchung@1638: this.jarfile = jf; mchung@1472: } mchung@1472: mchung@1472: public ClassFile getClassFile(String name) throws IOException { mchung@1472: if (name.indexOf('.') > 0) { mchung@1472: int i = name.lastIndexOf('.'); mchung@1472: String entryName = name.replace('.', '/') + ".class"; mchung@1472: JarEntry e = jarfile.getJarEntry(entryName); mchung@1472: if (e == null) { mchung@1472: e = jarfile.getJarEntry(entryName.substring(0, i) + "$" mchung@1472: + entryName.substring(i + 1, entryName.length())); mchung@1472: } mchung@1472: if (e != null) { mchung@1472: return readClassFile(e); mchung@1472: } mchung@1472: } else { mchung@1472: JarEntry e = jarfile.getJarEntry(name + ".class"); mchung@1472: if (e != null) { mchung@1472: return readClassFile(e); mchung@1472: } mchung@1472: } mchung@1472: return null; mchung@1472: } mchung@1472: mchung@1472: private ClassFile readClassFile(JarEntry e) throws IOException { mchung@1472: InputStream is = null; mchung@1472: try { mchung@1472: is = jarfile.getInputStream(e); mchung@1472: return ClassFile.read(is); mchung@1472: } catch (ConstantPoolException ex) { mchung@1472: throw new ClassFileError(ex); mchung@1472: } finally { mchung@1472: if (is != null) mchung@1472: is.close(); mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: public Iterable getClassFiles() throws IOException { mchung@1472: final Iterator iter = new JarFileIterator(); mchung@1472: return new Iterable() { mchung@1472: public Iterator iterator() { mchung@1472: return iter; mchung@1472: } mchung@1472: }; mchung@1472: } mchung@1472: mchung@1472: class JarFileIterator implements Iterator { mchung@1472: private Enumeration entries; mchung@1472: private JarEntry nextEntry; mchung@1472: JarFileIterator() { mchung@1472: this.entries = jarfile.entries(); mchung@1472: while (entries.hasMoreElements()) { mchung@1472: JarEntry e = entries.nextElement(); mchung@1472: String name = e.getName(); mchung@1472: if (name.endsWith(".class")) { mchung@1472: this.nextEntry = e; mchung@1472: break; mchung@1472: } mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: public boolean hasNext() { mchung@1472: return nextEntry != null; mchung@1472: } mchung@1472: mchung@1472: public ClassFile next() { mchung@1472: if (!hasNext()) { mchung@1472: throw new NoSuchElementException(); mchung@1472: } mchung@1472: mchung@1472: ClassFile cf; mchung@1472: try { mchung@1472: cf = readClassFile(nextEntry); mchung@1472: } catch (IOException ex) { mchung@1472: throw new ClassFileError(ex); mchung@1472: } mchung@1472: JarEntry entry = nextEntry; mchung@1472: nextEntry = null; mchung@1472: while (entries.hasMoreElements()) { mchung@1472: JarEntry e = entries.nextElement(); mchung@1472: String name = e.getName(); mchung@1472: if (name.endsWith(".class")) { mchung@1472: nextEntry = e; mchung@1472: break; mchung@1472: } mchung@1472: } mchung@1472: return cf; mchung@1472: } mchung@1472: mchung@1472: public void remove() { mchung@1472: throw new UnsupportedOperationException("Not supported yet."); mchung@1472: } mchung@1472: } mchung@1472: } mchung@1472: }