View Javadoc

1   package org.apache.maven.javadoc;
2   
3   /* ====================================================================
4    *   Licensed to the Apache Software Foundation (ASF) under one or more
5    *   contributor license agreements.  See the NOTICE file distributed with
6    *   this work for additional information regarding copyright ownership.
7    *   The ASF licenses this file to You under the Apache License, Version 2.0
8    *   (the "License"); you may not use this file except in compliance with
9    *   the License.  You may obtain a copy of the License at
10   *
11   *       http://www.apache.org/licenses/LICENSE-2.0
12   *
13   *   Unless required by applicable law or agreed to in writing, software
14   *   distributed under the License is distributed on an "AS IS" BASIS,
15   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *   See the License for the specific language governing permissions and
17   *   limitations under the License.
18   * ====================================================================
19   */
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.FileOutputStream;
25  import java.io.FileReader;
26  import java.io.IOException;
27  import java.io.OutputStreamWriter;
28  import java.io.PrintWriter;
29  import java.io.UnsupportedEncodingException;
30  import java.util.ArrayList;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  import java.util.TreeMap;
36  import java.util.TreeSet;
37  
38  import org.apache.commons.collections.SetUtils;
39  import org.apache.commons.lang.StringEscapeUtils;
40  import org.apache.commons.lang.StringUtils;
41  
42  /**
43   * Converts the javadoc warnings into an xml (xdoc format) file.
44   *
45   * @author Steven Caswell (stevencaswell at apache.org)
46   * @version $Id: JavadocWarningsTextToXml.java 531612 2007-04-23 21:28:38Z ltheussl $
47   */
48  public class JavadocWarningsTextToXml
49  {
50      /** Number of args on the command line.*/
51      private static final int ARGS_LENGTH = 3;
52  
53      /** XML padding. */
54      private static final int PADDING = 10;
55  
56      /** Full path for the input file (javadoc plain text report). */
57      private String inputFileName;
58  
59      /** XML encoding. */
60      private String outputEncoding;
61  
62      /** Full path for the generated XDoc file. */
63      private String outputFileName;
64  
65      /** Verbose flag. */
66      private boolean verbose = false;
67  
68      /**
69       * Invokes an instance of <code>JavadocWarningsTextToXml</code> from the
70       * command line.  The following command line arguments are expected in this
71       * order:
72       *
73       * <ol>
74       * <li>
75       * input file name
76       * </li>
77       * <li>
78       * output file name
79       * </li>
80       * <li>
81       * output encoding
82       * </li>
83       * </ol>
84       *
85       *
86       * @param args the command line arguments
87       *
88       * @throws IOException if an exception occurs opening, reading, or writing
89       *         files
90       */
91      public static void main( final String[] args )
92          throws IOException
93      {
94          if ( args.length != ARGS_LENGTH )
95          {
96              throw new IllegalArgumentException( "Wrong number of arguments" );
97          }
98  
99          JavadocWarningsTextToXml runner = new JavadocWarningsTextToXml();
100         int i = 0;
101 
102         runner.setInputFileName( args[i++] );
103         runner.setOutputFileName( args[i++] );
104         runner.setOutputEncoding( args[i++] );
105         runner.build();
106     }
107 
108     /**
109      * Sets the name of the input file. This file is expected to be a report
110      * generated by Javadoc.
111      *
112      * @param inputFileNameValue the input file name
113      */
114     public void setInputFileName( final String inputFileNameValue )
115     {
116         if ( this.isVerbose() )
117         {
118             System.out.println( "Setting input file name: '" + inputFileNameValue + "'" );
119         }
120 
121         this.inputFileName = inputFileNameValue;
122     }
123 
124     /**
125      * Returns the name of the input file.
126      *
127      * @return the inptu file name
128      */
129     public String getInputFileName()
130     {
131         return this.inputFileName;
132     }
133 
134     /**
135      * Sets the output encoding.
136      *
137      * @param outputEncodingValue the output encoding
138      */
139     public void setOutputEncoding( final String outputEncodingValue )
140     {
141         this.outputEncoding = outputEncodingValue;
142     }
143 
144     /**
145      * Returns the output encoding.
146      *
147      * @return the output encoding
148      */
149     public String getOutputEncoding()
150     {
151         return this.outputEncoding;
152     }
153 
154     /**
155      * Sets the output file name.
156      *
157      * @param outputFileNameValue the output file name
158      */
159     public void setOutputFileName( final String outputFileNameValue )
160     {
161         if ( this.isVerbose() )
162         {
163             System.out.println( "Setting output file name: '" + outputFileNameValue + "'" );
164         }
165 
166         this.outputFileName = outputFileNameValue;
167     }
168 
169     /**
170      * Returns the name of the output file.
171      *
172      * @return the output file name
173      */
174     public String getOutputFileName()
175     {
176         return this.outputFileName;
177     }
178 
179     /**
180      * Sets the verbose mode. <br>
181      *
182      * @param verboseValue the verbose mode.
183      */
184     public void setVerbose( final boolean verboseValue )
185     {
186         this.verbose = verboseValue;
187 
188         if ( this.isVerbose() )
189         {
190             System.out.println( "verbose is true" );
191         }
192     }
193 
194     /**
195      * Builds the xml file from the javadoc report input.
196      *
197      * @throws FileNotFoundException if the input file cannot be opened for
198      *         reading or the output file cannot be opened for writing
199      * @throws IOException if an error occurs reading or writing
200      * @throws UnsupportedEncodingException if an unknown encoding was specified
201      */
202     public void build()
203         throws FileNotFoundException, IOException, UnsupportedEncodingException
204     {
205         if ( StringUtils.isBlank( getInputFileName() ) )
206         {
207             throw new IllegalArgumentException( "Input file name must be specified" );
208         }
209 
210         if ( StringUtils.isBlank( getOutputFileName() ) )
211         {
212             throw new IllegalArgumentException( "Output file name must be specified" );
213         }
214 
215         String[] lines = this.readInput();
216         Map fileMap = this.buildMap( lines );
217 
218         this.buildOutput( fileMap );
219     }
220 
221     /**
222      * Returns the verbose.
223      *
224      * @return Returns the verbose.
225      */
226     public boolean isVerbose()
227     {
228         return verbose;
229     }
230 
231     /**
232      * Build a Map from the javadoc report lines.
233      * @param input the lines
234      * @return the Map of files with javadoc errors.
235      */
236     private Map buildMap( final String[] input )
237     {
238         if ( this.isVerbose() )
239         {
240             System.out.println( "Building map from " + input.length + " input line(s)" );
241         }
242 
243         Map files = new TreeMap();
244 
245         for ( int i = 0; i < input.length; i++ )
246         {
247             String line = input[i];
248 
249             if ( this.isVerbose() )
250             {
251                 System.out.println( "Parsing line " + line );
252             }
253 
254             try
255             {
256                 // Break up line into pieces
257                 int javaDocStart = line.indexOf( "[javadoc]" );
258                 int fileNameStart = javaDocStart += PADDING;
259                 int warningStart = line.indexOf( "warning - " );
260                 int fileNameEnd = warningStart - 1;
261                 String fileNameAndLineNumber = line.substring( fileNameStart, fileNameEnd );
262                 int lastColon = fileNameAndLineNumber.lastIndexOf( ':' );
263                 int nextToLastColon = fileNameAndLineNumber.lastIndexOf( ':', lastColon - 1 );
264                 String fileName = fileNameAndLineNumber.substring( 0, nextToLastColon );
265                 String lineNumber = fileNameAndLineNumber.substring( nextToLastColon + 1, lastColon );
266                 int msgStart = warningStart + PADDING;
267                 String msg = line.substring( msgStart );
268 
269                 Integer lineNumberInt;
270                 try
271                 {
272                     lineNumberInt = new Integer( lineNumber );
273                 }
274                 catch ( NumberFormatException nfe )
275                 {
276                     // Warnings from package.html files don't contain line numbers
277                     fileName = fileNameAndLineNumber.substring( 0, lastColon );
278                     System.err.println( "*** WARNING: no line number found in '" + fileName + "', setting to 0." );
279                     lineNumberInt = new Integer( 0 );
280                 }
281 
282                 // Get the messages for the file
283                 Map fileMessages = (Map) files.get( fileName );
284 
285                 if ( fileMessages == null )
286                 {
287                     fileMessages = new TreeMap();
288                     files.put( fileName, fileMessages );
289                 }
290 
291                 // Get the messages for the line
292                 Set lineMessages = (Set) fileMessages.get( lineNumberInt );
293 
294                 if ( lineMessages == null )
295                 {
296                     lineMessages = SetUtils.orderedSet( new TreeSet() );
297 
298                     fileMessages.put( lineNumberInt, lineMessages );
299                 }
300 
301                 // Put the message into the line messages set
302                 lineMessages.add( msg );
303             }
304             catch ( Throwable t )
305             {
306                 System.err.println( "*** WARNING: exception parsing line '" + line + "': " + t.getMessage() );
307 
308                 if ( this.isVerbose() )
309                 {
310                     t.printStackTrace();
311                 }
312             }
313         } // for
314 
315         return files;
316     }
317 
318     /**
319      * Build the output on the disk.
320      * @param fileMap The map containing the results.
321      * @throws FileNotFoundException If the file cannot be created.
322      * @throws UnsupportedEncodingException If the encoding isn't supported.
323      */
324     private void buildOutput( final Map fileMap )
325         throws FileNotFoundException, UnsupportedEncodingException
326     {
327         File output = new File( this.outputFileName );
328         File dir = output.getParentFile();
329 
330         if ( dir != null )
331         {
332             dir.mkdirs();
333         }
334 
335         PrintWriter out = new PrintWriter( new OutputStreamWriter( new FileOutputStream( output ), this
336             .getOutputEncoding() ) );
337 
338         writeOutput( fileMap, out );
339         out.flush();
340         out.close();
341     }
342 
343     /**
344      * Extract warning lines from the javadoc result.
345      * @return a table of lines.
346      * @throws FileNotFoundException If the javadoc result can't be read.
347      * @throws IOException If there's a problem while reading the javadoc result.
348      */
349     private String[] readInput()
350         throws FileNotFoundException, IOException
351     {
352         if ( this.isVerbose() )
353         {
354             System.out.println( "Reading '" + getInputFileName() + "'" );
355         }
356 
357         BufferedReader reader = new BufferedReader( new FileReader( getInputFileName() ) );
358 
359         List lines = new ArrayList();
360         String line = null;
361 
362         while ( ( line = reader.readLine() ) != null )
363         {
364             // Look for lines containing the string "warning - "
365             if ( line.indexOf( "warning - " ) >= 0 )
366             {
367                 lines.add( line );
368             }
369         }
370 
371         reader.close();
372 
373         if ( this.isVerbose() )
374         {
375             System.out.println( "Read " + lines.size() + " line(s) from input file" );
376         }
377 
378         return (String[]) lines.toArray( new String[lines.size()] );
379     }
380 
381     /**
382      * Write the file map on the output
383      * @param fileMap The filemap to translate in XML.
384      * @param out The output to write.
385      */
386     private void writeOutput( final Map fileMap, final PrintWriter out )
387     {
388         if ( this.isVerbose() )
389         {
390             System.out.println( "Writing to output file '" + this.outputFileName );
391         }
392 
393         out.println( "<?xml version=\"1.0\" encoding=\"" + this.getOutputEncoding() + "\"?>" );
394         out.println( "<javadoc>" );
395 
396         for ( Iterator fileMapIterator = fileMap.keySet().iterator(); fileMapIterator.hasNext(); )
397         {
398             String fileName = (String) fileMapIterator.next();
399 
400             out.println( "<file name=\"" + fileName + "\">" );
401 
402             Map fileMessages = (Map) fileMap.get( fileName );
403 
404             for ( Iterator fileMessagesIterator = fileMessages.entrySet().iterator(); fileMessagesIterator.hasNext(); )
405             {
406                 Map.Entry entry = (Map.Entry) fileMessagesIterator.next();
407                 Integer lineNumber = (Integer) entry.getKey();
408                 Set lineMessages = (Set) entry.getValue();
409 
410                 for ( Iterator lineMessagesIterator = lineMessages.iterator(); lineMessagesIterator.hasNext(); )
411                 {
412                     String msg = (String) lineMessagesIterator.next();
413 
414                     out.println( "<error line=\"" + lineNumber + "\" severity=\"warning\" message=\""
415                         + StringEscapeUtils.escapeXml( msg ) + "\"/>" );
416                 }
417             }
418 
419             out.println( "</file>" );
420         }
421 
422         out.println( "</javadoc>" );
423 
424         if ( this.isVerbose() )
425         {
426             System.out.println( "Finished writing output file" );
427         }
428     }
429 }