View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.tool;
19  
20  import org.apache.river.tool.classdepend.ClassDepend;
21  import org.apache.river.tool.classdepend.ClassDependParameters;
22  import org.apache.river.tool.classdepend.ClassDependParameters.CDPBuilder;
23  import org.apache.river.tool.classdepend.ClassDependencyRelationship;
24  import java.io.File;
25  import java.io.IOException;
26  import java.text.MessageFormat;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.Comparator;
31  import java.util.Iterator;
32  import java.util.LinkedList;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.MissingResourceException;
36  import java.util.ResourceBundle;
37  import java.util.Set;
38  import java.util.SortedSet;
39  import java.util.TreeSet;
40  
41  /**
42   * Tool used to analyze a set of classes and determine on what other classes
43   * they directly or indirectly depend. Typically this tool is used to
44   * compute the necessary and sufficient set of classes to include in a JAR
45   * file, for use in the class path of a client or service, or for use in the
46   * codebase of a client or service. The tool starts with a set of "root"
47   * classes and recursively computes a dependency graph, finding all of the
48   * classes referenced directly by the root classes, finding all of the
49   * classes referenced in turn by those classes, and so on, until no new
50   * classes are found or until classes that are not of interest are
51   * found. The normal output of the tool is a list of all of the classes in
52   * the dependency graph. The output from this command can be used as input
53   * to the <code>jar</code> tool, to create a JAR file containing precisely
54   * those classes.
55   * <p>
56   * The following items are discussed below:
57   * <ul>
58   * <li><a href="#running">Running the Tool</a>
59   * <li><a href="#processing">Processing Options</a>
60   * <li><a href="#output">Output Options and Arguments</a>
61   * <li><a href="#examples">Examples</a>
62   * </ul>
63   *
64   * <a name="running"></a>
65   * <h3>Running the Tool</h3>
66   *
67   * The command line for running the tool has the form:
68   * <blockquote><pre>
69   * java -jar <var><b>install_dir</b></var>/lib/classdep.jar \
70   *      -cp <var><b>input_classpath</b></var> <var><b>processing_options</b></var> <var><b>output_options</b></var>
71   * </pre></blockquote>
72   * <p>
73   * where <var><b>install_dir</b></var> is the directory where the JGDMS release
74   * is installed.
75   * Note that the options for this tool can be specified in any order, and
76   * can be intermixed.
77   *
78   * <p>
79   * The <code>-cp</code> class path value,
80   * <var><b>input_classpath</b></var>,
81   * is an argument to the <code>ClassDep</code> tool itself and should
82   * include all of the classes that might need to be included in the
83   * dependency analysis. Typically this will include all of your application
84   * classes, classes from the JGDMS release, and any other classes on which
85   * your classes might depend. It is safe to include more classes than are
86   * actually necessary (since the purpose of this tool is, after all, to
87   * determine which subset of these classes is actually necessary), but it is
88   * not necessary to include any classes that are part of the Java 2 SDK.
89   * The class path should be in the form of a list of directories or JAR
90   * files, delimited by a colon (":") on UNIX platforms and a semi-colon
91   * (";") on Microsoft Windows platforms. The order of locations in the path
92   * does not matter. If you use JAR files, any <code>Class-Path</code>
93   * manifest entries in those JAR files are ignored, so you must include the
94   * values of those manifest entries explicitly in the path, or errors may
95   * result. For example, if you include <code>jini-ext.jar</code> then you
96   * should explicitly include <code>jini-core.jar</code> as well, because
97   * <code>jini-core.jar</code> is in the <code>Class-Path</code> manifest
98   * entry of <code>jini-ext.jar</code>.
99   *
100  * <a name="processing"></a>
101  * <h3>Processing Options</h3>
102  *
103  * The root classes of the dependency graph can be specified by any
104  * combination of individual classes and directories of classes. Each of
105  * these options can be used any number of times, and are illustrated in the
106  * <a href="#examples">Examples</a> section of this page.
107  * <p>
108  * In general, you only need to specify concrete classes as roots, not
109  * interface types. When analyzing classes for the class path of an
110  * application, you typically need to include the top-level class (the one
111  * with the <code>main</code> method). When analyzing classes for the
112  * codebase of a service, you typically need to include the top-level proxy
113  * classes used by the service, any trust verifier classes for those
114  * proxies, and any custom entry classes used by the service for lookup
115  * service attributes. Also when analyzing classes for the codebase of a
116  * service, if the service's proxy can return leases, registration objects,
117  * or other proxies, or if your service generates events, and if those
118  * objects are only referenced by interface type in the top-level proxy, not
119  * by concrete class, then you also need to include the concrete classes of
120  * those other objects. In all cases, you typically need to include any stub
121  * classes that you generated with the <code>rmic</code> tool.
122  * <p>
123  * <dl>
124  * <dt><b><var>class</var></b>
125  * <dd>This option specifies the fully qualified name of an individual class
126  * to include as a root of the dependency graph. This option can be
127  * specified zero or more times. Each class you specify with this option
128  * needs to be in a package that is defined to be "inside" the graph (as
129  * described further below).</dd>
130  * 
131  * <dt><b><var>directory</var></b>
132  * <dd>This option specifies the root directory of a tree of compiled class
133  * files, all of which are to be included as roots of the dependency
134  * graph. This option can be specified zero or more times. The directory
135  * must be one of the directories specified in
136  * <var><b>input_classpath</b></var>,
137  * or a subdirectory of one, and must contain at least one filename
138  * separator character. Each class in the tree needs to be in a package that
139  * is defined to be "inside" the graph (as described further below).
140  * <p>
141  * <dt><b><var>directory</var></b>
142  * <dd>This option specifies the root directory of a tree of compiled class
143  * files that are considered as root for the dependency checking. This option
144  * can be specified zero or more times. The actual behavior depends on whether
145  * the <code>-newdirbehavior</code> options is specified. The directory must
146  * contain at least one filename separator character and be one of the
147  * directories specified in <var><b>input_classpath</b></var>, or when the old
148  * behavior is effective it can be a subdirectory of one of the directories on
149  * the <var><b>input_classpath</b></var>. Each class in the tree needs to be in
150  * a package that is defined to be "inside" the graph (as described further
151  * below).
152  * <p>
153  * When the <code>-newdirbehavior</code> options is set the <code>-inroot</code>
154  * and <code>-outroot</code> options can be used to include/exclude particular
155  * subtrees from the potential set of roots. When the old behavior is effective
156  * all classes are considered as root, but can be excluded through the
157  * <code>-prune</code> option.
158  * <p>
159  * <dl>
160  * <dt><b><code>-inroot</code> <var>package-prefix</var></b> (only valid with
161  * <code>-newdirbehavior</code>)
162  * <dd>Specifies a package namespace to include when selecting roots from the
163  * directory trees. Any classes found in this package or a subpackage of it are
164  * considered as root for the dependency checking, unless they are explicitly
165  * excluded using <code>-out</code>, <code>-skip</code> or <code>-outroot</code>
166  * options. If not specified all found classes are considered roots. This option
167  * can be specified zero or more times. Note that the argument to
168  * <code>-inroot</code> is a package namespace (delimited by "."), not a
169  * directory.</dd>
170  * </dl>
171  * <p>
172  * <dl>
173  * <dt><b><code>-outroot</code> <var>package-prefix</var></b> (only valid with
174  * <code>-newdirbehavior</code>)
175  * <dd>Specifies a package namespace to exclude when selecting roots from the
176  * directory trees. Within the directory trees as specified by the
177  * <code>rootdir</code> element, any classes that are in the given package or a
178  * subpackage of it are not treated as roots. This option can be specified zero
179  * or more times. Note that the argument to <code>-outroot</code> is a package
180  * namespace (delimited by "."), not a directory.</dd>
181  * </dl>
182  * <p>
183  * <dl>
184  * <dt><b><code>-prune</code> <var>package-prefix</var></b> (old behavior only)
185  * <dd>Specifies a package namespace to exclude when selecting roots from
186  * directory trees. Within the directory trees, any classes that are in the
187  * given package or a subpackage of it are not treated as roots. Note that
188  * this option has <i>no</i> effect on whether the classes in question end
189  * up "inside" or "outside" the dependency graph (as defined further below);
190  * it simply controls their use as roots. This option can be specified zero
191  * or more times. Note that the argument to <code>-prune</code> is a package
192  * namespace (delimited by "."), not a directory.</dd>
193  * </dl>
194  * <p>
195  * The <code>-skip</code> option (described further below) can be used to
196  * exclude specific classes from the set of roots.
197  * </dd>
198  * </dl>
199  * <p>
200  * Starting with the root classes, a dependency graph is constructed by
201  * examining the compiled class file for a class, finding all of the classes
202  * it references, and then in turn examining those classes.  The extent of
203  * the graph is determined by which packages are defined to be "inside" the
204  * graph and which are defined to be "outside" the graph.  If a referenced
205  * class is in a package that is defined to be outside the graph, that class
206  * is not included in the graph, and none of classes that it references are
207  * examined. All of the root classes must be in packages that are defined to
208  * be "inside" the graph.
209  * <p>
210  * The inside and outside packages are specified by using the following
211  * options. Each of these options may be specified zero or more times.  Some
212  * variations are illustrated in the <a href="#examples">Examples</a> section
213  * of this page.
214  * <p>
215  * <dl>
216  * <dt><b><code>-in</code> <var>package-prefix</var></b>
217  * <dd>Specifies a namespace of "inside" packages. Any classes in this
218  * package or a subpackage of it are included in the dependency graph (and
219  * hence are to be included in your JAR file), unless they are explicitly
220  * excluded using <code>-out</code> or <code>-skip</code> options. This
221  * option can be specified zero or more times. If no <code>-in</code>
222  * options are specified, the default is that all packages are considered to
223  * be inside packages.  Note that the argument to <code>-in</code> is a
224  * namespace, so none of its subpackages need to be specified as an argument
225  * to <code>-in</code>.
226  * <p>
227  * If you use this option, you will likely need to use it multiple
228  * times. For example, if your application classes are in the
229  * <code>com.corp.foo</code> namespace, and you also use some classes in the
230  * <code>org.apache.river</code> and <code>net.jini</code> namespaces, then you
231  * might specify:
232  * <pre>-in com.corp.foo -in org.apache.river -in net.jini</pre>
233  * </dd>
234  * 
235  * <dt><b><code>-out</code> <var>package-prefix</var></b>
236  * <dd>Specifies a namespace of "outside" packages. Any classes in this
237  * package or a subpackage of it are excluded from the dependency graph (and
238  * hence are to be excluded from your JAR file). This option can be
239  * specified zero or more times.  If you specify <code>-in</code> options,
240  * then each <code>-out</code> namespace should be a subspace of some
241  * <code>-in</code> namespace. Note that the argument to <code>-out</code>
242  * is a namespace, so none of its subpackages need to be specified as an
243  * argument to <code>-out</code>.
244  * <p>
245  * If you use this option, you will likely need to use it multiple
246  * times. For example, if you do not specify any <code>-in</code> options,
247  * then all packages are considered inside the graph, including packages
248  * defined in the Java 2 SDK that you typically want to exclude, so you
249  * might exclude them by specifying:
250  * <pre>-out java -out javax</pre>
251  * As another example, if you have specified <code>-in com.corp.foo</code>
252  * but you don't want to include any of the classes in the
253  * <code>com.corp.foo.test</code> or <code>com.corp.foo.qa</code> namespaces
254  * in the dependency graph, then you would specify:
255  * <pre>-out com.corp.foo.test -out com.corp.foo.qa</pre>
256  * </dd>
257  * 
258  * <dt><b><code>-skip</code> <var>class</var></b>
259  * <dd>Specifies the fully qualified name of a specific class to exclude
260  * from the dependency graph. This option allows an individual class to be
261  * considered "outside" without requiring the entire package it is defined
262  * in to be considered outside.  This option can be specified zero or more
263  * times.
264  * </dd> 
265  * 
266  * <dt><b><code>-outer</code></b>
267  * <dd>By default, if a static nested class is included in the dependency
268  * graph, all references from that static nested class to its immediate
269  * lexically enclosing class are ignored (except when the static nested class
270  * extends its outer class), to avoid inadvertent inclusion of the enclosing
271  * class. (The default is chosen this way because the compiled class file of a
272  * static nested class always contains a reference to the immediate lexically
273  * enclosing class.) This option causes all such references to be considered
274  * rather than ignored.  Note that this option is needed very infrequently.</dd>
275  * </dl>
276  * <dl>
277  * <dt><b><code>-newdirbehavior</code></b>
278  * <dd>This option causes the utility to select classes, to serve as root for
279  * the dependency checking, from the directory argument based on the
280  * <code>-inroot</code> and <code>-outroot</code> options specified. When
281  * this option is set subdirectories of the specified directory are no longer
282  * considered as root for finding classes. When this option is not set, the
283  * default, the utility maintains the old behavior with respect to the semantics
284  * for the directories passed in and the <code>-prune</code> option must be
285  * used. You are advised to set this option as there are some edge cases for
286  * which the old behavior won't work as expected, especially when no
287  * <code>-in</code> options are set.</dd>
288  * </dl>
289  *
290  * <a name="output"></a>
291  * <h3>Output Options and Arguments</h3>
292  * 
293  * The following options and arguments determine the content and format of
294  * the output produced by this tool. These options do not affect the
295  * dependency graph computation, only the information displayed in the
296  * output as a result of the computation. Most of these options may be
297  * specified multiple times. Some variations are illustrated in the
298  * <a href="#examples">Examples</a> section of this page.
299  * <dl>
300  * <dt><b><code>-edges</code></b>
301  * <dd>By default, the classes which are included in the dependency graph
302  * are displayed in the output. This option specifies that instead, the
303  * classes which are excluded from the dependency graph, but which are
304  * directly referenced by classes in the dependency graph, should be
305  * displayed in the output. These classes form the outside "edges" of the
306  * dependency graph.
307  * <p>
308  * For example, you might exclude classes from the Java 2 SDK from the
309  * dependency graph because you don't want to include them in your JAR file,
310  * but you might be interested in knowing which classes from the Java 2 SDK
311  * are referenced directly by the classes in your JAR file. The
312  * <code>-edges</code> option can be used to display this information.
313  * </dd>
314  * 
315  * <dt><b><code>-show</code> <var>package-prefix</var></b>
316  * <dd>Displays the classes that are in the specified package or a
317  * subpackage of it. This option can be specified zero or more times. If no
318  * <code>-show</code> options are specified, the default is that all classes
319  * in the dependency graph are displayed (or all edge classes, if
320  * <code>-edges</code> is specified). Note that the argument to
321  * <code>-show</code> is a namespace, so none of its subpackages need to be
322  * specified as an argument to <code>-show</code>.
323  * <p>
324  * For example, to determine which classes from the Java 2 SDK your
325  * application depends on, you might not specify any <code>-in</code>
326  * options, but limit the output by specifying:
327  * <pre>-show java -show javax</pre></dd>
328  * 
329  * <dt><b><code>-hide</code> <var>package-prefix</var></b>
330  * <dd>Specifies a namespace of packages which should not be displayed. Any
331  * classes in this package or a subpackage of it are excluded from the
332  * output. This option can be specified zero or more times. If you specify
333  * <code>-show</code> options, then each <code>-hide</code> namespace should
334  * be a subspace of some <code>-show</code> namespace. Note that the
335  * argument to <code>-hide</code> is a namespace, so none of its subpackages
336  * need to be specified as an argument to <code>-hide</code>.
337  * <p>
338  * For example, to determine which non-core classes from the
339  * <code>net.jini</code> namespace you use, you might specify:
340  * <pre>-show net.jini -hide net.jini.core</pre></dd>
341  * 
342  * <dt><b><code>-files</code></b>
343  * <dd>By default, fully qualified class names are displayed, with package
344  * names delimited by ".". This option causes the output to be in filename
345  * format instead, with package names delimited by filename separators and
346  * with ".class" appended. For example, using this option on Microsoft
347  * Windows platforms would produce output in the form of
348  * <code>com\corp\foo\Bar.class</code> instead of
349  * <code>com.corp.foo.Bar</code>. This option should be used to generate
350  * output suitable as input to the <code>jar</code> tool.
351  * <p>
352  * For more information on the <code>jar</code> tool, see:
353  * <ul>
354  * <li><a href="http://java.sun.com/j2se/1.4/docs/tooldocs/solaris/jar.html">
355  * http://java.sun.com/j2se/1.4/docs/tooldocs/solaris/jar.html</a>;
356  * <li><a href="http://java.sun.com/j2se/1.4/docs/tooldocs/windows/jar.html">
357  * http://java.sun.com/j2se/1.4/docs/tooldocs/windows/jar.html</a>;
358  * <li><a href="http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html">
359  * http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html</a>;
360  * </ul>
361  * </dd>
362  * 
363  * <dt><b><code>-tell</code> <var>class</var></b>
364  * <dd>Specifies the fully qualified name of a class for which dependency
365  * information is desired. This option causes the tool to display
366  * information about every class in the dependency graph that references the
367  * specified class. This information is sent to the error stream of the
368  * tool, not to the normal output stream.  This option can be specified zero
369  * or more times. If this option is used, all other output options are
370  * ignored, and the normal class output is not produced. This option is
371  * useful for debugging.
372  * </dd>
373  * </dl>
374  * 
375  * <a name="examples"></a>
376  * <h3>Examples</h3>
377  *
378  * (The examples in this section assume you ran the JGDMS release installer
379  * with an "Install Set" selection that created the top-level
380  * <code>classes</code> directory. Alternatively, if you have compiled from
381  * the source code, substitute <code>source/classes</code> for
382  * <code>classes</code> in the examples.)
383  * <p>
384  * The following example computes the classes required for a codebase JAR
385  * file, starting with a smart proxy class and a stub class as roots, and
386  * displays them in filename format. (A more complete example would include
387  * additional roots for leases, registrations, events, and lookup service
388  * attributes, and would exclude platform classes such as those in
389  * <code>jsk-platform.jar</code>.)
390  * <p>
391  * <blockquote><pre>
392  * java -jar <var><b>install_dir</b></var>/lib/classdep.jar \
393  *      -cp <var><b>install_dir</b></var>/classes \
394  *      org.apache.river.reggie.RegistrarProxy org.apache.river.reggie.RegistrarImpl_Stub \
395  *      -in org.apache.river -in net.jini \
396  *      -files
397  * </pre></blockquote>
398  * <p>
399  * The following example computes the classes required for a classpath JAR
400  * file, starting with all of the classes in a directory as roots, and
401  * displays them in class name format. (A more complete example would exclude
402  * platform classes such as those in <code>jsk-platform.jar</code>.)
403  * <p>
404  * <blockquote><pre>
405  * java -jar <var><b>install_dir</b></var>/lib/classdep.jar \
406  *      -cp <var><b>install_dir</b></var>/classes \
407  *      <var><b>install_dir</b></var>/classes/org/apache/river/reggie \
408  *      -in org.apache.river -in net.jini
409  * </pre></blockquote>
410  * <p>
411  * The following example computes the <code>org.apache.river</code> classes used
412  * by a service implementation, and displays the <code>net.jini</code>
413  * classes that are immediately referenced by those classes.
414  * <p>
415  * <blockquote><pre>
416  * java -jar <var><b>install_dir</b></var>/lib/classdep.jar \
417  *      -cp <var><b>install_dir</b></var>/classes \
418  *      org.apache.river.reggie.RegistrarImpl \
419  *      -in org.apache.river \
420  *      -edges \
421  *      -show net.jini
422  * </pre></blockquote>
423  * <p>
424  * The following example computes all of the classes used by a service
425  * implementation that are not part of the Java 2 SDK, and displays the
426  * classes that directly reference the class <code>java.awt.Image</code>.
427  * <p>
428  * <blockquote><pre>
429  * java -jar <var><b>install_dir</b></var>/lib/classdep.jar \
430  *      -cp <var><b>install_dir</b></var>/classes \
431  *      org.apache.river.reggie.RegistrarImpl \
432  *      -out java -out javax \
433  *      -tell java.awt.Image
434  * </pre></blockquote>
435  * <p>
436  * The following example computes all of the classes to include in
437  * <code>jini-ext.jar</code> and displays them in filename format.  Note
438  * that both <code>-out</code> and <code>-prune</code> options are needed
439  * for the <code>net.jini.core</code> namespace; <code>-out</code> to
440  * exclude classes from the dependency graph, and <code>-prune</code> to
441  * prevent classes from being used as roots.
442  * <p>
443  * <blockquote><pre>
444  * java -jar <var><b>install_dir</b></var>/lib/classdep.jar \
445  *      -cp <var><b>install_dir</b></var>/classes \
446  *      -in net.jini -out net.jini.core -in org.apache.river \
447  *      <var><b>install_dir</b></var>/classes/net/jini -prune net.jini.core \
448  *      -files
449  * </pre></blockquote>
450  *
451  * @author Sun Microsystems, Inc.
452  */
453 public class ClassDep {
454     private ClassDepend cd;
455      
456     /**
457      * If true class names are printed using
458      * the system's File.separator, else the 
459      * fully qualified class name is printed.
460      */
461     private boolean files = false;
462     /**
463      * Set of paths to find class definitions in order to determine 
464      * dependencies.
465      */
466     private String classpath = "";
467     /**
468      * Flag to determine whether there is interest
469      * in dependencies that go outside the set of 
470      * interested classes. If false then outside,
471      * references are ignored, if true they are noted.
472      * i.e, if looking only under <code>net.jini.core.lease</code>
473      * a reference to a class in <code>net.jini</code> is found it
474      * will be noted if the flag is set to true, else
475      * it will be ignored. <p>
476      * <b>Note:</b> these edge case dependencies must be
477      * included in the classpath in order to find their
478      * definitions.
479      */
480     private  boolean edges = false;
481     /**
482      * Static inner classes have a dependency on their outer
483      * parent class. Because the parent class may be really
484      * big and may pull other classes along with it we allow the
485      * choice to ignore the parent or not. If the flag is set to
486      * true we pull in the parent class. If it is false we don't
487      * look at the parent. The default is is to not include the 
488      * parent. <p>
489      * <b>Note:</b> This is an optimization for those who plan
490      * on doing work with the output of this utility. It does
491      * not impact this utility, but the work done on its 
492      * generated output  may have an impact. 
493      * 
494      * This will have to be implemented in ClassDepend note the above
495      * description conflicts with the variable name.
496      */
497     private  boolean ignoreOuter = true;
498     /**
499      * Package set that we have interest to work in.
500      */
501     private  final ArrayList inside  = new ArrayList();
502     /**
503      * Package set to not work with. This is useful if
504      * there is a subpackage that needs to be ignored.
505      */
506     private  final ArrayList outside = new ArrayList();
507     /**
508      * Class set to look at for dependencies. These are
509      * fully qualified names, ie, net.jini.core.lease.Lease.
510      * This is a subset of the values in
511      * <code>inside</code>.
512      */
513     private  final ArrayList classes = new ArrayList();
514     /**
515      * Set of directories to find dependencies in.
516      */
517     private  final ArrayList roots   = new ArrayList();
518     /**
519      * Set of packages to skip over in the processing of dependencies.
520      * This can be used in conjunction with <em>-out</em> option.
521      */
522     private  final ArrayList prunes  = new ArrayList();
523     /**
524      * Indicates whether the root directories specified for finding classes
525      * for dependency checking must be traversed in the 'old' way, or that the
526      * new behavior must be effective.
527      */
528     private boolean newRootDirBehavior;
529     /**
530      * Set of packages to include when classes are found through the specified
531      * root directories.
532      */
533     private  final ArrayList insideRoots  = new ArrayList();
534     /**
535      * Set of packages to exclude when classes are found through the specified
536      * root directories.
537      */
538     private  final ArrayList outsideRoots  = new ArrayList();
539     /**
540      * Set of classes to skip over in the processing of dependencies.
541      */
542     private  final ArrayList skips   = new ArrayList();
543     /**
544      * Given a specific fully qualified classes, what other classes
545      * in the roots list depend on it. This is more for debugging
546      * purposes rather then normal day to day usage.
547      */
548     private  final ArrayList tells   = new ArrayList();
549     /**
550      * Only display found dependencies that fall under the provided
551      * <code>roots</code> subset.
552      */
553     private  final ArrayList shows   = new ArrayList();
554     /**
555      * Suppress display of found dependencies that are under
556      * the provided package prefixes subset.
557      */
558     private  final ArrayList hides   = new ArrayList();
559     /**
560      * Container for found dependency classes.
561      */
562     private final SortedSet results = new TreeSet();
563 
564     /**
565      * Indicates whether a failure has been encountered during deep dependency
566      * checking.
567      */
568     private boolean failed;
569     private Set<String> providers = new TreeSet<String>();
570 
571     /**
572      * No argument constructor. The user must fill in the
573      * appropriate fields prior to asking for the processing
574      * of dependencies.
575      */
576     public ClassDep() {
577     }
578 
579     /**
580      * Constructor that takes command line arguments and fills in the
581      * appropriate fields. See the description of this class
582      * for a list and description of the acceptable arguments.
583      * @param cmdLine 
584      */
585     public ClassDep(String[] cmdLine){
586 	setupOptions(cmdLine);
587     }
588 
589     /**
590      * Take the given argument and add it to the provided container.
591      * We make sure that each inserted package-prefix is unique. For
592      * example if we had the following packages:
593      * <ul>
594      *     <li>a.b
595      *     <li>a.bx
596      *     <li>a.b.c
597      * </ul>
598      * Looking for <code>a.b</code> should not match 
599      * <code>a.bx</code> and <code>a.b</code>, 
600      * just <code>a.b</code>.
601      * 
602      * @param arg  the package-prefix in string form
603      * @param elts container to add elements to
604      *
605      */
606     private static void add(String arg, ArrayList elts) {
607 	if (!arg.endsWith(".")) {
608 	    arg = arg + '.';
609         }
610 	if (".".equals(arg)) {
611 	    arg = null;
612         }
613 	elts.add(arg);
614     }
615 
616     /**
617      * Recursively traverse a given path, finding all the classes that
618      * make up the set to work with. We take into account skips,
619      * prunes, and out sets defined.
620      * <p>
621      * This implementation is here to maintain the old behavior with regard
622      * to how root directories were interpreted.
623      *
624      * @param path path to traverse down from
625      */
626     private void traverse(String path) {
627 	String apath = path;
628 	/*
629  	 * We append File.separator to make sure that the path
630 	 * is unique for the matching that we are going to do
631 	 * next. 
632 	 */
633 	if (!apath.startsWith(File.separator)) {
634 	    apath = File.separator + apath;
635         }
636 	for (int i = 0; i < prunes.size(); i++) {
637 	    /*
638 	     * If we are on a root path that needs to be 
639 	     * pruned leave this current recursive thread.
640 	     */
641 	    if (apath.endsWith((String)prunes.get(i))) {
642 		return;
643 	}
644 	}
645 
646 	/*
647  	 * Get the current list of files at the current directory 
648 	 * we are in. If there are no files then leave this current
649 	 * recursive thread.
650 	 */
651 	String[] files_ = new File(path).list();
652 	if (files_ == null) {
653 	    return;
654         }
655     outer:
656 	/*
657  	 * Now, take the found list of files and iterate over them.
658 	 */
659 	for (int i = 0; i < files_.length; i++) {
660 	    String file = files_[i];
661 	    /*
662 	     * Now see if we have a ".class" file.
663 	     * If we do not then we lets call ourselves again.
664 	     * The assumption here is that we have a directory. If it
665 	     * is a class file we would have already been throw out
666 	     * by the empty directory contents test above.
667 	     */
668 	    if (!file.endsWith(".class")) {
669 		traverse(path + File.separatorChar + file);
670 	    } else {
671 		/*
672 		 * We have a class file, so remove the ".class" from it
673 		 * using the pattern:
674 		 * 
675 		 *     directory_name + File.Separator + filename = ".class"
676 		 *
677 		 * At this point the contents of the skip container follow
678 		 * the pattern of:
679 		 *
680 		 *     "File.Separator+DirectoryPath"
681 		 *
682 		 * with dots converted to File.Separators
683 		 */
684 		file = apath + File.separatorChar +
685 		       file.substring(0, file.length() - 6);
686 		/*
687 		 * See if there are any class files that need to be skipped.
688 		 */
689 		for (int j = 0; j < skips.size(); j++) {
690 		    String skip = (String)skips.get(j);
691 		    int k = file.indexOf(skip);
692 		    if (k < 0) {
693                         continue; 
694                     }//leave this current loop.
695 		    k += skip.length();
696 		    /*
697 		     * If we matched the entire class or if we have
698 		     * a class with an inner class, skip it and go
699 		     * on to the next outer loop.
700 		     */
701 		    if (file.length() == k || file.charAt(k) == '$') {
702 			continue outer;
703 		}
704 		}
705 		/*
706 		 * things to do:
707 		 * prune when outside.
708 		 * handle inside when its empty.
709 		 *
710 		 * Now see if we have classes within our working set "in".
711 		 * If so add them to our working list "classes".
712 		 */
713 		for (int j = 0; j < inside.size(); j++) {
714 		    if (inside.get(j) == null) {
715 			continue;
716                     }
717 		    int k = file.indexOf(File.separatorChar +
718 					 ((String)inside.get(j)).replace(
719 						     '.', File.separatorChar));
720 		    if (k >= 0) {
721 			/*
722 			 * Insert the class and make sure to replace
723 			 * File.separators into dots.
724 			 */
725 			classes.add(file.substring(k + 1).replace(
726 						   File.separatorChar, '.'));
727 		    }
728 		}
729 	    }
730 	}
731     }
732 
733     /**
734      * Recursively traverse a given path, finding all the classes that make up
735      * the set of classes to work with. We take into account inroot and outroot
736      * sets, skips, and the in and out sets defined.
737      *
738      * @param path path to traverse down from
739      * @param rootPath path to the directory that serves as the root for the
740      *        class files found, any path component below the root is part of
741      *        the full qualified name of the class
742      */
743     private void traverse(String path, String rootPath) {
744 	// get the current list of files at the current directory we are in. If
745 	// there are no files then leave this current recursive thread.
746 	String[] files_ = new File(path).list();
747 	if (files_ == null) {
748 	    return;
749         }
750 
751 	// determine the package name in File.Separators notation
752 	String packageName = path.substring(rootPath.length(), path.length())
753 				+ File.separatorChar;
754     outer:
755 	//take the found list of files and iterate over them
756 	for (int i = 0; i < files_.length; i++) {
757 	    String file = files_[i];
758 	    // see if we have a ".class" file. If not then we call ourselves
759 	    // again, assuming it is a directory, if not the call will return.
760 	    if (!file.endsWith(".class")) {
761 		traverse(path + File.separatorChar + file, rootPath);
762 	    } else {
763 		// when we have in roots defined verify whether we are inside
764 		if (!insideRoots.isEmpty()) {
765 		    boolean matched = false;
766 		    for (int j = 0; j < insideRoots.size(); j++) {
767 			if (packageName.startsWith(
768 				(String) insideRoots.get(j))) {
769 			    matched = true;
770 			    break;
771 			}
772 		    }
773 		    if (!matched) {
774 			continue;
775 		    }
776 		}
777 
778 		// when we have out roots and we are at this level outside we
779 		// can break the recursion
780 		if (!outsideRoots.isEmpty()) {
781 		    for (int j = 0; j < outsideRoots.size(); j++) {
782 			if (packageName.startsWith(
783 				(String) outsideRoots.get(j))) {
784 			    return;
785 			}
786 		    }
787 		}
788 
789 		// determine the fully qualified class name, but with dots
790 		// converted to File.Separators and starting with a
791 		// File.Separators as well
792 		String className = packageName
793 		    			+ file.substring(0, file.length() - 6);
794 		// see if there are any class files that need to be skipped, the
795 		// skip classes are in the above notation as well
796 		for (int j = 0; j < skips.size(); j++) {
797 		    String skip = (String) skips.get(j);
798 		    if (!className.startsWith(skip)) {
799 			continue;
800 		    }
801 		    // if we matched the entire class or if we have a class with
802 		    // an inner class, skip it and go on to the next outer loop
803 		    if (className.length() == skip.length()
804 			    || className.charAt(skip.length()) == '$') {
805 			continue outer;
806 		}
807 		}
808 
809 		// we found a class that satisfy all the criteria, convert it
810 		// to the proper notation
811 		classes.add(className.substring(1).replace(File.separatorChar,
812 							   '.'));
813 	    }
814 	}
815     }
816 
817     private static class Compare implements Comparator {
818 	public int compare(Object o1, Object o2) {
819 	    if (o1 == null) {
820 		return o2 == null ? 0 : 1;
821             }
822 	    else {
823 		return o2 == null ? -1 : ((Comparable) o2).compareTo(o1);
824 	}
825     }
826     }
827 
828     /**
829      * Method that takes the user provided switches that
830      * logically define the domain in which to look for
831      * dependencies.
832      * <p>
833      * Whether a failure has occurred during computing the dependent classes
834      * can be found out with a call to {@link #hasFailed()}.
835      *
836      * @return array containing the dependent classes found in the format as
837      * specified by the options passed in
838      */
839     public String[] compute() {
840 	failed = false;
841 
842 	if (!newRootDirBehavior) {
843 	    if (!insideRoots.isEmpty()) {
844 		failed = true;
845 		print("classdep.invalidoption", "-newdirbehavior", "-inroot");
846 	    }
847 	    if (!outsideRoots.isEmpty()) {
848 		failed = true;
849 		print("classdep.invalidoption", "-newdirbehavior", "-outroot");
850 	    }
851 	    if (failed) {
852 		return new String[0];
853 	    }
854 	}
855 
856 	/* sort ins and outs, longest first */
857 	Comparator c = new Compare();
858 	Collections.sort(inside, c);
859 	Collections.sort(outside, c);
860 	Collections.sort(shows, c);
861 	Collections.sort(hides, c);
862 
863 	/*
864  	 * Traverse the roots i.e the set of handed directories.
865 	 */
866 	for (int i = 0; i < roots.size(); i++) {
867 	    /*
868 	     * Get the classes that we want do to dependency checking on.
869 	     */
870 	    if (newRootDirBehavior) {
871 		traverse((String)roots.get(i), (String)roots.get(i));
872 	    }
873 	    else {
874 		traverse((String)roots.get(i));
875 	    }
876 	}
877         
878         // Now use ClassDepend to perform dependency computation
879         if (classpath.length() == 0) { classpath = null; }
880         try {
881             cd = ClassDepend.newInstance(classpath, null, true);
882         } catch (IOException e) {
883             e.printStackTrace();
884         }
885         Map classDependencyRelationMap = null;
886         try{
887             classDependencyRelationMap = cd.getDependencyRelationshipMap(classes, true); // get the reference to Collection<Class>
888         } catch (ClassNotFoundException e){
889             e.printStackTrace();
890         } catch (IOException e) {
891             e.printStackTrace();
892         }
893          
894 	if (!providers.isEmpty()){
895 	    List classDependencyRelations = new LinkedList();
896 	    Iterator it = providers.iterator();
897 	    while (it.hasNext()){
898 		classDependencyRelations.add(classDependencyRelationMap.get(it.next()));
899 	    }
900 	    it = classDependencyRelations.iterator();
901 	    while (it.hasNext()){
902 		ClassDependencyRelationship cdrs = (ClassDependencyRelationship) it.next();
903 		if (cdrs == null) continue;
904 		Set dependants = cdrs.getDependants();
905 		Iterator i = dependants.iterator();
906 		while (i.hasNext()){
907 		    results.add(i.next().toString());
908 		}
909 	    }
910 	}
911         if (tells.isEmpty()){
912             // Here's where we should build the parameter list and call the filter
913             //Ready the Parameter Builder for ClassDepend
914             // .ignoreOuterParentClass(ignoreOuter) isn't implemented in ClassDepend yet.
915             CDPBuilder cdpb = new CDPBuilder();
916             ClassDependParameters cdp = cdpb.addOutsidePackagesOrClasses(addClassesRecursively(outside))
917                     .addOutsidePackagesOrClasses(skips)
918                     .addInsidePackages(addClassesRecursively(inside))
919                     .addShowPackages(addClassesRecursively(shows))
920                     .addHidePackages(addClassesRecursively(hides))
921                     .ignoreOuterParentClass(ignoreOuter)                   
922                     .excludePlatformClasses(false)
923                     .edges(edges)
924                     .build();
925             Set result = cd.filterClassDependencyRelationShipMap
926                     (classDependencyRelationMap, cdp);
927             Iterator itr = result.iterator();
928             while (itr.hasNext()) {
929                 results.add(itr.next().toString());
930 	}
931         }else{
932         
933             Iterator iter = tells.iterator();
934             while (iter.hasNext()){
935                 String name = (String) iter.next();
936                 if ( classDependencyRelationMap.containsKey(name)){
937                     ClassDependencyRelationship provider = (ClassDependencyRelationship) classDependencyRelationMap.get(name);
938                     Set dependants = provider.getDependants();
939                     if (!dependants.isEmpty()) {
940                         Iterator it = dependants.iterator();
941                         while (it.hasNext()) {
942                             ClassDependencyRelationship dependant = (ClassDependencyRelationship) it.next();
943                             if (tells.size() > 1) {
944                                 print("classdep.cause", provider, dependant);
945         }
946                             else {
947                                 print("classdep.cause1", dependant);
948                             }                 
949                         }
950                     }
951                 }
952             }
953         }
954         // cannot change the return type or we break the API backward compatibility.
955 	return (String[])results.toArray(new String[results.size()]);
956     }
957 
958     /**
959      * Add all classes in Packages and subpackages recursively by appending **
960      * as per the syntax requirements of the ClassDepend API.
961      */
962     private List addClassesRecursively(List list){
963         for ( int i = 0, l = list.size() ; i < l ; i++){
964             list.set(i , list.get(i) + "**");
965         }
966         return list;
967     }
968     
969     /**
970      * Print out the usage for this utility.
971      */
972     public static void usage() {
973 	print("classdep.usage", null);
974     }
975 
976     /**
977      * Set the classpath to use for finding our class definitions.
978      * @param classpath 
979      */
980     public void setClassPath(String classpath) {
981 	this.classpath = classpath;
982     }
983 
984     /**
985      * Determines how to print out the fully qualified
986      * class names. If <code>true</code> it will use
987      * <code>File.separator</code>, else <code>.</code>'s
988      * will be used. 
989      * If not set the default is <code>false</code>.
990      * @param files 
991      */
992     public void setFiles(boolean files) {
993 	this.files = files;
994     }
995 
996     /**
997      * Add an entry into the set of package prefixes that
998      * are to remain hidden from processing.
999      * @param packagePrefix 
1000      */
1001     public void addHides(String packagePrefix) {
1002 	add(packagePrefix, hides);
1003     }
1004 
1005     /**
1006      * Add an entry into the working set of package prefixes 
1007      * that will make up the working domain space.
1008      * @param packagePrefix 
1009      */
1010     public void addInside(String packagePrefix) {
1011 	add(packagePrefix, inside);
1012     }
1013 
1014     /**
1015      * Determines whether to include package references
1016      * that lie outside the declared set of interest.
1017      * <p>
1018      * If true edges will be processed as well, else
1019      * they will be ignored. If not set the default
1020      * will be <code>false</code>.
1021      * <p>
1022      * <b>Note:</b> These edge classes must included
1023      * in the classpath for this utility.
1024      * @param edges 
1025      */
1026     public void setEdges(boolean edges) {
1027 	this.edges = edges;
1028     }
1029 
1030     /**
1031      * Add an entry into the set of package prefixes
1032      * that will bypassed during dependency checking.
1033      * These entries should be subsets of the contents
1034      * on the inside set.
1035      * @param packagePrefix 
1036      */
1037     public void addOutside(String packagePrefix) {
1038 	add(packagePrefix, outside);
1039     }
1040 
1041     /**
1042      * Add an entry into the set of package prefixes
1043      * that will be skipped as part of the dependency
1044      * generation.
1045      * <p>
1046      * This method has no impact if the new behavior is effective for the
1047      * interpretation of the root directories for finding class files to
1048      * include for dependency checking.
1049      * @param packagePrefix 
1050      */
1051     public void addPrune(String packagePrefix) {
1052 	String arg = packagePrefix;
1053 	if (arg.endsWith(".")) {
1054 	    arg = arg.substring(0, arg.length() - 1);
1055         }
1056 	/*
1057 	 * Convert dots into File.separator for later usage.
1058 	 */
1059 	arg = File.separator + arg.replace('.', File.separatorChar);
1060 	prunes.add(arg);
1061     }
1062 
1063     /**
1064      * Controls whether the behavior for finding class files in the specified
1065      * directories, if any, must be based on the old behavior (the default) or
1066      * the new behavior that solves some of the problems with the old behavior.
1067      * @param newBehavior 
1068      */
1069     public void setRootDirBehavior(boolean newBehavior) {
1070 	newRootDirBehavior = newBehavior;
1071     }
1072 
1073     /**
1074      * Adds an entry into the set of package prefixes for which classes found
1075      * through the specified root directories will be considered for dependency
1076      * generation.
1077      * <p>
1078      * Adding entries without a call to {@link #setRootDirBehavior(boolean)}
1079      * with <code>true</code> as the argument will cause {@link #compute()} to
1080      * fail.
1081      * @param packagePrefix 
1082      */
1083     public void addInsideRoot(String packagePrefix) {
1084 	String arg = packagePrefix;
1085 	if (arg.endsWith(".")) {
1086 	    arg = arg.substring(0, arg.length() - 1);
1087         }
1088 	/*
1089 	 * Convert dots into File.separator for later usage.
1090 	 */
1091 	if (arg.trim().length() == 0) {
1092 	    arg = File.separator;
1093         }
1094 	else {
1095             arg = File.separator + arg.replace('.', File.separatorChar) + File.separator;
1096         }
1097 	insideRoots.add(arg);
1098     }
1099 
1100     /**
1101      * Adds an entry into the set of package prefixes for which classes found
1102      * through the specified root directories, and that are part of the inside
1103      * root namespace, will be skipped as part of the dependency generation.
1104      * <p>
1105      * Adding entries without a call to {@link #setRootDirBehavior(boolean)}
1106      * with <code>true</code> as the argument will cause {@link #compute()} to
1107      * fail.
1108      * @param packagePrefix 
1109      */
1110     public void addOutsideRoot(String packagePrefix) {
1111 	String arg = packagePrefix;
1112 	if (arg.endsWith(".")) {
1113 	    arg = arg.substring(0, arg.length() - 1);
1114         }
1115 	/*
1116 	 * Convert dots into File.separator for later usage.
1117 	 */
1118 	if (arg.trim().length() == 0) {
1119 	    arg = File.separator;
1120         }
1121 	else {
1122             arg = File.separator + arg.replace('.', File.separatorChar) + File.separator;
1123         }
1124 	outsideRoots.add(arg);
1125     }
1126 
1127     /**
1128      * Add an entry into the set of package prefixes
1129      * that we want to display.
1130      * This applies only to the final output, so this
1131      * set should be a subset of the inside set with
1132      * edges, if that was indicated.
1133      * @param packagePrefix 
1134      */
1135     public void addShow(String packagePrefix) {
1136 	add(packagePrefix, shows);
1137     }
1138 
1139     /**
1140      * Add an entry into the set of classes that
1141      * should be skipped during dependency generation.
1142      * @param packagePrefix 
1143      */
1144     public void addSkip(String packagePrefix){
1145 	String arg = packagePrefix;
1146 	if (arg.endsWith(".")) {
1147 	    arg = arg.substring(0, arg.length() - 1);
1148         }
1149         /* No Longer required as ClassDepend will be passed the skips array
1150 	else {
1151 	    seen.add(Identifier.lookup(arg));
1152         }
1153          */
1154         
1155 	/*
1156 	 * Convert dots into File.separator for later usage.
1157 	 */
1158 	arg = File.separator + arg.replace('.', File.separatorChar);
1159 	skips.add(arg);
1160     }
1161 
1162     /**
1163      * Add an entry in to the set of classes whose dependents
1164      * that lie with the inside set are listed. This in
1165      * the converse of the rest of the utility and is meant
1166      * more for debugging purposes.
1167      * @param className 
1168      */
1169     public void addTells(String className) {
1170 	tells.add(className);
1171     }
1172 
1173     /**
1174      * Add an entry into the set of directories to
1175      * look under for the classes that fall within
1176      * the working domain space as defined by the
1177      * intersection of the following sets:
1178      * inside,outside,prune,show, and hide.
1179      * @param rootName 
1180      */
1181     public void addRoots(String rootName) {
1182 	if (rootName.endsWith(File.separator)) {
1183 	    //remove trailing File.separator
1184 	    rootName = rootName.substring(0, rootName.length() - 1);
1185         }
1186 	//these are directories.
1187 	roots.add(rootName);
1188     }
1189 
1190     /**
1191      * Add an entry into the set of classes that
1192      * dependencies are going to be computed on.
1193      * @param className 
1194      */
1195     public void addClasses(String className) {
1196 	classes.add(className);
1197     }
1198 
1199    /**
1200      * If true classnames will be separated using
1201      * File.separator, else it will use dots.
1202     * @return true or false
1203      */
1204     public boolean getFiles() {
1205 	return files;
1206     }
1207 
1208     /**
1209      * Accessor method for the found dependencies.
1210      * @return String[] dependencies
1211      */
1212     public String[] getResults() {
1213 	String[] vals = (String[])results.toArray(new String[results.size()]);
1214 	Arrays.sort(vals);
1215 	return vals;
1216     }
1217 
1218     /**
1219      * Indicates whether computing the dependent classes as result of the last
1220      * call to {@link #compute()} resulted in one or more failures.
1221      *
1222      * @return <code>true</code> in case a failure has happened, such as a
1223      * class not being found, <code>false</code> otherwise
1224      */
1225     public boolean hasFailed() {
1226 	return failed;
1227     }
1228 
1229     /**
1230      * Convenience method for initializing an instance with specific
1231      * command line arguments. See the description of this class
1232      * for a list and description of the acceptable arguments.
1233      * @param args 
1234      */
1235     public void setupOptions(String[] args) {
1236 	for (int i = 0; i < args.length ; i++ ) {
1237 	    String arg = args[i];
1238 	    if (arg.equals("-newdirbehavior")) {
1239 		newRootDirBehavior = true;
1240 	    }
1241 	    else if (arg.equals("-cp")) {
1242 		i++;
1243 		setClassPath(args[i]);
1244 	    } else if (arg.equals("-files")) {
1245 		setFiles(true);
1246 	    } else if (arg.equals("-hide")) {
1247 		i++;
1248 		addHides(args[i]);
1249 	    } else if (arg.equals("-in")) {
1250 		i++;
1251 		addInside(args[i]);
1252 	    } else if (arg.equals("-edges")) {
1253 		setEdges(true);
1254 	    } else if (arg.equals("-out")) {
1255 		i++;
1256 		addOutside(args[i]);
1257 	    } else if (arg.equals("-outer")) {
1258 		ignoreOuter = false;
1259 	    } else if (arg.equals("-prune")) {
1260 		i++;
1261 		addPrune(args[i]);
1262 	    } else if (arg.equals("-inroot")) {
1263 		i++;
1264 		addInsideRoot(args[i]);
1265 	    } else if (arg.equals("-outroot")) {
1266 		i++;
1267 		addOutsideRoot(args[i]);
1268 	    } else if (arg.equals("-show")) {
1269 		i++;
1270 		addShow(args[i]);
1271 	    } else if (arg.equals("-skip")) {
1272 		i++;
1273 		addSkip(args[i]);
1274 	    } else if (arg.equals("-tell")) {
1275 		i++;
1276 		addTells(args[i]);
1277 	    } else if (arg.indexOf(File.separator) >= 0) {
1278 		addRoots(arg);
1279 	    } else if ("-prov".equals(args[i])){
1280 		i++;
1281 		providers.add(args[i]);
1282 	    } else if (arg.startsWith("-")) {
1283 		usage();
1284 	    } else {
1285 		addClasses(arg);
1286 	    }
1287 	}
1288     }
1289 
1290     private static ResourceBundle resources;
1291     private static boolean resinit = false;
1292 
1293     /**
1294      * Get the strings from our resource localization bundle.
1295      */
1296     private static synchronized String getString(String key) {
1297 	if (!resinit) {
1298 	    resinit = true;
1299 	    try {
1300 		resources = ResourceBundle.getBundle
1301 		    ("org.apache.river.tool.resources.classdep");
1302 	    } catch (MissingResourceException e) {
1303 		e.printStackTrace();
1304 	    }
1305 	}
1306 	if (resources != null) {
1307 	    try {
1308 		return resources.getString(key);
1309 	    } catch (MissingResourceException e) {
1310 	    }
1311 	}
1312 	return null;
1313     }
1314 
1315     /**
1316      * Print out string according to resourceBundle format.
1317      */
1318     private static void print(String key, Object val) {
1319 	String fmt = getString(key);
1320 	if (fmt == null) {
1321 	    fmt = "no text found: \"" + key + "\" {0}";
1322         }
1323 	System.err.println(MessageFormat.format(fmt, new Object[]{val}));
1324     }
1325 
1326     /**
1327      * Print out string according to resourceBundle format.
1328      */
1329     private static void print(String key, Object val1, Object val2) {
1330 	String fmt = getString(key);
1331 	if (fmt == null) {
1332 	    fmt = "no text found: \"" + key + "\" {0} {1}";
1333         }
1334 	System.err.println(MessageFormat.format(fmt,
1335 						new Object[]{val1, val2}));
1336     }
1337 
1338     /**
1339      * Command line interface for generating the list of classes that
1340      * a set of classes depends upon. See the description of this class
1341      * for a list and description of the acceptable arguments.
1342      */
1343     public static void main(String[] args) {
1344         try {
1345             if (args.length == 0) {
1346                 usage();
1347                 return;
1348             }
1349             ClassDep dep = new ClassDep();
1350             //boolean files = false;
1351             dep.setupOptions(args);
1352             String[] vals = dep.compute();
1353             int l = vals.length;
1354             for (int i = 0; i < l; i++) {
1355                 if (dep.getFiles()) {
1356                     System.out.println(vals[i].replace('.', File.separatorChar) + ".class");
1357                 } else {
1358                     System.out.println(vals[i]);
1359                 }
1360             }
1361         } finally {
1362             System.out.flush();
1363             System.out.close();
1364         }
1365     }
1366 }