GetDeps.java revision 3014:a3dd196e5341
1/*
2 * Copyright (c) 2009, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import java.io.*;
25import java.util.*;
26import java.util.regex.Pattern;
27import javax.tools.*;
28
29import com.sun.tools.classfile.*;
30import com.sun.tools.classfile.Dependencies.*;
31import com.sun.tools.classfile.Dependency.Location;
32import com.sun.tools.javac.file.JavacFileManager;
33import com.sun.tools.javac.util.Context;
34
35/**
36 * Demo utility for using the classfile dependency analysis API framework.
37 *
38 * Usage:
39 *    getdeps [options] classes
40 * where options include:
41 *    -classpath path   where to find classes to analyze
42 *    -p package-name   restrict analysis to classes in this package
43 *                      (may be given multiple times)
44 *    -r regex          restrict analysis to packages matching pattern
45 *                      (-p and -r are exclusive)
46 *    -rev              invert the dependencies in the output
47 *    -t                transitive closure of dependencies
48 */
49public class GetDeps {
50    public static void main(String... args) throws Exception {
51        new GetDeps().run(args);
52    }
53
54    void run(String... args) throws IOException, ClassFileNotFoundException {
55        PrintWriter pw = new PrintWriter(System.out);
56        try {
57            run(pw, args);
58        } finally {
59            pw.flush();
60        }
61    }
62
63    void run(PrintWriter out, String... args) throws IOException, ClassFileNotFoundException {
64        decodeArgs(args);
65
66        final StandardJavaFileManager fm = new JavacFileManager(new Context(), false, null);
67        if (classpath != null)
68            fm.setLocation(StandardLocation.CLASS_PATH, classpath);
69
70        ClassFileReader reader = new ClassFileReader(fm);
71
72        Dependencies d = new Dependencies();
73
74        if (regex != null)
75            d.setFilter(Dependencies.getRegexFilter(Pattern.compile(regex)));
76
77        if (packageNames.size() > 0)
78            d.setFilter(Dependencies.getPackageFilter(packageNames, false));
79
80        SortedRecorder r = new SortedRecorder(reverse);
81
82        d.findAllDependencies(reader, rootClassNames, transitiveClosure, r);
83
84        SortedMap<Location,SortedSet<Dependency>> deps = r.getMap();
85        for (Map.Entry<Location, SortedSet<Dependency>> e: deps.entrySet()) {
86            out.println(e.getKey());
87            for (Dependency dep: e.getValue()) {
88                out.println("    " + dep.getTarget());
89            }
90        }
91    }
92
93    void decodeArgs(String... args) {
94        rootClassNames = new TreeSet<String>();
95        packageNames = new TreeSet<String>();
96
97        for (int i = 0; i < args.length; i++) {
98            String arg = args[i];
99            if (arg.equals("-classpath") && (i + 1 < args.length))
100                classpath = getPathFiles(args[++i]);
101            else if (arg.equals("-p") && (i + 1 < args.length))
102                packageNames.add(args[++i]);
103            else if (arg.equals("-r") && (i + 1 < args.length))
104                regex = args[++i];
105            else if (arg.equals("-rev"))
106                reverse = true;
107            else if (arg.equals("-t"))
108                transitiveClosure = true;
109            else if (arg.startsWith("-"))
110                throw new Error(arg);
111            else {
112                for ( ; i < args.length; i++)
113                    rootClassNames.add(args[i]);
114            }
115        }
116    }
117
118    List<File> getPathFiles(String path) {
119        List<File> files = new ArrayList<File>();
120        for (String p: path.split(File.pathSeparator)) {
121            if (p.length() > 0)
122                files.add(new File(p));
123        }
124        return files;
125    }
126
127    boolean transitiveClosure;
128    List<File> classpath;
129    Set<String> rootClassNames;
130    Set<String> packageNames;
131    String regex;
132    boolean reverse;
133
134
135    static class ClassFileReader implements Dependencies.ClassFileReader {
136        private JavaFileManager fm;
137
138        ClassFileReader(JavaFileManager fm) {
139            this.fm = fm;
140        }
141
142        @Override
143        public ClassFile getClassFile(String className) throws ClassFileNotFoundException {
144            try {
145                JavaFileObject fo = fm.getJavaFileForInput(
146                        StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS);
147                if (fo == null)
148                    fo = fm.getJavaFileForInput(
149                        StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS);
150                if (fo == null)
151                    throw new ClassFileNotFoundException(className);
152                InputStream in = fo.openInputStream();
153                try {
154                    return ClassFile.read(in);
155                } finally {
156                    in.close();
157                }
158            } catch (ConstantPoolException e) {
159                throw new ClassFileNotFoundException(className, e);
160            } catch (IOException e) {
161                throw new ClassFileNotFoundException(className, e);
162            }
163        }
164    };
165
166    static class SortedRecorder implements Recorder {
167        public SortedRecorder(boolean reverse) {
168            this.reverse = reverse;
169        }
170
171        public void addDependency(Dependency d) {
172            Location o = (reverse ? d.getTarget() : d.getOrigin());
173            SortedSet<Dependency> odeps = map.get(o);
174            if (odeps == null) {
175                Comparator<Dependency> c = (reverse ? originComparator : targetComparator);
176                map.put(o, odeps = new TreeSet<Dependency>(c));
177            }
178            odeps.add(d);
179        }
180
181        public SortedMap<Location, SortedSet<Dependency>> getMap() {
182            return map;
183        }
184
185        private Comparator<Dependency> originComparator = new Comparator<Dependency>() {
186            public int compare(Dependency o1, Dependency o2) {
187                return o1.getOrigin().toString().compareTo(o2.getOrigin().toString());
188            }
189        };
190
191        private Comparator<Dependency> targetComparator = new Comparator<Dependency>() {
192            public int compare(Dependency o1, Dependency o2) {
193                return o1.getTarget().toString().compareTo(o2.getTarget().toString());
194            }
195        };
196
197        private Comparator<Location> locationComparator = new Comparator<Location>() {
198            public int compare(Location o1, Location o2) {
199                return o1.toString().compareTo(o2.toString());
200            }
201        };
202
203        private final SortedMap<Location, SortedSet<Dependency>> map =
204                new TreeMap<Location, SortedSet<Dependency>>(locationComparator);
205
206        boolean reverse;
207    }
208
209}
210