View Javadoc

1   /*
2    *           Copyright 2004 Matthew Kurjanowicz
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package projassist.readme;
17  
18  import java.io.File;
19  import java.util.Iterator;
20  import java.util.Set;
21  import java.util.Stack;
22  import java.util.TreeSet;
23  import java.util.Vector;
24  import java.io.FileOutputStream;
25  import java.io.FileInputStream;
26  
27  /***
28   * This is the Bean that is used in the Readme plugin.
29   *
30   * @author $Author: mkurjano $
31   * @version $Revision: 1.3 $, $Date: 2004/07/22 05:14:35 $
32   */
33  public final class ReadmePlugin {
34  
35      /***
36       * Whether or not to debug.
37       * @todo Make this a bean property.
38       */
39      private static final boolean DEBUG = true;
40  
41      /***
42       * The size of the buffer that is used when reading and writing files.
43       */
44      private static final int BUFFER_SIZE = 2048;
45  
46      /***
47       * This is the base path that the plugin starts looking for files.
48       * This directory, and all of its subdirectories will be
49       * recursively searched for files.
50       */
51      private String rootPath = null;
52  
53      /***
54       * This is the name of the file that we are looking for.  By
55       * default it is set to <code>readme.xml</code>.
56       */
57      private String seekFileName = null;
58  
59      /***
60       * This is the directory to which we will write the output.
61       */
62      private String outputDirectory = null;
63  
64      /***
65       * This is the name of the file to which we will write the
66       * overview of our findings.  This defaults to
67       * <code>readme-report.xml</code>.
68       */
69      private String outputReportFileName = null;
70  
71      /***
72       * The number of files that this plugin processed.
73       */
74      private int processedCount = 0;
75  
76      /***
77       * The baseDir of the current project.  So we can exclude basedir/target.
78       */
79      private String baseDir = null;
80  
81      private String reportHeader = ""
82          + "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \n"
83          + "<document> \n"
84          + "  <properties> \n"
85          + "    <author email=\"nobody@nobody.com\">Some Author</author> \n"
86          + "    <title>Readme Files</title> \n"
87          + "  </properties> \n"
88          + "  <meta name=\"keyword\" content=\"readme,readme files\" /> \n"
89          + "  <body> \n"
90          + "    <section name=\"Readme Files\"> \n"
91          + "      <p> \n";
92  
93      private String reportFooter = ""
94          + "      </p> \n"
95          + "    </section> \n"
96          + "  </body> \n"
97          + "</document> \n";
98  
99      /***
100      * Default constructor, sets all values to their defaults.
101      *
102      * @see #ReadmePlugin(String, String, String, String, String)
103      */
104     public ReadmePlugin() {
105         this(null, null, null, null, null);
106     }
107 
108     /***
109      * Constructor that sets all fields to given values.
110      *
111      * @param theRootPath The root directory that we will begin
112      * searching in.
113      * @param theSeekFileName The name of the file that we are looking
114      * for.
115      * @param theOutputDirectory The name of the directory that we
116      * will write all output to.
117      * @param theOutputReportFileName The name of the file that we
118      * will write the overview page of our findings.
119      * @param theBaseDir The base directory of the project we're
120      * working on.
121      *
122      * @see #setRootPath(String)
123      * @see #setSeekFileName(String)
124      * @see #setOutputDirectory(String)
125      * @see #setOutputReportFileName(String)
126      * @see #setBaseDir(String)
127      */
128     public ReadmePlugin(final String theRootPath,
129                         final String theSeekFileName,
130                         final String theOutputDirectory,
131                         final String theOutputReportFileName,
132                         final String theBaseDir) {
133         setRootPath(theRootPath);
134         setSeekFileName(theSeekFileName);
135         setOutputDirectory(theOutputDirectory);
136         setOutputReportFileName(theOutputReportFileName);
137         setBaseDir(theBaseDir);
138     }
139 
140     /***
141      * Get the root search path.
142      *
143      * @return The root search path.
144      */
145     public String getRootPath() {
146         return this.rootPath;
147     }
148 
149     /***
150      * Set the root search path.  If <code>theRootPath</code> is
151      * <code>null</code>, then this value is set to <code>.</code>.
152      *
153      * @param theRootPath The root search path.
154      *
155      * @todo See if we can try to get the default from a system property.
156      */
157     public void setRootPath(final String theRootPath) {
158         if (null == theRootPath) {
159             this.rootPath = ".";
160         } else {
161             this.rootPath = theRootPath;
162         }
163     }
164 
165     /***
166      * Get the filename to look for.
167      *
168      * @return The filename to look for.
169      */
170     public String getSeekFileName() {
171         return this.seekFileName;
172     }
173 
174     /***
175      * Set the filename to look for.  If <code>theSeekFileName</code>
176      * is <code>null</code>, then this value is set to
177      * <code>readme.xml</code>.
178      *
179      * @param theSeekFileName The file name to look for.
180      *
181      * @todo See if we can get the default from a system property.
182      */
183     public void setSeekFileName(final String theSeekFileName) {
184         if (null == theSeekFileName) {
185             this.seekFileName = "readme.xml";
186         } else {
187             this.seekFileName = theSeekFileName;
188         }
189     }
190 
191     /***
192      * Get the base output directory.
193      *
194      * @return The base output directory.
195      */
196     public String getOutputDirectory() {
197         return this.outputDirectory;
198     }
199 
200     /***
201      * Set the base output directory.  If
202      * <code>theOutputDirectory</code> is <code>null</code>, then
203      * this value is set to <code>/tmp</code>.
204      *
205      * @param theOutputDirectory The base output directory.
206      *
207      * @todo See if we can get the default from a system property.
208      */
209     public void setOutputDirectory(final String theOutputDirectory) {
210         if (null == theOutputDirectory) {
211             this.outputDirectory = "/tmp";
212         } else {
213             this.outputDirectory = theOutputDirectory;
214         }
215     }
216 
217     /***
218      * Get the output report file name.
219      *
220      * @return The output report file name.
221      */
222     public String getOutputReportFileName() {
223         return this.outputReportFileName;
224     }
225 
226     /***
227      * Set the output report file name.  If
228      * <code>theOutputReportFileName</code> is <code>null</code>, the
229      * this value is set to <code>readme-report.xml</code>.
230      *
231      * @param theOutputReportFileName The output report file name.
232      *
233      * @todo See if we can get the default from a system property.
234      */
235     public void setOutputReportFileName(final String theOutputReportFileName) {
236         if (null == theOutputReportFileName) {
237             this.outputReportFileName = "readme-report.xml";
238         } else {
239             this.outputReportFileName = theOutputReportFileName;
240         }
241     }
242 
243     /***
244      * Get this project's base directory.
245      *
246      * @return This project's base directory.
247      */
248     public String getBaseDir() {
249         return this.baseDir;
250     }
251 
252     /***
253      * Set the base dir of the project we're working in.  If
254      * <code>theBaseDir</code> is <code>null</code> then this value is
255      * set to <code>/tmp</code>.
256      *
257      * @param theBaseDir The base directory of the project we're
258      * working on.
259      *
260      * @todo See if wew can get the default from a system property.
261      */
262     public void setBaseDir(final String theBaseDir) {
263         if (null == theBaseDir) {
264             this.baseDir = "/tmp";
265         } else {
266             this.baseDir = theBaseDir;
267         }
268     }
269 
270     /***
271      * Get the number of files that were processed in this report.
272      *
273      * @return The number of files that were processed in this report.
274      */
275     public int getProcessedCount() {
276         return this.processedCount;
277     }
278 
279     /***
280      * Execute this plugin.
281      *
282      * @throws Exception If something went wrong.
283      */
284     public void generateReport() throws Exception {
285         try {
286             Set files = populateFileList();
287             Set processedFileNames = new TreeSet();
288             int i = 0;
289             File outputDirectoryFile = new File(outputDirectory);
290             /* Make any needed files. */
291             outputDirectoryFile.mkdirs();
292 
293             if (DEBUG) {
294                 System.out.println("Files that we are going to use:");
295             }
296 
297             for (Iterator it = files.iterator(); it.hasNext(); i++) {
298                 File next = (File) it.next();
299                 String newFileName = getNewFileName(next);
300                 File newFile = new File(outputDirectoryFile, newFileName);
301 
302                 if (DEBUG) {
303                     System.out.println("File [" + i + "]: " + next);
304                     System.out.println("File [" + i + "]: " + newFileName);
305                     System.out.println("File [" + i + "]: " + newFile);
306                 }
307 
308                 processedFileNames.add(newFileName);
309                 copyFile(next, newFile);
310             }
311 
312             writeOverview(processedFileNames);
313 
314             this.processedCount = i;
315         } catch (Exception e) {
316             String msg = "Got exception when processing reports.";
317             System.err.println(msg);
318             System.err.println(e.getMessage() + "\n");
319             e.printStackTrace();
320         }
321     }
322 
323     /***
324      * Populate a <code>{@link java.util.Set}</code> with all files
325      * that match the given file that are in or below the root
326      * directory.
327      *
328      * @note Currently this method excludes all subdirectories of
329      * ${basedir}/target/.
330      *
331      * @todo Get the excluded directories from a property - i.e. a set.
332      *
333      * @return A <code>{@link java.util.Set}</code> of all files that
334      * we are processing.
335      *
336      * @throws Exception If something went wrong.
337      */
338     private Set populateFileList() throws Exception {
339         Stack toEvaluate = initializeStack();
340         Set evaluated = new TreeSet();
341         Set files = new TreeSet();
342 
343         while (!(toEvaluate.isEmpty())) {
344             File currentDir = (File) toEvaluate.pop();
345             File[] fileList = currentDir.listFiles();
346 
347             for (int i = 0; i < fileList.length; i++) {
348                 if (DEBUG) {
349                     System.out.println("Processing file: " + fileList[i]);
350                 }
351 
352                 if (fileList[i].isDirectory()) {
353                     toEvaluate.push(fileList[i]);
354                 } else if (fileList[i].getName().equals(seekFileName)) {
355                     files.add(fileList[i]);
356                 }
357             }
358         }
359 
360         return files;
361     }
362 
363     /***
364      * Create a new stack and add the root directory to it.
365      *
366      * @return A <code>{@link java.util.Stack}</code> that contains a
367      * <code>{@link java.io.File}</code> that is the root directory.
368      *
369      * @throws Exception If the given root directory is not a
370      * directory.
371      *
372      * @see #rootPath
373      */
374     private Stack initializeStack() throws Exception {
375         Stack stack = new Stack();
376         File root = new File(rootPath);
377 
378         if (!root.isDirectory()) {
379             String msg = "Root path: "
380                 + rootPath
381                 + " is not actually a directory!";
382             System.err.println("FATAL: " + msg);
383             throw new Exception(msg);
384         }
385 
386         stack.push(root);
387 
388         return stack;
389     }
390 
391     /***
392      * Get a String file name from a project descriptor.
393      *
394      * @param oldFileName The original file.
395      *
396      * @return The absolute path of the original file, with spaces,
397      * forward slashes, back slashes, and the system's path separator
398      * characters are replaced with underscores.
399      */
400     private String getNewFileName(File oldFileName) {
401         String path = oldFileName.getAbsolutePath();
402 
403         /* Make sure that we take care or the path separator. */
404         String newFileName = path.replace(File.pathSeparatorChar, '_');
405 
406         /*
407          * Certain characters that we don't want anywhere in our
408          * file.
409          */
410         newFileName = newFileName.replace(' ', '_');
411         newFileName = newFileName.replace('/', '_');
412         newFileName = newFileName.replace('//', '_');
413 
414         return newFileName;
415     }
416 
417     /***
418      * Copy a file from one location to another.
419      *
420      * @param src The file descriptor of the source file to copy from.
421      * @param dest The File descriptor of the file to copy to.
422      *
423      * @todo Write out header and footer information
424      */
425     private void copyFile(File src, File dest) throws Exception {
426         FileInputStream fis = null;
427         FileOutputStream fos = null;
428         try {
429             fis = new FileInputStream(src);
430             fos = new FileOutputStream(dest);
431             int r = -1;
432             byte[] buf = new byte[BUFFER_SIZE];
433 
434             while ((r = fis.read(buf, 0, BUFFER_SIZE)) > 0) {
435                 fos.write(buf, 0, r);
436             }
437         } catch (Exception ex) {
438             throw ex;
439         } finally {
440             try {
441                 if (null != fis) {
442                     fis.close();
443                 }
444             } catch (Exception e) {
445                 throw e;
446             } finally {
447                 if (null != fos) {
448                     fos.close();
449                 }
450             }
451         }
452     }
453 
454     private void writeOverview(Set files) throws Exception {
455         File outputReport = new File(outputDirectory, outputReportFileName);
456         FileOutputStream fos = null;
457         try {
458             fos = new FileOutputStream(outputReport);
459 
460             /* Header */
461             byte[] toWrite = reportHeader.getBytes();
462             fos.write(toWrite, 0, toWrite.length);
463 
464             /* Files */
465             for (Iterator it = files.iterator(); it.hasNext();) {
466                 String curr = (String) it.next();
467                 curr = curr.replaceAll("//.xml", ".html");
468                 String out = "<a href=\"" + curr + "\">" + curr + "</a><br />";
469 
470                 toWrite = out.getBytes();
471                 fos.write(toWrite, 0, toWrite.length);
472             }
473 
474             /* Footer */
475             toWrite = reportFooter.getBytes();
476             fos.write(toWrite, 0, toWrite.length);
477         } catch (Exception e) {
478             throw e;
479         } finally {
480             if (null != fos) {
481                 fos.close();
482             }
483         }
484     }
485 
486     public static void main(String[] args) throws Exception {
487         ReadmePlugin rp = new ReadmePlugin();
488 
489         rp.setRootPath("/Users/mkurjano/cvswork/readme-plugin/");
490         rp.generateReport();
491     }
492 }