1 package org.apache.maven.javadoc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
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
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
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
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 }
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
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 }