View Javadoc
1   /* #%L
2    * wcm.io
3    * %%
4    * Copyright (C) 2018 wcm.io
5    * %%
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   * #L%
18   */
19  package io.wcm.qa.glnm.maven.freemarker;
20  
21  import static org.apache.commons.lang3.ArrayUtils.EMPTY_STRING_ARRAY;
22  
23  import java.io.File;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Map;
27  
28  /*
29   * Copyright 2001-2005 The Apache Software Foundation.
30   *
31   * Licensed under the Apache License, Version 2.0 (the "License");
32   * you may not use this file except in compliance with the License.
33   * You may obtain a copy of the License at
34   *
35   *      http://www.apache.org/licenses/LICENSE-2.0
36   *
37   * Unless required by applicable law or agreed to in writing, software
38   * distributed under the License is distributed on an "AS IS" BASIS,
39   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40   * See the License for the specific language governing permissions and
41   * limitations under the License.
42   */
43  
44  import org.apache.maven.plugin.AbstractMojo;
45  import org.apache.maven.plugin.MojoExecutionException;
46  import org.apache.maven.plugins.annotations.LifecyclePhase;
47  import org.apache.maven.plugins.annotations.Mojo;
48  import org.apache.maven.plugins.annotations.Parameter;
49  import org.codehaus.plexus.util.DirectoryScanner;
50  
51  import freemarker.template.Template;
52  import io.wcm.qa.glnm.configuration.ConfigurationUtil;
53  import io.wcm.qa.glnm.configuration.GaleniumConfiguration;
54  import io.wcm.qa.glnm.exceptions.GaleniumException;
55  import io.wcm.qa.glnm.maven.freemarker.pojo.SelectorPojo;
56  import io.wcm.qa.glnm.maven.freemarker.pojo.SpecPojo;
57  import io.wcm.qa.glnm.maven.freemarker.util.FormatUtil;
58  import io.wcm.qa.glnm.maven.freemarker.util.FreemarkerUtil;
59  import io.wcm.qa.glnm.selectors.base.Selector;
60  
61  /**
62   * Goal which finds Galen specs, extracts objects and generates Java code from it.
63   *
64   * @since 1.0.0
65   */
66  @Mojo(name = "specs", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
67  public class GalenSpecsMojo extends AbstractMojo {
68  
69    /**
70     * Root directory for generated output.
71     */
72    @Parameter(defaultValue = "${project.build.directory}/target/specs", property = "inputDir", required = true)
73    private String inputDirectory;
74  
75    /**
76     * Name of Freemarker template to use for abstract interactive selector base class.
77     */
78    @Parameter(defaultValue = "interactive-selector-base.ftlh", property = "interactiveSelectorBaseTemplate")
79    private String interactiveSelectorBaseTemplate;
80  
81    /**
82     * Name of Freemarker template to use for interactive selector interface.
83     */
84    @Parameter(defaultValue = "interactive-selector.ftlh", property = "interactiveSelectorInterfaceTemplate")
85    private String interactiveSelectorInterfaceTemplate;
86  
87    /**
88     * Root directory for generated output.
89     */
90    @Parameter(defaultValue = "${project.build.directory}/generated-sources/java", property = "outputDir", required = true)
91    private File outputDirectory;
92  
93    /**
94     * Package name to generate {@link Selector} code into.
95     */
96    @Parameter(defaultValue = "io.wcm.qa.glnm.selectors", property = "packagePrefixSelectors", required = true)
97    private String packagePrefixSelectors;
98  
99    /**
100    * Package name to generate {@link Selector} code into.
101    */
102   @Parameter(defaultValue = "io.wcm.qa.glnm.specs", property = "packagePrefixSpecs", required = true)
103   private String packagePrefixSpecs;
104 
105   /**
106    * A set of file patterns to exclude from selector generation.
107    */
108   @Parameter(property = "selectorExcludes")
109   private String[] selectorExcludes;
110 
111   /**
112    * A set of file patterns to include in selector generation.
113    */
114   @Parameter(property = "selectorIncludes")
115   private String[] selectorIncludes;
116 
117   /**
118    * Name of Freemarker template to use for generating top level classes.
119    */
120   @Parameter(defaultValue = "selector.ftlh", property = "selectorTemplate")
121   private String selectorTemplate;
122 
123   /**
124    * A set of file patterns to exclude from spec generation.
125    */
126   @Parameter(property = "specExcludes")
127   private String[] specExcludes;
128 
129   /**
130    * A set of file patterns to include in spec generation.
131    */
132   @Parameter(property = "specIncludes")
133   private String[] specIncludes;
134 
135   private final Collection<SpecPojo> specsForSelectors = new ArrayList<>();
136   private final Collection<SpecPojo> specsForSpecs = new ArrayList<>();
137 
138   /**
139    * Name of Freemarker template to use for recursively generating static inner classes.
140    */
141   @Parameter(defaultValue = "spec.ftlh", property = "specTemplate")
142   private String specTemplate;
143 
144   /**
145    * To be transferred to system properties when running plugin. Mostly used to manipulate
146    * {@link GaleniumConfiguration}.
147    */
148   @Parameter(property = "systemPropertyVariables")
149   private Map<String, String> systemPropertyVariables;
150 
151   /**
152    * Directory containing the Freemarker templates.
153    */
154   @Parameter(defaultValue = "${project.basedir}/src/main/resources/freemarker", property = "templateDir", required = true)
155   private File templateDirectory;
156 
157   @Parameter(defaultValue = "web-element.ftlh", property = "webElementTemplate")
158   private String webElementTemplate;
159 
160   /** {@inheritDoc} */
161   @Override
162   public void execute() throws MojoExecutionException {
163 
164     if (!initPlugin()) {
165       // if initialization does not work, plugin does not work
166       throw new GaleniumException("Plugin initialization failed.");
167     }
168 
169     // handle spec files and collect objects
170     parseSpecs();
171 
172     // prepare Freemarker data model and process template
173     generateCode();
174   }
175 
176   private boolean checkDirectory(File directory) {
177     getLog().info("checking directory: " + directory.getPath());
178     if (!directory.isDirectory()) {
179       getLog().error("directory not found: " + directory.getPath());
180       return false;
181     }
182 
183     return true;
184   }
185 
186   private boolean checkInputParams() {
187     return checkDirectory(templateDirectory);
188   }
189 
190   private void generateCode() {
191     generateInteractiveSelectorInterfaceCode();
192     generateInteractiveSelectorBaseCode();
193     generateSelectorCode();
194     generateWebElementCode();
195     generateSpecCode();
196   }
197 
198   private void generateInteractiveSelectorBaseCode() {
199     Template template = FreemarkerUtil.getTemplate(templateDirectory, interactiveSelectorBaseTemplate);
200     String className = getInteractiveSelectorBaseClassName();
201     String interfaceName = getInteractiveSelectorInterfaceClassName();
202     String packageName = getInteractiveSelectorPackageName();
203     Map<String, Object> model = FreemarkerUtil.getDataModelForInteractiveSelector(packageName, interfaceName, className);
204     File outputFile = FreemarkerUtil.getOutputFile(outputDirectory, packageName, className);
205     FreemarkerUtil.process(template, model, outputFile);
206   }
207 
208   private void generateInteractiveSelectorInterfaceCode() {
209     Template template = FreemarkerUtil.getTemplate(templateDirectory, interactiveSelectorInterfaceTemplate);
210     String className = getInteractiveSelectorBaseClassName();
211     String interfaceName = getInteractiveSelectorInterfaceClassName();
212     String packageName = getInteractiveSelectorPackageName();
213     Map<String, Object> model = FreemarkerUtil.getDataModelForInteractiveSelector(packageName, interfaceName, className);
214     File outputFile = FreemarkerUtil.getOutputFile(outputDirectory, packageName, interfaceName);
215     FreemarkerUtil.process(template, model, outputFile);
216   }
217 
218   private void generateSelectorCode() {
219     // same template for all selectors
220     getLog().info("fetching selector template");
221     Template template = FreemarkerUtil.getTemplate(templateDirectory, selectorTemplate);
222 
223     for (SpecPojo specPojo : specsForSelectors) {
224 
225       getLog().info("generating data models for '" + specPojo.getSpecPath() + "'");
226       for (SelectorPojo selector : specPojo.getRootSelectors()) {
227 
228         getLog().info("generating data model for '" + selector.elementName() + "'");
229         Map<String, Object> dataModelForSelector = FreemarkerUtil.getDataModelForSelector(
230             selector,
231             specPojo,
232             getInteractiveSelectorPackageName(),
233             getInteractiveSelectorBaseClassName(),
234             getInteractiveSelectorInterfaceClassName());
235 
236         getLog().debug("processing template");
237         FreemarkerUtil.process(template, dataModelForSelector, getSelectorOutputFile(selector, specPojo));
238       }
239     }
240   }
241 
242   private void generateSpecCode() {
243     // same template for all specs
244     getLog().info("fetching spec template");
245     Template template = FreemarkerUtil.getTemplate(templateDirectory, specTemplate);
246 
247     for (SpecPojo specPojo : specsForSpecs) {
248 
249       getLog().info("generating data model for '" + specPojo.getSpecPath() + "'");
250       Map<String, Object> dataModelForSpec = FreemarkerUtil.getDataModelForSpec(specPojo, packagePrefixSpecs);
251 
252       getLog().debug("processing template");
253       FreemarkerUtil.process(template, dataModelForSpec, getSpecOutputFile(specPojo));
254 
255     }
256   }
257 
258   private void generateWebElementCode() {
259     // same template for all webelements
260     getLog().info("fetching webelement template");
261     Template template = FreemarkerUtil.getTemplate(templateDirectory, webElementTemplate);
262 
263     for (SpecPojo specPojo : specsForSelectors) {
264 
265       getLog().info("generating data models for '" + specPojo.getSpecPath() + "'");
266       for (SelectorPojo selector : specPojo.getRootSelectors()) {
267 
268         getLog().info("generating data model for '" + selector.elementName() + "'");
269         Map<String, Object> dataModelForSelector = FreemarkerUtil.getDataModelForWebElement(
270             selector,
271             specPojo,
272             getInteractiveSelectorPackageName(),
273             getInteractiveSelectorBaseClassName(),
274             getInteractiveSelectorInterfaceClassName());
275 
276         getLog().debug("processing template");
277         FreemarkerUtil.process(template, dataModelForSelector, getWebElementOutputFile(selector, specPojo));
278       }
279     }
280 
281   }
282 
283   private File getWebElementOutputFile(SelectorPojo selector, SpecPojo specPojo) {
284     String outputPackage = FormatUtil.getSelectorsPackageName(packagePrefixSelectors, specPojo);
285     String className = FormatUtil.getClassName(selector) + "Gwe";
286     return FreemarkerUtil.getOutputFile(outputDirectory, outputPackage, className);
287   }
288 
289   private String[] getIncludedFiles(String baseDir, String[] includes, String[] excludes) {
290     if (new File(baseDir).isDirectory()) {
291       DirectoryScanner directoryScanner = new DirectoryScanner();
292       directoryScanner.setIncludes(includes);
293       directoryScanner.setExcludes(excludes);
294       directoryScanner.setBasedir(baseDir);
295       directoryScanner.scan();
296       String[] includedFiles = directoryScanner.getIncludedFiles();
297       return includedFiles;
298     }
299 
300     return EMPTY_STRING_ARRAY;
301   }
302 
303   private String[] getIncludedFilesForSelectors() {
304     return getIncludedFiles(inputDirectory, selectorIncludes, selectorExcludes);
305   }
306 
307   private String[] getIncludedFilesForSpecs() {
308     return getIncludedFiles(inputDirectory, specIncludes, specExcludes);
309   }
310 
311   private String getInteractiveSelectorBaseClassName() {
312     return FormatUtil.getClassName(new File(interactiveSelectorBaseTemplate));
313   }
314 
315   private String getInteractiveSelectorInterfaceClassName() {
316     return FormatUtil.getClassName(new File(interactiveSelectorInterfaceTemplate));
317   }
318 
319   private String getInteractiveSelectorPackageName() {
320     return packagePrefixSelectors;
321   }
322 
323   private File getSelectorOutputFile(SelectorPojo selector, SpecPojo spec) {
324     String outputPackage = FormatUtil.getSelectorsPackageName(packagePrefixSelectors, spec);
325     String className = FormatUtil.getClassName(selector);
326     return FreemarkerUtil.getOutputFile(outputDirectory, outputPackage, className);
327   }
328 
329   private File getSpecOutputFile(SpecPojo spec) {
330     String outputPackage = packagePrefixSpecs;
331     String className = FormatUtil.getClassName(spec);
332     return FreemarkerUtil.getOutputFile(outputDirectory, outputPackage, className);
333   }
334 
335   private void storeSpecForSelector(SpecPojo specPojo) {
336     specsForSelectors.add(specPojo);
337   }
338 
339   private void storeSpecForSpec(SpecPojo specPojo) {
340     specsForSpecs.add(specPojo);
341   }
342 
343   protected boolean initPlugin() {
344 
345     // transfer system properties
346     ConfigurationUtil.addToSystemProperties(systemPropertyVariables);
347     System.setProperty("packageRootName", packagePrefixSelectors);
348     System.setProperty("galenium.specPath", inputDirectory);
349 
350     // check input parameters
351     if (!checkInputParams()) {
352       return false;
353     }
354 
355     return true;
356   }
357 
358   protected void parseSpecs() {
359     getLog().info("processing spec files for selectors");
360     for (String specPath : getIncludedFilesForSelectors()) {
361       storeSpecForSelector(new SpecPojo(specPath));
362     }
363     getLog().info("processing spec files for specs");
364     for (String specPath : getIncludedFilesForSpecs()) {
365       storeSpecForSpec(new SpecPojo(specPath));
366     }
367   }
368 
369 }