• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package jdiff;
2 
3 import com.sun.javadoc.*;
4 
5 import java.util.*;
6 import java.io.*;
7 import java.lang.reflect.*; // Used for invoking Javadoc indirectly
8 import java.lang.Runtime;
9 import java.lang.Process;
10 
11 /**
12  * Generates HTML describing the changes between two sets of Java source code.
13  *
14  * See the file LICENSE.txt for copyright details.
15  * @author Matthew Doar, mdoar@pobox.com.
16  */
17 public class JDiff extends Doclet {
18 
languageVersion()19     public static LanguageVersion languageVersion(){
20       return LanguageVersion.JAVA_1_5;
21     }
22     /**
23      * Doclet-mandated start method. Everything begins here.
24      *
25      * @param root  a RootDoc object passed by Javadoc
26      * @return true if document generation succeeds
27      */
start(RootDoc root)28     public static boolean start(RootDoc root) {
29         if (root != null)
30             System.out.println("JDiff: doclet started ...");
31         JDiff jd = new JDiff();
32         return jd.startGeneration(root);
33     }
34 
35     /**
36      * Generate the summary of the APIs.
37      *
38      * @param root  the RootDoc object passed by Javadoc
39      * @return true if no problems encountered within JDiff
40      */
startGeneration(RootDoc newRoot)41     protected boolean startGeneration(RootDoc newRoot) {
42         long startTime = System.currentTimeMillis();
43 
44         // Open the file where the XML representing the API will be stored.
45         // and generate the XML for the API into it.
46         if (writeXML) {
47             RootDocToXML.writeXML(newRoot);
48         }
49 
50         if (compareAPIs) {
51 	    String tempOldFileName = oldFileName;
52 	    if (oldDirectory != null) {
53 		tempOldFileName = oldDirectory;
54 		if (!tempOldFileName.endsWith(JDiff.DIR_SEP)) {
55 		    tempOldFileName += JDiff.DIR_SEP;
56 		}
57 		tempOldFileName += oldFileName;
58 	    }
59 
60             // Check the file for the old API exists
61             File f = new File(tempOldFileName);
62             if (!f.exists()) {
63                 System.out.println("Error: file '" + tempOldFileName + "' does not exist for the old API");
64                 return false;
65             }
66             // Check the file for the new API exists
67 
68 	    String tempNewFileName = newFileName;
69             if (newDirectory != null) {
70 		tempNewFileName = newDirectory;
71 		if (!tempNewFileName.endsWith(JDiff.DIR_SEP)) {
72 		    tempNewFileName += JDiff.DIR_SEP;
73 		}
74 		tempNewFileName += newFileName;
75             }
76             f = new File(tempNewFileName);
77             if (!f.exists()) {
78                 System.out.println("Error: file '" + tempNewFileName + "' does not exist for the new API");
79                 return false;
80             }
81 
82             // Read the file where the XML representing the old API is stored
83             // and create an API object for it.
84             System.out.print("JDiff: reading the old API in from file '" + tempOldFileName + "'...");
85             // Read the file in, but do not add any text to the global comments
86             API oldAPI = XMLToAPI.readFile(tempOldFileName, false, oldFileName);
87 
88             // Read the file where the XML representing the new API is stored
89             // and create an API object for it.
90             System.out.print("JDiff: reading the new API in from file '" + tempNewFileName + "'...");
91             // Read the file in, and do add any text to the global comments
92             API newAPI = XMLToAPI.readFile(tempNewFileName, true, newFileName);
93 
94             // Compare the old and new APIs.
95             APIComparator comp = new APIComparator();
96 
97             comp.compareAPIs(oldAPI, newAPI);
98 
99             // Read the file where the XML for comments about the changes between
100             // the old API and new API is stored and create a Comments object for
101             // it. The Comments object may be null if no file exists.
102             int suffix = oldFileName.lastIndexOf('.');
103             String commentsFileName = "user_comments_for_" + oldFileName.substring(0, suffix);
104             suffix = newFileName.lastIndexOf('.');
105             commentsFileName += "_to_" + newFileName.substring(0, suffix) + ".xml";
106             commentsFileName = commentsFileName.replace(' ', '_');
107                 if (HTMLReportGenerator.commentsDir !=null) {
108                   commentsFileName = HTMLReportGenerator.commentsDir + DIR_SEP + commentsFileName;
109                 } else if (HTMLReportGenerator.outputDir != null) {
110                   commentsFileName = HTMLReportGenerator.outputDir + DIR_SEP + commentsFileName;
111                 }
112             System.out.println("JDiff: reading the comments in from file '" + commentsFileName + "'...");
113             Comments existingComments = Comments.readFile(commentsFileName);
114             if (existingComments == null)
115                 System.out.println(" (the comments file will be created)");
116 
117             // Generate an HTML report which summarises all the API differences.
118             HTMLReportGenerator reporter = new HTMLReportGenerator();
119             reporter.generate(comp, existingComments);
120 
121             // Emit messages about which comments are now unused and
122             // which are new.
123             Comments newComments = reporter.getNewComments();
124             Comments.noteDifferences(existingComments, newComments);
125 
126             // Write the new comments out to the same file, with unused comments
127             // now commented out.
128             System.out.println("JDiff: writing the comments out to file '" + commentsFileName + "'...");
129             Comments.writeFile(commentsFileName, newComments);
130         }
131 
132         System.out.print("JDiff: finished (took " + (System.currentTimeMillis() - startTime)/1000 + "s");
133         if (writeXML)
134             System.out.println(", not including scanning the source files).");
135         else if (compareAPIs)
136             System.out.println(").");
137        return true;
138     }
139 
140 //
141 // Option processing
142 //
143 
144     /**
145      * This method is called by Javadoc to
146      * parse the options it does not recognize. It then calls
147      * {@link #validOptions} to validate them.
148      *
149      * @param option  a String containing an option
150      * @return an int telling how many components that option has
151      */
optionLength(String option)152     public static int optionLength(String option) {
153         return Options.optionLength(option);
154     }
155 
156     /**
157      * After parsing the available options using {@link #optionLength},
158      * Javadoc invokes this method with an array of options-arrays.
159      *
160      * @param options   an array of String arrays, one per option
161      * @param reporter  a DocErrorReporter for generating error messages
162      * @return true if no errors were found, and all options are
163      *         valid
164      */
validOptions(String[][] options, DocErrorReporter reporter)165     public static boolean validOptions(String[][] options,
166                                        DocErrorReporter reporter) {
167         return Options.validOptions(options, reporter);
168     }
169 
170     /**
171      * This method is only called when running JDiff as a standalone
172      * application, and uses ANT to execute the build configuration in the
173      * XML configuration file passed in.
174      */
main(String[] args)175     public static void main(String[] args) {
176         if (args.length == 0) {
177             //showUsage();
178             System.out.println("Looking for a local 'build.xml' configuration file");
179         } else if (args.length == 1) {
180             if (args[0].compareTo("-help") == 0 ||
181                 args[0].compareTo("-h") == 0 ||
182                 args[0].compareTo("?") == 0) {
183                 showUsage();
184             } else if (args[0].compareTo("-version") == 0) {
185                 System.out.println("JDiff version: " + JDiff.version);
186             }
187             return;
188         }
189         int rc = runAnt(args);
190         return;
191     }
192 
193     /**
194      * Display usage information for JDiff.
195      */
showUsage()196     public static void showUsage() {
197         System.out.println("usage: java jdiff.JDiff [-version] [-buildfile <XML configuration file>]");
198         System.out.println("If no build file is specified, the local build.xml file is used.");
199     }
200 
201     /**
202      * Invoke ANT by reflection.
203      *
204      * @return The integer return code from running ANT.
205      */
runAnt(String[] args)206     public static int runAnt(String[] args) {
207         String className = null;
208         Class c = null;
209         try {
210             className = "org.apache.tools.ant.Main";
211             c = Class.forName(className);
212         } catch (ClassNotFoundException e1) {
213             System.err.println("Error: ant.jar not found on the classpath");
214             return -1;
215         }
216         try {
217             Class[] methodArgTypes = new Class[1];
218             methodArgTypes[0] = args.getClass();
219             Method mainMethod = c.getMethod("main", methodArgTypes);
220             Object[] methodArgs = new Object[1];
221             methodArgs[0] = args;
222             // The object can be null because the method is static
223             Integer res = (Integer)mainMethod.invoke(null, methodArgs);
224             System.gc(); // Clean up after running ANT
225             return res.intValue();
226         } catch (NoSuchMethodException e2) {
227             System.err.println("Error: method \"main\" not found");
228             e2.printStackTrace();
229         } catch (IllegalAccessException e4) {
230             System.err.println("Error: class not permitted to be instantiated");
231             e4.printStackTrace();
232         } catch (InvocationTargetException e5) {
233             System.err.println("Error: method \"main\" could not be invoked");
234             e5.printStackTrace();
235         } catch (Exception e6) {
236             System.err.println("Error: ");
237             e6.printStackTrace();
238         }
239         System.gc(); // Clean up after running ANT
240         return -1;
241     }
242 
243     /**
244      * The name of the file where the XML representing the old API is
245      * stored.
246      */
247     static String oldFileName = "old_java.xml";
248 
249     /**
250      * The name of the directory where the XML representing the old API is
251      * stored.
252      */
253     static String oldDirectory = null;
254 
255     /**
256      * The name of the file where the XML representing the new API is
257      * stored.
258      */
259     static String newFileName = "new_java.xml";
260 
261     /**
262      * The name of the directory where the XML representing the new API is
263      * stored.
264      */
265     static String newDirectory = null;
266 
267     /** If set, then generate the XML for an API and exit. */
268     static boolean writeXML = false;
269 
270     /** If set, then read in two XML files and compare their APIs. */
271     static boolean compareAPIs = false;
272 
273     /**
274      * The file separator for the local filesystem, forward or backward slash.
275      */
276     static String DIR_SEP = System.getProperty("file.separator");
277 
278     /** Details for where to find JDiff. */
279     static final String jDiffLocation = "https://www.jdiff.org";
280     /** Contact email address for the primary JDiff maintainer. */
281     static final String authorEmail = "mdoar@pobox.com";
282 
283     /** A description for HTML META tags. */
284     static final String jDiffDescription = "JDiff is a Javadoc doclet which generates an HTML report of all the packages, classes, constructors, methods, and fields which have been removed, added or changed in any way, including their documentation, when two APIs are compared.";
285     /** Keywords for HTML META tags. */
286     static final String jDiffKeywords = "diff, jdiff, javadiff, java diff, java difference, API difference, difference between two APIs, API diff, Javadoc, doclet";
287 
288     /** The current JDiff version. */
289     static final String version = "1.1.0";
290 
291     /** The current virtual machine version. */
292     static String javaVersion = System.getProperty("java.version");
293 
294     /** Set to enable increased logging verbosity for debugging. */
295     private static boolean trace = false;
296 
297 } //JDiff
298