View Javadoc

1   /*
2    * Copyright 2007-2013 smartics, Kronseder & Reiner GmbH
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 de.smartics.maven.exceptions.conf;
17  
18  import java.io.File;
19  import java.lang.reflect.Constructor;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.ResourceBundle;
24  
25  import org.apache.commons.lang.StringUtils;
26  
27  import de.smartics.exceptions.report.data.ProjectConfiguration;
28  import de.smartics.exceptions.report.generator.ReportGenerator;
29  import de.smartics.exceptions.report.utils.JavadocUtils;
30  import de.smartics.maven.exceptions.runtime.ProjectClassLoader;
31  import de.smartics.messages.core.BundleMapper;
32  
33  /**
34   * Default implementation.
35   *
36   * @param <O> the type of the output instance that handles the writing.
37   */
38  public final class DefaultProjectConfiguration<O> implements // NOPMD Builder
39      ProjectConfiguration<O>
40  {
41    // ********************************* Fields *********************************
42  
43    // --- constants ------------------------------------------------------------
44  
45    // --- members --------------------------------------------------------------
46  
47    /**
48     * The name of the project this configuration is for.
49     */
50    private final String projectName;
51  
52    /**
53     * The classpath used by the tool.
54     */
55    private final List<String> toolClassPath;
56  
57    /**
58     * The list of class path root elements of this project.
59     */
60    private final Collection<String> classRootDirectoryNames;
61  
62    /**
63     * The class loader to load project classes.
64     */
65    private final ClassLoader projectClassLoader;
66  
67    /**
68     * The encoding of the source files in this project.
69     */
70    private final String sourceEncoding;
71  
72    /**
73     * The Java version specified for the sources in the project.
74     */
75    private final String sourceVersion;
76  
77    /**
78     * The list of source path root elements of this project.
79     */
80    private final Collection<String> sourceRootDirectoryNames;
81  
82    /**
83     * The includes for the scanner.
84     */
85    private final List<String> includes;
86  
87    /**
88     * The excludes for the scanner.
89     */
90    private final List<String> excludes;
91  
92    /**
93     * The encoding of the generated report.
94     */
95    private final String reportEncoding;
96  
97    /**
98     * The reference to the reporter that generates the report document.
99     */
100   private final ReportGenerator<O> reporterInstance;
101 
102   /**
103    * The class name of the reporter.
104    */
105   private final String reporter;
106 
107   /**
108    * The name of the file the report will be written to.
109    */
110   private final String report;
111 
112   /**
113    * The bundle set for this report execution.
114    */
115   private final ResourceBundle bundle;
116 
117   /**
118    * The directory where the Javadoc pages are found. This information is used
119    * to make relative links from the exceptions report to the Javadocs.
120    */
121   private final File javadocDir;
122 
123   /**
124    * The reference to the style sheet to add as link element to the head of the
125    * XHTML document.
126    */
127   private final String styleSheet;
128 
129   /**
130    * The mapper to the resource bundles of a given item class. The item is to be
131    * documented.
132    */
133   private final BundleMapper bundleMapper;
134 
135   // ****************************** Initializer *******************************
136 
137   // ****************************** Constructors ******************************
138 
139   /**
140    * Default constructor.
141    *
142    * @param builder the information to set to the value object.
143    */
144   private DefaultProjectConfiguration(final Builder<O> builder)
145   {
146     this.projectName = builder.projectName;
147     this.toolClassPath = builder.toolClassPath;
148     this.bundle = builder.bundle;
149     this.sourceEncoding = builder.sourceEncoding;
150     this.sourceVersion = builder.sourceVersion;
151     this.excludes =
152         builder.excludes != null ? Collections
153             .unmodifiableList(builder.excludes) : null;
154     this.includes =
155         builder.includes != null ? Collections
156             .unmodifiableList(builder.includes) : null;
157     this.javadocDir = builder.javadocDir;
158     this.report = builder.report;
159     this.reportEncoding = builder.reportEncoding;
160     this.reporter = builder.reporter;
161     this.reporterInstance = builder.reporterInstance;
162     this.classRootDirectoryNames =
163         Collections.unmodifiableCollection(builder.classRootDirectoryNames);
164     this.projectClassLoader = builder.projectClassLoader;
165     this.sourceRootDirectoryNames =
166         Collections.unmodifiableCollection(builder.sourceRootDirectoryNames);
167     this.styleSheet = builder.styleSheet;
168     this.bundleMapper = builder.bundleMapper;
169   }
170 
171   // ****************************** Inner Classes *****************************
172 
173   /**
174    * The configuration builder.
175    */
176   public static final class Builder<O>
177   {
178     // ******************************** Fields ********************************
179 
180     // --- constants ----------------------------------------------------------
181 
182     // --- members ------------------------------------------------------------
183 
184     /**
185      * The name of the project this configuration is for.
186      */
187     private final String projectName;
188 
189     /**
190      * The classpath used by the tool.
191      */
192     private List<String> toolClassPath;
193 
194     /**
195      * The list of class path root elements of this project.
196      */
197     private Collection<String> classRootDirectoryNames;
198 
199     /**
200      * The class loader to load project classes.
201      */
202     private ClassLoader projectClassLoader;
203 
204     /**
205      * The encoding of the source files in this project.
206      */
207     private String sourceEncoding = "UTF-8";
208 
209     /**
210      * The Java version specified for the sources in the project.
211      */
212     private String sourceVersion;
213 
214     /**
215      * The list of source path root elements of this project.
216      */
217     private Collection<String> sourceRootDirectoryNames;
218 
219     /**
220      * The includes for the scanner.
221      */
222     private List<String> includes;
223 
224     /**
225      * The excludes for the scanner.
226      */
227     private List<String> excludes;
228 
229     /**
230      * The encoding of the generated report.
231      */
232     private String reportEncoding = "UTF-8";
233 
234     /**
235      * The reference to the reporter that generates the report document.
236      */
237     private ReportGenerator<O> reporterInstance;
238 
239     /**
240      * The class name of the reporter.
241      */
242     private String reporter;
243 
244     /**
245      * The name of the file the report will be written to.
246      */
247     private String report;
248 
249     /**
250      * The bundle set for this report execution.
251      */
252     private ResourceBundle bundle;
253 
254     /**
255      * The directory where the Javadoc pages are found. This information is used
256      * to make relative links from the exceptions report to the Javadocs.
257      */
258     private File javadocDir;
259 
260     /**
261      * The reference to the style sheet to add as link element to the head of
262      * the XHTML document.
263      */
264     private String styleSheet;
265 
266     /**
267      * The class of the mapper to the resource bundles of a given item class.
268      * The item is to be documented.
269      */
270     private String bundleMapperClassName;
271 
272     /**
273      * The mapper to the resource bundles of a given item class. The item is to
274      * be documented.
275      */
276     private BundleMapper bundleMapper;
277 
278     // ***************************** Initializer ******************************
279 
280     // ***************************** Constructors *****************************
281 
282     /**
283      * Default constructor.
284      *
285      * @param projectName the name of the project this configuration is for.
286      */
287     public Builder(final String projectName)
288     {
289       this.projectName = projectName;
290     }
291 
292     // ***************************** Inner Classes ****************************
293 
294     // ******************************** Methods *******************************
295 
296     // --- init ---------------------------------------------------------------
297 
298     // --- get&set ------------------------------------------------------------
299 
300     /**
301      * Sets the classpath used by the tool.
302      *
303      * @param toolClassPath the classpath used by the tool.
304      */
305     public void setToolClassPath(final List<String> toolClassPath)
306     {
307       this.toolClassPath = toolClassPath;
308     }
309 
310     /**
311      * Sets the list of class path root elements of this project.
312      *
313      * @param classRootDirectoryNames the list of class path root elements of
314      *          this project.
315      */
316     public void setClassRootDirectoryNames(
317         final Collection<String> classRootDirectoryNames)
318     {
319       this.classRootDirectoryNames = classRootDirectoryNames;
320     }
321 
322     /**
323      * Sets the list of source path root elements of this project.
324      *
325      * @param sourceRootDirectoryNames the list of source path root elements of
326      *          this project.
327      */
328     public void setSourceRootDirectoryNames(
329         final Collection<String> sourceRootDirectoryNames)
330     {
331       this.sourceRootDirectoryNames = sourceRootDirectoryNames;
332     }
333 
334     /**
335      * Sets the includes for the scanner.
336      *
337      * @param includes the includes for the scanner.
338      */
339     public void setIncludes(final List<String> includes)
340     {
341       this.includes = includes;
342     }
343 
344     /**
345      * Sets the excludes for the scanner.
346      *
347      * @param excludes the excludes for the scanner.
348      */
349     public void setExcludes(final List<String> excludes)
350     {
351       this.excludes = excludes;
352     }
353 
354     /**
355      * Sets the encoding of the source files in this project.
356      *
357      * @param sourceEncoding the encoding of the source files in this project.
358      */
359     public void setSourceEncoding(final String sourceEncoding)
360     {
361       this.sourceEncoding = sourceEncoding;
362     }
363 
364     /**
365      * Sets the Java version specified for the sources in the project.
366      *
367      * @param sourceEncoding the Java version specified for the sources in the
368      *          project.
369      */
370     public void setSourceVersion(final String sourceVersion)
371     {
372       this.sourceVersion = sourceVersion;
373     }
374 
375     /**
376      * Sets the encoding of the generated report.
377      *
378      * @param reportEncoding the encoding of the generated report.
379      */
380     public void setReportEncoding(final String reportEncoding)
381     {
382       this.reportEncoding = reportEncoding;
383     }
384 
385     /**
386      * Sets the class name of the reporter.
387      *
388      * @param reporterClass the class name of the reporter.
389      */
390     public void setReporter(final String reporterClass)
391     {
392       this.reporter = reporterClass;
393     }
394 
395     /**
396      * Sets the name of the file the report will be written to.
397      *
398      * @param report the name of the file the report will be written to.
399      */
400     public void setReport(final String report)
401     {
402       this.report = report;
403     }
404 
405     /**
406      * Sets the bundle set for this report execution.
407      *
408      * @param bundle the bundle set for this report execution.
409      */
410     public void setBundle(final ResourceBundle bundle)
411     {
412       this.bundle = bundle;
413     }
414 
415     /**
416      * Sets the directory where the Javadoc pages are found. This information is
417      * used to make relative links from the exceptions report to the Javadocs.
418      *
419      * @param javadocDir the directory where the Javadoc pages are found.
420      */
421     public void setJavadocDir(final File javadocDir)
422     {
423       this.javadocDir = javadocDir;
424     }
425 
426     /**
427      * Sets the reference to the style sheet to add as link element to the head
428      * of the XHTML document.
429      *
430      * @param styleSheet the reference to the style sheet to add as link element
431      *          to the head of the XHTML document.
432      */
433     public void setStyleSheet(final String styleSheet)
434     {
435       this.styleSheet = styleSheet;
436     }
437 
438     /**
439      * Sets the class of the mapper to the resource bundles of a given item
440      * class. The item is to be documented.
441      *
442      * @param bundleMapperClassName the class of the mapper to the resource
443      *          bundles of a given item class.
444      */
445     public void setBundleMapperClassName(final String bundleMapperClassName)
446     {
447       this.bundleMapperClassName = bundleMapperClassName;
448     }
449 
450     // --- business -----------------------------------------------------------
451 
452     /**
453      * Creates the project configuration.
454      *
455      * @return the project configuration.
456      * @throws IllegalArgumentException if any property is not valid.
457      */
458     public ProjectConfiguration<O> build() throws IllegalArgumentException
459     {
460       checkConstraints();
461 
462       // Please note that the parent class loader is required to be the one
463       // loading these classes since we have to match the ones returned by the
464       // Javadoc tool with the ones loaded from this class path.
465       projectClassLoader =
466           new ProjectClassLoader(getClass().getClassLoader(),
467               this.classRootDirectoryNames);
468       this.bundleMapper = createCodeBundleMapper(projectClassLoader);
469 
470       final ProjectConfiguration<O> config =
471           new DefaultProjectConfiguration<O>(this);
472       return config;
473     }
474 
475     @SuppressWarnings("unchecked")
476     private BundleMapper createCodeBundleMapper(final ClassLoader loader)
477       throws IllegalArgumentException
478     {
479       if (StringUtils.isNotBlank(bundleMapperClassName))
480       {
481         try
482         {
483           final Class<BundleMapper> clazz =
484               (Class<BundleMapper>) Class.forName(bundleMapperClassName);
485           final Constructor<BundleMapper> constructor =
486               clazz.getConstructor(ClassLoader.class);
487           final BundleMapper mapper =
488               (BundleMapper) constructor.newInstance(loader);
489           return mapper;
490         }
491         catch (final Exception e)
492         {
493           throw new IllegalArgumentException(
494               "Cannot create code bundle mapper for class '"
495                   + bundleMapperClassName + "'.", e);
496         }
497       }
498       return null;
499     }
500 
501     /**
502      * Checks that the builder constraints are met.
503      *
504      * @throws IllegalArgumentException if at least one constraint is offended.
505      */
506     private void checkConstraints() throws IllegalArgumentException
507     {
508       final StringBuilder buffer = new StringBuilder();
509 
510       ConfigUtils.checkStringValueProvided(buffer, projectName,
511           "Project name must not be 'null'.");
512       ConfigUtils.checkCollectionValueProvided(buffer, classRootDirectoryNames,
513           "Class root directory names must be provided.");
514       ConfigUtils.checkCollectionValueProvided(buffer,
515           sourceRootDirectoryNames,
516           "Source root directory names must be provided.");
517       ConfigUtils.checkStringValueProvided(buffer, report,
518           "The name for the report must be provided.");
519 
520       try
521       {
522         final Class<ReportGenerator<O>> clazz = getClassForName(reporter);
523         this.reporterInstance = clazz.newInstance();
524         this.reporterInstance.setEncoding(reportEncoding);
525       }
526       catch (final Exception e)
527       {
528         buffer.append('\n').append(
529             "Cannot create reporter instance '" + reporter + "': "
530                 + e.getMessage());
531       }
532 
533       ConfigUtils.checkStringValueProvided(buffer, reporter,
534           "The reporter class must be provided.");
535       ConfigUtils.checkValueProvided(buffer, bundle,
536           "The message bundle must be provided.");
537 
538       if (buffer.length() > 0)
539       {
540         throw new IllegalArgumentException(
541             "The following arguments are invalid:" + buffer);
542       }
543     }
544 
545     /**
546      * Returns the class for the given class name.
547      * <p>
548      * Implementation note: this method is used to not inline annotate the
549      * unchecked cast.
550      *
551      * @param reporterClass the name of the class to fetch.
552      * @return the class instance for the given name.
553      * @throws ClassNotFoundException if the class cannot be found.
554      */
555     @SuppressWarnings("unchecked")
556     private Class<ReportGenerator<O>> getClassForName(final String reporterClass)
557       throws ClassNotFoundException
558     {
559       return (Class<ReportGenerator<O>>) Class.forName(reporterClass);
560     }
561 
562     // --- object basics ------------------------------------------------------
563   }
564 
565   // ********************************* Methods ********************************
566 
567   // --- init -----------------------------------------------------------------
568 
569   // --- get&set --------------------------------------------------------------
570 
571   /**
572    * Returns the classpath used by the tool.
573    *
574    * @return the classpath used by the tool.
575    */
576   public List<String> getToolClassPath()
577   {
578     return toolClassPath;
579   }
580 
581   /**
582    * {@inheritDoc}
583    *
584    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getClassRootDirectoryNames()
585    */
586   public Collection<String> getClassRootDirectoryNames()
587   {
588     return classRootDirectoryNames;
589   }
590 
591   /**
592    * Returns the class loader to load project classes.
593    *
594    * @return the class loader to load project classes.
595    */
596   public ClassLoader getProjectClassLoader()
597   {
598     return projectClassLoader;
599   }
600 
601   /**
602    * {@inheritDoc}
603    *
604    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getSourceRootDirectoryNames()
605    */
606   public Collection<String> getSourceRootDirectoryNames()
607   {
608     return sourceRootDirectoryNames;
609   }
610 
611   /**
612    * {@inheritDoc}
613    *
614    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getProjectName()
615    */
616   public String getProjectName()
617   {
618     return projectName;
619   }
620 
621   /**
622    * Returns the includes for the scanner.
623    *
624    * @return the includes for the scanner.
625    */
626   public List<String> getIncludes()
627   {
628     return includes;
629   }
630 
631   /**
632    * Returns the excludes for the scanner.
633    *
634    * @return the excludes for the scanner.
635    */
636   public List<String> getExcludes()
637   {
638     return excludes;
639   }
640 
641   /**
642    * {@inheritDoc}
643    *
644    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getSourceEncoding()
645    */
646   public String getSourceEncoding()
647   {
648     return sourceEncoding;
649   }
650 
651   /**
652    * {@inheritDoc}
653    *
654    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getReportEncoding()
655    */
656   public String getReportEncoding()
657   {
658     return reportEncoding;
659   }
660 
661   /**
662    * {@inheritDoc}
663    */
664   public String getReporter()
665   {
666     return reporter;
667   }
668 
669   /**
670    * {@inheritDoc}
671    *
672    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getReporterInstance()
673    */
674   public ReportGenerator<O> getReporterInstance()
675   {
676     return reporterInstance;
677   }
678 
679   /**
680    * {@inheritDoc}
681    *
682    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getReport()
683    */
684   public String getReport()
685   {
686     return report;
687   }
688 
689   /**
690    * {@inheritDoc}
691    *
692    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getBundle()
693    */
694   public ResourceBundle getBundle()
695   {
696     return bundle;
697   }
698 
699   /**
700    * Returns the directory where the Javadoc pages are found. This information
701    * is used to make relative links from the exceptions report to the Javadocs.
702    *
703    * @return the directory where the Javadoc pages are found.
704    */
705   public File getJavadocDir()
706   {
707     return javadocDir;
708   }
709 
710   /**
711    * {@inheritDoc}
712    * <p>
713    * This value is optional and may be <code>null</code>.
714    * </p>
715    *
716    * @see de.smartics.exceptions.report.core.ExceptionCodeProjectConfiguration#getJavadocRelativeDir()
717    */
718   public String getJavadocRelativeDir()
719   {
720     final File dir = getJavadocDir();
721     if (dir != null)
722     {
723       final File reportLocation = new File(getReport());
724       final String relativePath;
725       if (reportLocation.isDirectory())
726       {
727         relativePath =
728             JavadocUtils.constructRelativePath(dir.getAbsolutePath(),
729                 reportLocation.getAbsolutePath());
730       }
731       else
732       {
733         relativePath =
734             JavadocUtils.constructRelativePath(dir.getAbsolutePath(),
735                 reportLocation.getParent());
736       }
737       return relativePath;
738     }
739     else
740     {
741       return null;
742     }
743   }
744 
745   /**
746    * Returns the reference to the style sheet to add as link element to the head
747    * of the XHTML document.
748    *
749    * @return the reference to the style sheet to add as link element to the head
750    *         of the XHTML document.
751    */
752   public String getStyleSheet()
753   {
754     return styleSheet;
755   }
756 
757   public String getSourceVersion()
758   {
759     return sourceVersion;
760   }
761 
762   /**
763    * {@inheritDoc}
764    *
765    * @see de.smartics.report.conf.ProjectConfiguration#getBundleMapper()
766    */
767   public BundleMapper getBundleMapper()
768   {
769     return bundleMapper;
770   }
771 
772   // --- business -------------------------------------------------------------
773 
774   // --- object basics --------------------------------------------------------
775 
776 }