Thu, 17 Oct 2013 13:19:48 -0700
8015912: jdeps support to output in dot file format
8026255: Switch jdeps to follow traditional Java option style
Reviewed-by: alanb
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;
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;
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(Path path) throws IOException {
49 if (!Files.exists(path)) {
50 throw new FileNotFoundException(path.toString());
51 }
53 if (Files.isDirectory(path)) {
54 return new DirectoryReader(path);
55 } else if (path.getFileName().toString().endsWith(".jar")) {
56 return new JarFileReader(path);
57 } else {
58 return new ClassFileReader(path);
59 }
60 }
62 /**
63 * Returns a ClassFileReader instance of a given JarFile.
64 */
65 public static ClassFileReader newInstance(Path path, JarFile jf) throws IOException {
66 return new JarFileReader(path, jf);
67 }
69 protected final Path path;
70 protected final String baseFileName;
71 private ClassFileReader(Path path) {
72 this.path = path;
73 this.baseFileName = path.getFileName() != null
74 ? path.getFileName().toString()
75 : path.toString();
76 }
78 public String getFileName() {
79 return baseFileName;
80 }
82 /**
83 * Returns the ClassFile matching the given binary name
84 * or a fully-qualified class name.
85 */
86 public ClassFile getClassFile(String name) throws IOException {
87 if (name.indexOf('.') > 0) {
88 int i = name.lastIndexOf('.');
89 String pathname = name.replace('.', File.separatorChar) + ".class";
90 if (baseFileName.equals(pathname) ||
91 baseFileName.equals(pathname.substring(0, i) + "$" +
92 pathname.substring(i+1, pathname.length()))) {
93 return readClassFile(path);
94 }
95 } else {
96 if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
97 return readClassFile(path);
98 }
99 }
100 return null;
101 }
103 public Iterable<ClassFile> getClassFiles() throws IOException {
104 return new Iterable<ClassFile>() {
105 public Iterator<ClassFile> iterator() {
106 return new FileIterator();
107 }
108 };
109 }
111 protected ClassFile readClassFile(Path p) throws IOException {
112 InputStream is = null;
113 try {
114 is = Files.newInputStream(p);
115 return ClassFile.read(is);
116 } catch (ConstantPoolException e) {
117 throw new ClassFileError(e);
118 } finally {
119 if (is != null) {
120 is.close();
121 }
122 }
123 }
125 class FileIterator implements Iterator<ClassFile> {
126 int count;
127 FileIterator() {
128 this.count = 0;
129 }
130 public boolean hasNext() {
131 return count == 0 && baseFileName.endsWith(".class");
132 }
134 public ClassFile next() {
135 if (!hasNext()) {
136 throw new NoSuchElementException();
137 }
138 try {
139 ClassFile cf = readClassFile(path);
140 count++;
141 return cf;
142 } catch (IOException e) {
143 throw new ClassFileError(e);
144 }
145 }
147 public void remove() {
148 throw new UnsupportedOperationException("Not supported yet.");
149 }
150 }
152 public String toString() {
153 return path.toString();
154 }
156 private static class DirectoryReader extends ClassFileReader {
157 DirectoryReader(Path path) throws IOException {
158 super(path);
159 }
161 public ClassFile getClassFile(String name) throws IOException {
162 if (name.indexOf('.') > 0) {
163 int i = name.lastIndexOf('.');
164 String pathname = name.replace('.', File.separatorChar) + ".class";
165 Path p = path.resolve(pathname);
166 if (!Files.exists(p)) {
167 p = path.resolve(pathname.substring(0, i) + "$" +
168 pathname.substring(i+1, pathname.length()));
169 }
170 if (Files.exists(p)) {
171 return readClassFile(p);
172 }
173 } else {
174 Path p = path.resolve(name + ".class");
175 if (Files.exists(p)) {
176 return readClassFile(p);
177 }
178 }
179 return null;
180 }
182 public Iterable<ClassFile> getClassFiles() throws IOException {
183 final Iterator<ClassFile> iter = new DirectoryIterator();
184 return new Iterable<ClassFile>() {
185 public Iterator<ClassFile> iterator() {
186 return iter;
187 }
188 };
189 }
191 private List<Path> walkTree(Path dir) throws IOException {
192 final List<Path> files = new ArrayList<Path>();
193 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
194 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
195 throws IOException {
196 if (file.getFileName().toString().endsWith(".class")) {
197 files.add(file);
198 }
199 return FileVisitResult.CONTINUE;
200 }
201 });
202 return files;
203 }
205 class DirectoryIterator implements Iterator<ClassFile> {
206 private List<Path> entries;
207 private int index = 0;
208 DirectoryIterator() throws IOException {
209 entries = walkTree(path);
210 index = 0;
211 }
213 public boolean hasNext() {
214 return index != entries.size();
215 }
217 public ClassFile next() {
218 if (!hasNext()) {
219 throw new NoSuchElementException();
220 }
221 Path path = entries.get(index++);
222 try {
223 return readClassFile(path);
224 } catch (IOException e) {
225 throw new ClassFileError(e);
226 }
227 }
229 public void remove() {
230 throw new UnsupportedOperationException("Not supported yet.");
231 }
232 }
233 }
235 private static class JarFileReader extends ClassFileReader {
236 final JarFile jarfile;
237 JarFileReader(Path path) throws IOException {
238 this(path, new JarFile(path.toFile()));
239 }
240 JarFileReader(Path path, JarFile jf) throws IOException {
241 super(path);
242 this.jarfile = jf;
243 }
245 public ClassFile getClassFile(String name) throws IOException {
246 if (name.indexOf('.') > 0) {
247 int i = name.lastIndexOf('.');
248 String entryName = name.replace('.', '/') + ".class";
249 JarEntry e = jarfile.getJarEntry(entryName);
250 if (e == null) {
251 e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
252 + entryName.substring(i + 1, entryName.length()));
253 }
254 if (e != null) {
255 return readClassFile(e);
256 }
257 } else {
258 JarEntry e = jarfile.getJarEntry(name + ".class");
259 if (e != null) {
260 return readClassFile(e);
261 }
262 }
263 return null;
264 }
266 private ClassFile readClassFile(JarEntry e) throws IOException {
267 InputStream is = null;
268 try {
269 is = jarfile.getInputStream(e);
270 return ClassFile.read(is);
271 } catch (ConstantPoolException ex) {
272 throw new ClassFileError(ex);
273 } finally {
274 if (is != null)
275 is.close();
276 }
277 }
279 public Iterable<ClassFile> getClassFiles() throws IOException {
280 final Iterator<ClassFile> iter = new JarFileIterator();
281 return new Iterable<ClassFile>() {
282 public Iterator<ClassFile> iterator() {
283 return iter;
284 }
285 };
286 }
288 class JarFileIterator implements Iterator<ClassFile> {
289 private Enumeration<JarEntry> entries;
290 private JarEntry nextEntry;
291 JarFileIterator() {
292 this.entries = jarfile.entries();
293 while (entries.hasMoreElements()) {
294 JarEntry e = entries.nextElement();
295 String name = e.getName();
296 if (name.endsWith(".class")) {
297 this.nextEntry = e;
298 break;
299 }
300 }
301 }
303 public boolean hasNext() {
304 return nextEntry != null;
305 }
307 public ClassFile next() {
308 if (!hasNext()) {
309 throw new NoSuchElementException();
310 }
312 ClassFile cf;
313 try {
314 cf = readClassFile(nextEntry);
315 } catch (IOException ex) {
316 throw new ClassFileError(ex);
317 }
318 JarEntry entry = nextEntry;
319 nextEntry = null;
320 while (entries.hasMoreElements()) {
321 JarEntry e = entries.nextElement();
322 String name = e.getName();
323 if (name.endsWith(".class")) {
324 nextEntry = e;
325 break;
326 }
327 }
328 return cf;
329 }
331 public void remove() {
332 throw new UnsupportedOperationException("Not supported yet.");
333 }
334 }
335 }
336 }