View Javadoc

1   /*
2    * Copyright 2010-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.testdoc.collect.processor;
17  
18  import java.io.File;
19  import java.util.Collections;
20  import java.util.HashSet;
21  import java.util.Map;
22  import java.util.Set;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  
26  import javax.annotation.processing.AbstractProcessor;
27  import javax.annotation.processing.Filer;
28  import javax.annotation.processing.ProcessingEnvironment;
29  import javax.annotation.processing.RoundEnvironment;
30  import javax.annotation.processing.SupportedAnnotationTypes;
31  import javax.annotation.processing.SupportedSourceVersion;
32  import javax.lang.model.element.TypeElement;
33  import javax.lang.model.util.Elements;
34  
35  import org.apache.commons.lang.StringUtils;
36  
37  import de.smartics.testdoc.collect.extractor.ExtractorConfig;
38  import de.smartics.testdoc.core.TestDocProperty;
39  import de.smartics.testdoc.core.adapter.BasedFilerSingletonInMemoryExportAdapter;
40  import de.smartics.testdoc.core.adapter.DirectoryExportAdapter;
41  import de.smartics.testdoc.core.adapter.FilerExportAdapter;
42  import de.smartics.testdoc.core.doc.names.EnglishTestNameUtils;
43  import de.smartics.testdoc.core.doc.names.TestNameUtils;
44  import de.smartics.testdoc.core.export.ExportAdapter;
45  import de.smartics.testdoc.core.source.SourceCodeHelper;
46  import de.smartics.testdoc.core.source.SourceCodeHelperFactory;
47  
48  /**
49   * Processes {@link de.smartics.testdoc.annotations.Uut} annotations.
50   *
51   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
52   * @version $Revision:591 $
53   */
54  @SupportedAnnotationTypes("de.smartics.testdoc.annotations.Uut")
55  @SupportedSourceVersion(javax.lang.model.SourceVersion.RELEASE_6)
56  public class TestDocProcessor extends AbstractProcessor
57  { // NOPMD
58    // ********************************* Fields *********************************
59  
60    // --- constants ------------------------------------------------------------
61  
62    /**
63     * Class logger.
64     */
65    private static final Logger LOG = Logger.getLogger(TestDocProcessor.class
66        .getName());
67  
68    // --- members --------------------------------------------------------------
69  
70    /**
71     * Flag to refrain from writing notes.
72     */
73    private boolean quiet;
74  
75    /**
76     * The annotation processor logger.
77     */
78    private ProcessorLogHelper log;
79  
80    /**
81     * The adapter to export unit test documentation.
82     */
83    private ExportAdapter exporter;
84  
85    /**
86     * The utilities to create displayable names (labels) for tests.
87     */
88    private TestNameUtils testNameUtils;
89  
90    /**
91     * The reference to the filer provided by the APT environment.
92     */
93    private Filer filer;
94  
95    /**
96     * Utilities to extract information.
97     */
98    private ExtractorConfig extractorConfig;
99  
100   // ****************************** Initializer *******************************
101 
102   // ****************************** Constructors ******************************
103 
104   // ****************************** Inner Classes *****************************
105 
106   // ********************************* Methods ********************************
107 
108   // --- init -----------------------------------------------------------------
109 
110   // --- get&set --------------------------------------------------------------
111 
112   // --- business -------------------------------------------------------------
113 
114   /**
115    * {@inheritDoc}
116    */
117   @Override
118   public synchronized void init(final ProcessingEnvironment processingEnv)
119   {
120     super.init(processingEnv);
121 
122     log = new ProcessorLogHelper(quiet, processingEnv);
123 
124     filer = processingEnv.getFiler();
125     final Map<String, String> options = processingEnv.getOptions();
126     initProcessor(options);
127     initTestNameUtils(options);
128     initExporter(options);
129 
130     final Elements elementUtils = processingEnv.getElementUtils();
131 
132     final SourceCodeHelperFactory factory =
133         new SourceCodeHelperFactory(processingEnv);
134 
135     final SourceCodeHelper sourceCodeHelper = factory.create();
136     extractorConfig =
137         new ExtractorConfig(elementUtils, sourceCodeHelper, testNameUtils);
138   }
139 
140   private void initProcessor(final Map<String, String> options)
141   {
142     final String key = TestDocProperty.PROC_CONF_QUIET.getName();
143     final String quietValue = options.get(key);
144     if (quietValue != null)
145     {
146       quiet = Boolean.valueOf(quietValue);
147     }
148     else
149     {
150       quiet = (Boolean) TestDocProperty.PROC_CONF_QUIET.getDefaultValue();
151     }
152   }
153 
154   private void initTestNameUtils(final Map<String, String> options)
155   {
156     final String key = TestDocProperty.TEST_NAME_UTILS_CLASS_NAME.getName();
157     final String className = options.get(key);
158     if (StringUtils.isNotBlank(className))
159     {
160       testNameUtils = (TestNameUtils) createInstance(className);
161     }
162     else
163     {
164       testNameUtils = new EnglishTestNameUtils();
165     }
166   }
167 
168   private void initExporter(final Map<String, String> options)
169   {
170     final String baseDirKey = TestDocProperty.EXPORT_ADAPTER_BASE_DIR.getName();
171     final String baseDirName = options.get(baseDirKey);
172     if (StringUtils.isNotBlank(baseDirName))
173     {
174       final File baseDir = new File(baseDirName);
175       exporter = new DirectoryExportAdapter(baseDir);
176     }
177     else
178     {
179       final String key = TestDocProperty.EXPORT_ADAPTER_CLASS_NAME.getName();
180       final String className = options.get(key);
181       if (StringUtils.isNotBlank(className) && isNoFilerBasedType(className))
182       {
183         exporter = (ExportAdapter) createInstance(className);
184       }
185       else
186       {
187         if (FilerExportAdapter.class.getName().equals(className))
188         {
189           exporter = new FilerExportAdapter(filer);
190         }
191         else
192         {
193           exporter = new BasedFilerSingletonInMemoryExportAdapter(filer);
194         }
195       }
196     }
197   }
198 
199   private static boolean isNoFilerBasedType(final String className)
200   {
201     return !FilerExportAdapter.class.getName().equals(className)
202            && !BasedFilerSingletonInMemoryExportAdapter.class.getName().equals(
203                className);
204   }
205 
206   private Object createInstance(final String className)
207   {
208     try
209     {
210       return Class.forName(className).newInstance();
211     }
212     catch (final InstantiationException e)
213     {
214       throw handleCreationProblem(className, ExportAdapter.class, e);
215     }
216     catch (final IllegalAccessException e)
217     {
218       throw handleCreationProblem(className, ExportAdapter.class, e);
219     }
220     catch (final ClassNotFoundException e)
221     {
222       throw handleCreationProblem(className, ExportAdapter.class, e);
223     }
224   }
225 
226   @Override
227   public Set<String> getSupportedOptions()
228   {
229     final Set<String> options = new HashSet<String>();
230     options.addAll(super.getSupportedOptions());
231     for (final TestDocProperty property : TestDocProperty.values())
232     {
233       final String name = property.getName();
234       options.add(name);
235     }
236     return Collections.unmodifiableSet(options);
237   }
238 
239   private RuntimeException handleCreationProblem(final String className,
240       final Class<?> type, final Exception e)
241   {
242     final String message =
243         "Cannot instantiate '" + className + "' as implementation of the '"
244             + type.getName() + "' interface.";
245     log.error(message);
246     return new IllegalArgumentException(message, e);
247   }
248 
249   /**
250    * {@inheritDoc}
251    *
252    * @return <code>false</code>, always.
253    */
254   @Override
255   public boolean process(final Set<? extends TypeElement> annotations,
256       final RoundEnvironment roundEnv)
257   {
258     for (final TypeElement annotation : annotations)
259     {
260       LOG.log(Level.FINER, "Processing annotation {0}.",
261           annotation.getQualifiedName());
262 
263       final UutProcess process = new UutProcess(log, exporter, extractorConfig);
264       process.processElements(roundEnv, annotation);
265     }
266 
267     return false;
268   }
269 
270   // --- object basics --------------------------------------------------------
271 
272 }