View Javadoc
1   /*
2    * Copyright (C) 2025 the original author or authors.
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  
17  package org.codehaus.gmavenplus.util;
18  
19  import org.apache.maven.plugin.logging.Log;
20  import org.codehaus.gmavenplus.model.GroovyCompileConfiguration;
21  import org.codehaus.gmavenplus.model.GroovyDocConfiguration;
22  import org.codehaus.gmavenplus.model.GroovyStubConfiguration;
23  import org.codehaus.gmavenplus.model.Link;
24  import org.apache.maven.shared.model.fileset.FileSet;
25  import org.apache.maven.shared.model.fileset.util.FileSetManager;
26  import org.codehaus.gmavenplus.groovyworkarounds.GroovyDocTemplateInfo;
27  import java.util.ArrayList;
28  import java.util.Properties;
29  import org.codehaus.gmavenplus.model.internal.Version;
30  
31  import java.io.File;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.security.CodeSource;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  
40  import static org.codehaus.gmavenplus.util.ReflectionUtils.*;
41  
42  /**
43   * Handles the actual compilation logic, separated from the Mojo to allow forked execution.
44   *
45   * @author Keegan Witt
46   */
47  public class GroovyCompiler {
48  
49      /**
50       * Groovy 5.0.0-beta-1 version.
51       */
52      protected static final Version GROOVY_5_0_0_BETA_1 = new Version(5, 0, 0, "beta-1");
53  
54      /**
55       * Groovy 5.0.0-alpha-13 version.
56       */
57      protected static final Version GROOVY_5_0_0_ALPHA13 = new Version(5, 0, 0, "alpha-13");
58  
59      /**
60       * Groovy 5.0.0-alpha-11 version.
61       */
62      protected static final Version GROOVY_5_0_0_ALPHA11 = new Version(5, 0, 0, "alpha-11");
63  
64      /**
65       * Groovy 5.0.0-alpha-8 version.
66       */
67      protected static final Version GROOVY_5_0_0_ALPHA8 = new Version(5, 0, 0, "alpha-8");
68  
69      /**
70       * Groovy 5.0.0-alpha-3 version.
71       */
72      protected static final Version GROOVY_5_0_0_ALPHA3 = new Version(5, 0, 0, "alpha-3");
73  
74      /**
75       * Groovy 5.0.0-alpha-1 version.
76       */
77      protected static final Version GROOVY_5_0_0_ALPHA1 = new Version(5, 0, 0, "alpha-1");
78  
79      /**
80       * Groovy 4.0.27 version.
81       */
82      protected static final Version GROOVY_4_0_27 = new Version(4, 0, 27);
83  
84      /**
85       * Groovy 4.0.24 version.
86       */
87      protected static final Version GROOVY_4_0_24 = new Version(4, 0, 24);
88  
89      /**
90       * Groovy 4.0.21 version.
91       */
92      protected static final Version GROOVY_4_0_21 = new Version(4, 0, 21);
93  
94      /**
95       * Groovy 4.0.11 version.
96       */
97      protected static final Version GROOVY_4_0_16 = new Version(4, 0, 16);
98  
99      /**
100      * Groovy 4.0.11 version.
101      */
102     protected static final Version GROOVY_4_0_11 = new Version(4, 0, 11);
103 
104     /**
105      * Groovy 4.0.6 version.
106      */
107     protected static final Version GROOVY_4_0_6 = new Version(4, 0, 6);
108 
109     /**
110      * Groovy 4.0.2 version.
111      */
112     protected static final Version GROOVY_4_0_2 = new Version(4, 0, 2);
113 
114     /**
115      * Groovy 4.0.0 beta-1 version.
116      */
117     protected static final Version GROOVY_4_0_0_BETA1 = new Version(4, 0, 0, "beta-1");
118 
119     /**
120      * Groovy 4.0.0 alpha-3 version.
121      */
122     protected static final Version GROOVY_4_0_0_ALPHA3 = new Version(4, 0, 0, "alpha-3");
123 
124     /**
125      * Groovy 4.0.0 alpha-1 version.
126      */
127     protected static final Version GROOVY_4_0_0_ALPHA1 = new Version(4, 0, 0, "alpha-1");
128 
129     /**
130      * Groovy 3.0.8 version.
131      */
132     protected static final Version GROOVY_3_0_8 = new Version(3, 0, 8);
133 
134     /**
135      * Groovy 3.0.6 version.
136      */
137     protected static final Version GROOVY_3_0_6 = new Version(3, 0, 6);
138 
139     /**
140      * Groovy 3.0.5 version.
141      */
142     protected static final Version GROOVY_3_0_5 = new Version(3, 0, 5);
143 
144     /**
145      * Groovy 3.0.3 version.
146      */
147     protected static final Version GROOVY_3_0_3 = new Version(3, 0, 3);
148 
149     /**
150      * Groovy 3.0.0 beta-2 version.
151      */
152     protected static final Version GROOVY_3_0_0_BETA2 = new Version(3, 0, 0, "beta-2");
153 
154     /**
155      * Groovy 3.0.0 beta-1 version.
156      */
157     protected static final Version GROOVY_3_0_0_BETA1 = new Version(3, 0, 0, "beta-1");
158 
159     /**
160      * Groovy 3.0.0 alpha-4 version.
161      */
162     protected static final Version GROOVY_3_0_0_ALPHA4 = new Version(3, 0, 0, "alpha-4");
163 
164     /**
165      * Groovy 3.0.0 alpha-2 version.
166      */
167     protected static final Version GROOVY_3_0_0_ALPHA2 = new Version(3, 0, 0, "alpha-2");
168 
169     /**
170      * Groovy 3.0.0 alpha-1 version.
171      */
172     protected static final Version GROOVY_3_0_0_ALPHA1 = new Version(3, 0, 0, "alpha-1");
173 
174     /**
175      * Groovy 2.6.0 alpha-4 version.
176      */
177     protected static final Version GROOVY_2_6_0_ALPHA4 = new Version(2, 6, 0, "alpha-4");
178 
179     /**
180      * Groovy 2.6.0 alpha-1 version.
181      */
182     protected static final Version GROOVY_2_6_0_ALPHA1 = new Version(2, 6, 0, "alpha-1");
183 
184     /**
185      * Groovy 2.5.7 version.
186      */
187     protected static final Version GROOVY_2_5_7 = new Version(2, 5, 7);
188 
189     /**
190      * Groovy 2.5.3 version.
191      */
192     protected static final Version GROOVY_2_5_3 = new Version(2, 5, 3);
193 
194     /**
195      * Groovy 2.5.0 alpha-1 version.
196      */
197     protected static final Version GROOVY_2_5_0_ALPHA1 = new Version(2, 5, 0, "alpha-1");
198 
199     /**
200      * Groovy 2.3.3 version.
201      */
202     protected static final Version GROOVY_2_3_3 = new Version(2, 3, 3);
203 
204     /**
205      * Groovy 1.8.2 version.
206      */
207     protected static final Version GROOVY_1_8_2 = new Version(1, 8, 2);
208 
209     /**
210      * Groovy 1.8.3 version.
211      */
212     protected static final Version GROOVY_1_8_3 = new Version(1, 8, 3);
213 
214     /**
215      * Groovy 2.1.3 version.
216      */
217     protected static final Version GROOVY_2_1_3 = new Version(2, 1, 3);
218 
219     /**
220      * Groovy 2.1.0 beta-1 version.
221      */
222     protected static final Version GROOVY_2_1_0_BETA1 = new Version(2, 1, 0, "beta-1");
223 
224     /**
225      * Groovy 2.0.0 beta-3 version.
226      */
227     protected static final Version GROOVY_2_0_0_BETA3 = new Version(2, 0, 0, "beta-3");
228 
229     /**
230      * Groovy 1.9.0 beta-1 version.
231      */
232     protected static final Version GROOVY_1_9_0_BETA1 = new Version(1, 9, 0, "beta-1");
233 
234     /**
235      * Groovy 1.9.0 beta-3 version.
236      */
237     protected static final Version GROOVY_1_9_0_BETA3 = new Version(1, 9, 0, "beta-3");
238 
239     /**
240      * Groovy 1.6.0 version.
241      */
242     protected static final Version GROOVY_1_6_0 = new Version(1, 6, 0);
243 
244     /**
245      * Groovy 1.6.0 RC-2 version.
246      */
247     protected static final Version GROOVY_1_6_0_RC2 = new Version(1, 6, 0, "RC-2");
248 
249     /**
250      * Groovy 1.5.2 version.
251      */
252     protected static final Version GROOVY_1_5_2 = new Version(1, 5, 2);
253 
254     /**
255      * Java 1.7 version.
256      */
257     protected static final Version JAVA_1_7 = new Version(1, 7);
258 
259     /**
260      * Java 1.8 version.
261      */
262     protected static final Version JAVA_1_8 = new Version(1, 8);
263 
264     /**
265      * Java 1.8 version.
266      */
267     protected static final Version JAVA_12 = new Version(12);
268 
269     private final ClassWrangler classWrangler;
270     private final Log log;
271 
272     public GroovyCompiler(ClassWrangler classWrangler, Log log) {
273         this.classWrangler = classWrangler;
274         this.log = log;
275     }
276 
277     public void compile(GroovyCompileConfiguration configuration) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
278         if (configuration.getSources() == null || configuration.getSources().isEmpty()) {
279             log.info("No sources specified for compilation. Skipping.");
280             return;
281         }
282 
283         if (!configuration.isSkipBytecodeCheck()) {
284             verifyGroovyVersionSupportsTargetBytecode(configuration.getTargetBytecode());
285         }
286 
287         // get classes we need with reflection
288         Class<?> compilerConfigurationClass = classWrangler.getClass("org.codehaus.groovy.control.CompilerConfiguration");
289         Class<?> compilationUnitClass = classWrangler.getClass("org.codehaus.groovy.control.CompilationUnit");
290         Class<?> groovyClassLoaderClass = classWrangler.getClass("groovy.lang.GroovyClassLoader");
291 
292         // setup compile options
293         Object compilerConfiguration = setupCompilerConfiguration(configuration, compilerConfigurationClass);
294         Object groovyClassLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class, compilerConfigurationClass), classWrangler.getClassLoader(), compilerConfiguration);
295         Object transformLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class), classWrangler.getClassLoader());
296 
297         // add Groovy sources
298         Object compilationUnit = setupCompilationUnit(configuration.getSources(), compilerConfigurationClass, compilationUnitClass, groovyClassLoaderClass, compilerConfiguration, groovyClassLoader, transformLoader);
299 
300         // compile the classes
301         invokeMethod(findMethod(compilationUnitClass, "compile"), compilationUnit);
302 
303         // log compiled classes
304         List<?> classes = (List<?>) invokeMethod(findMethod(compilationUnitClass, "getClasses"), compilationUnit);
305         log.info("Compiled " + classes.size() + " file" + (classes.size() != 1 ? "s" : "") + ".");
306     }
307 
308     public void generateGroovyDoc(GroovyDocConfiguration configuration) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
309         if (configuration.getSourceDirectories() == null || configuration.getSourceDirectories().length == 0) {
310             log.info("No source directories specified for GroovyDoc generation. Skipping.");
311             return;
312         }
313 
314         // Note: minGroovyVersion check is usually done by caller (Mojo) but strict check is good.
315         // GroovyDoc supports 1.5.2+ generally (LinkArgument) or 1.6+ for properties?
316         // AbstractGroovyDocMojo checks groovyVersionSupportsAction() which uses mojo defined min version.
317         // We can skip strict min version check here and rely on feature detection or caller.
318 
319         // get classes we need with reflection
320         Class<?> groovyDocToolClass = classWrangler.getClass(configuration.getGroovyDocToolClass() == null ? "org.codehaus.groovy.tools.groovydoc.GroovyDocTool" : configuration.getGroovyDocToolClass());
321         Class<?> outputToolClass = classWrangler.getClass(configuration.getOutputToolClass() == null ? "org.codehaus.groovy.tools.groovydoc.OutputTool" : configuration.getOutputToolClass());
322         Class<?> fileOutputToolClass = classWrangler.getClass(configuration.getFileOutputToolClass() == null ? "org.codehaus.groovy.tools.groovydoc.FileOutputTool" : configuration.getFileOutputToolClass());
323         Class<?> resourceManagerClass = classWrangler.getClass(configuration.getResourceManagerClass() == null ? "org.codehaus.groovy.tools.groovydoc.ResourceManager" : configuration.getResourceManagerClass());
324         Class<?> classpathResourceManagerClass = classWrangler.getClass(configuration.getClasspathResourceManagerClass() == null ? "org.codehaus.groovy.tools.groovydoc.ClasspathResourceManager" : configuration.getClasspathResourceManagerClass());
325 
326         // set up GroovyDoc options
327         if (configuration.isAttachGroovyDocAnnotation()) {
328             if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA4)) {
329                 System.setProperty("runtimeGroovydoc", "true");
330             } else {
331                 log.warn("Requested to enable attaching GroovyDoc annotation, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_3_0_0_ALPHA4 + " or newer). Ignoring enableGroovyDocAnnotation parameter.");
332             }
333         }
334 
335         Object fileOutputTool = invokeConstructor(findConstructor(fileOutputToolClass));
336         Object classpathResourceManager = invokeConstructor(findConstructor(classpathResourceManagerClass));
337         FileSetManager fileSetManager = new FileSetManager();
338         List<String> sourceDirectoriesStrings = new ArrayList<>();
339         for (FileSet sourceDirectory : configuration.getSourceDirectories()) {
340             sourceDirectoriesStrings.add(sourceDirectory.getDirectory());
341         }
342         GroovyDocTemplateInfo groovyDocTemplateInfo = new GroovyDocTemplateInfo(classWrangler.getGroovyVersion());
343 
344         List<?> groovyDocLinks = setupLinks(configuration);
345 
346         if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2) && configuration.getDocProperties() != null && !configuration.getDocProperties().isEmpty()) {
347             log.warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support GroovyDoc documentation properties (docTitle, footer, header, displayAuthor, overviewFile, and scope). You need Groovy 1.6-RC-2 or newer to support this. Ignoring properties.");
348         }
349 
350         // prevent Java stubs from overwriting GroovyDoc
351         List<String> groovyDocSources = setupGroovyDocSources(configuration.getSourceDirectories(), fileSetManager);
352 
353         // instantiate GroovyDocTool
354         Object groovyDocTool = createGroovyDocTool(groovyDocToolClass, resourceManagerClass, configuration.getDocProperties(), classpathResourceManager, sourceDirectoriesStrings, groovyDocTemplateInfo, groovyDocLinks, configuration);
355 
356         // generate GroovyDoc
357         performGroovyDocGeneration(configuration.getOutputDirectory(), groovyDocToolClass, outputToolClass, fileOutputTool, groovyDocSources, groovyDocTool);
358     }
359 
360     @SuppressWarnings({"rawtypes", "unchecked"})
361     protected List<?> setupLinks(GroovyDocConfiguration configuration) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
362         List linksList = new ArrayList();
363         if (configuration.getLinks() != null && !configuration.getLinks().isEmpty()) {
364             Class<?> linkArgumentClass = null;
365             if (configuration.getLinkArgumentClass() == null) {
366                 if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2)) {
367                     linkArgumentClass = classWrangler.getClass("org.codehaus.groovy.tools.groovydoc.LinkArgument");
368                 } else if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_5_2)) {
369                     linkArgumentClass = classWrangler.getClass("org.codehaus.groovy.ant.Groovydoc$LinkArgument");
370                 }
371             } else {
372                 linkArgumentClass = classWrangler.getClass(configuration.getLinkArgumentClass());
373             }
374             if (linkArgumentClass != null) {
375                 Method setHref = findMethod(linkArgumentClass, "setHref", String.class);
376                 Method setPackages = findMethod(linkArgumentClass, "setPackages", String.class);
377                 for (Link link : configuration.getLinks()) {
378                     Object linkArgument = invokeConstructor(findConstructor(linkArgumentClass));
379                     invokeMethod(setHref, linkArgument, link.getHref());
380                     invokeMethod(setPackages, linkArgument, link.getPackages());
381                     linksList.add(linkArgument);
382                 }
383             } else {
384                 log.warn("Requested to use GroovyDoc links, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be 1.5.2 or newer). Ignoring links parameter.");
385             }
386         }
387         return linksList;
388     }
389 
390     protected Object createGroovyDocTool(final Class<?> groovyDocToolClass, final Class<?> resourceManagerClass, final Properties docProperties, final Object classpathResourceManager, final List<String> sourceDirectories, final GroovyDocTemplateInfo groovyDocTemplateInfo, final List<?> groovyDocLinks, GroovyDocConfiguration configuration) throws InvocationTargetException, IllegalAccessException, InstantiationException {
391         Object groovyDocTool;
392 
393         String[] defaultDocTemplates = configuration.getDefaultDocTemplates();
394         String[] defaultPackageTemplates = configuration.getDefaultPackageTemplates();
395         String[] defaultClassTemplates = configuration.getDefaultClassTemplates();
396 
397         if ((ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_27) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1)) || ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_5_0_0_BETA_1)) {
398              groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String[].class, String[].class, String[].class, String[].class, List.class, String.class, Properties.class),
399                     classpathResourceManager,
400                     sourceDirectories.toArray(new String[0]),
401                     defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
402                     defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
403                     defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
404                     groovyDocLinks,
405                     configuration.getLanguageLevel(),
406                     docProperties
407             );
408         } else if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2)) {
409             groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String[].class, String[].class, String[].class, String[].class, List.class, Properties.class),
410                     classpathResourceManager,
411                     sourceDirectories.toArray(new String[0]),
412                     defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
413                     defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
414                     defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
415                     groovyDocLinks,
416                     docProperties
417             );
418         } else if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_5_2)) {
419             groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String.class, String[].class, String[].class, String[].class, List.class),
420                     classpathResourceManager,
421                     sourceDirectories.get(0),
422                     defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
423                     defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
424                     defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates,
425                     groovyDocLinks
426             );
427             if (sourceDirectories.size() > 1) {
428                 log.warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support more than one GroovyDoc source directory (must be 1.6-RC-2 or newer). Only using first source directory (" + sourceDirectories.get(0) + ").");
429             }
430         } else {
431             groovyDocTool = invokeConstructor(findConstructor(groovyDocToolClass, resourceManagerClass, String.class, String[].class, String[].class, String[].class),
432                     classpathResourceManager,
433                     sourceDirectories.get(0),
434                     defaultDocTemplates == null ? groovyDocTemplateInfo.defaultDocTemplates() : defaultDocTemplates,
435                     defaultPackageTemplates == null ? groovyDocTemplateInfo.defaultPackageTemplates() : defaultPackageTemplates,
436                     defaultClassTemplates == null ? groovyDocTemplateInfo.defaultClassTemplates() : defaultClassTemplates
437             );
438              if (sourceDirectories.size() > 1) {
439                 log.warn("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support more than one GroovyDoc source directory (must be 1.6-RC-2 or newer). Only using first source directory (" + sourceDirectories.get(0) + ").");
440             }
441         }
442         return groovyDocTool;
443     }
444 
445     protected List<String> setupGroovyDocSources(final FileSet[] sourceDirectories, final FileSetManager fileSetManager) {
446         List<String> javaSources = new ArrayList<>();
447         List<String> groovySources = new ArrayList<>();
448         List<String> possibleGroovyStubs = new ArrayList<>();
449         for (FileSet sourceDirectory : sourceDirectories) {
450             String[] sources = fileSetManager.getIncludedFiles(sourceDirectory);
451             for (String source : sources) {
452                 if (source.endsWith(".java") && !javaSources.contains(source)) {
453                     javaSources.add(source);
454                 } else if (!groovySources.contains(source)) {
455                     groovySources.add(source);
456                     possibleGroovyStubs.add(source.replaceFirst("\\." + FileUtils.getFileExtension(source), ".java"));
457                 }
458             }
459         }
460         javaSources.removeAll(possibleGroovyStubs);
461         List<String> groovyDocSources = new ArrayList<>();
462         groovyDocSources.addAll(javaSources);
463         groovyDocSources.addAll(groovySources);
464 
465         return groovyDocSources;
466     }
467 
468     protected void performGroovyDocGeneration(final File outputDirectory, final Class<?> groovyDocToolClass, final Class<?> outputToolClass, final Object fileOutputTool, final List<String> groovyDocSources, final Object groovyDocTool) throws InvocationTargetException, IllegalAccessException {
469         log.debug("Adding sources to generate GroovyDoc for:");
470         if (log.isDebugEnabled()) {
471             for (String groovyDocSource : groovyDocSources) {
472                 log.debug("    " + groovyDocSource);
473             }
474         }
475         if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0_RC2)) {
476             invokeMethod(findMethod(groovyDocToolClass, "add", List.class), groovyDocTool, groovyDocSources);
477         } else {
478             Method add = findMethod(groovyDocToolClass, "add", String.class);
479             for (String groovyDocSource : groovyDocSources) {
480                 invokeMethod(add, groovyDocTool, groovyDocSource);
481             }
482         }
483         invokeMethod(findMethod(groovyDocToolClass, "renderToOutput", outputToolClass, String.class), groovyDocTool, fileOutputTool, outputDirectory.getAbsolutePath());
484     }
485 
486     public void generateStubs(GroovyStubConfiguration configuration) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
487 
488         if (configuration.getStubSources() == null || configuration.getStubSources().isEmpty()) {
489             log.info("No sources specified for stub generation. Skipping.");
490             return;
491         }
492 
493         if (!supportsStubGeneration()) {
494             log.error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support stub generation. The minimum version of Groovy required is 1.8.2. Skipping stub generation.");
495             return;
496         }
497 
498         if (!configuration.isSkipBytecodeCheck()) {
499             verifyGroovyVersionSupportsTargetBytecode(configuration.getTargetBytecode());
500         }
501 
502         // get classes we need with reflection
503         Class<?> compilerConfigurationClass = classWrangler.getClass("org.codehaus.groovy.control.CompilerConfiguration");
504         Class<?> javaStubCompilationUnitClass = classWrangler.getClass("org.codehaus.groovy.tools.javac.JavaStubCompilationUnit");
505         Class<?> groovyClassLoaderClass = classWrangler.getClass("groovy.lang.GroovyClassLoader");
506 
507         // setup stub generation options
508         Object compilerConfiguration = setupStubCompilerConfiguration(configuration, compilerConfigurationClass);
509         Object groovyClassLoader = invokeConstructor(findConstructor(groovyClassLoaderClass, ClassLoader.class, compilerConfigurationClass), classWrangler.getClassLoader(), compilerConfiguration);
510         Object javaStubCompilationUnit = invokeConstructor(findConstructor(javaStubCompilationUnitClass, compilerConfigurationClass, groovyClassLoaderClass, File.class), compilerConfiguration, groovyClassLoader, configuration.getOutputDirectory());
511 
512         // add Groovy sources
513         addGroovySources(configuration.getStubSources(), compilerConfigurationClass, javaStubCompilationUnitClass, compilerConfiguration, javaStubCompilationUnit);
514 
515         // generate the stubs
516         invokeMethod(findMethod(javaStubCompilationUnitClass, "compile"), javaStubCompilationUnit);
517     }
518 
519     protected Object setupStubCompilerConfiguration(final GroovyStubConfiguration configuration, final Class<?> compilerConfigurationClass) throws InvocationTargetException, IllegalAccessException, InstantiationException {
520         Object compilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
521         invokeMethod(findMethod(compilerConfigurationClass, "setDebug", boolean.class), compilerConfiguration, configuration.isDebug());
522         invokeMethod(findMethod(compilerConfigurationClass, "setVerbose", boolean.class), compilerConfiguration, configuration.isVerbose());
523         invokeMethod(findMethod(compilerConfigurationClass, "setWarningLevel", int.class), compilerConfiguration, configuration.getWarningLevel());
524         invokeMethod(findMethod(compilerConfigurationClass, "setTolerance", int.class), compilerConfiguration, configuration.getTolerance());
525         invokeMethod(findMethod(compilerConfigurationClass, "setTargetBytecode", String.class), compilerConfiguration, translateJavacTargetToTargetBytecode(configuration.getTargetBytecode()));
526         if (configuration.getSourceEncoding() != null) {
527             invokeMethod(findMethod(compilerConfigurationClass, "setSourceEncoding", String.class), compilerConfiguration, configuration.getSourceEncoding());
528         }
529         Map<String, Object> options = new HashMap<>();
530         options.put("stubDir", configuration.getOutputDirectory());
531         options.put("keepStubs", Boolean.TRUE);
532         invokeMethod(findMethod(compilerConfigurationClass, "setJointCompilationOptions", Map.class), compilerConfiguration, options);
533 
534         return compilerConfiguration;
535     }
536 
537     protected void addGroovySources(final Set<File> stubSources, final Class<?> compilerConfigurationClass, final Class<?> javaStubCompilationUnitClass, final Object compilerConfiguration, final Object javaStubCompilationUnit) throws InvocationTargetException, IllegalAccessException {
538         // Since we don't have FileUtils here easily without dependencies, we can just use simple extension check.
539         // Wait, FileUtils is in util/FileUtils.java in this project.
540         // I should check if I can assume it is available. It is in the same package or similar.
541         // It's org.codehaus.gmavenplus.util.FileUtils.
542         // I will use it.
543 
544         Set<String> scriptExtensions = new java.util.HashSet<>();
545         for (File stubSource : stubSources) {
546             scriptExtensions.add(FileUtils.getFileExtension(stubSource));
547         }
548         log.debug("Detected Groovy file extensions: " + scriptExtensions + ".");
549         if (supportsSettingExtensions()) {
550             invokeMethod(findMethod(compilerConfigurationClass, "setScriptExtensions", Set.class), compilerConfiguration, scriptExtensions);
551         }
552         log.debug("Adding Groovy to generate stubs for:");
553         Method addSource = findMethod(javaStubCompilationUnitClass, "addSource", File.class);
554         for (File stubSource : stubSources) {
555             log.debug("    " + stubSource);
556             if (supportsSettingExtensions()) {
557                 invokeMethod(addSource, javaStubCompilationUnit, stubSource);
558             } else {
559                 // DotGroovyFile is in groovyworkarounds package.
560                 // org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile
561                 // I need to import it or use reflection? It is compiled code in this project, so I can import it.
562                 org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile dotGroovyFile = new org.codehaus.gmavenplus.groovyworkarounds.DotGroovyFile(stubSource).setScriptExtensions(scriptExtensions);
563                 invokeMethod(addSource, javaStubCompilationUnit, dotGroovyFile);
564             }
565         }
566     }
567 
568     protected boolean supportsStubGeneration() {
569         return ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_8_2);
570     }
571 
572     protected boolean supportsSettingExtensions() {
573         return ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_8_3) && (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_1_9_0_BETA1) || ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_1_9_0_BETA3));
574     }
575 
576     protected Object setupCompilationUnit(final Set<File> sources, final Class<?> compilerConfigurationClass, final Class<?> compilationUnitClass, final Class<?> groovyClassLoaderClass, final Object compilerConfiguration, final Object groovyClassLoader, final Object transformLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
577         Object compilationUnit;
578         if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_1_6_0)) {
579             compilationUnit = invokeConstructor(findConstructor(compilationUnitClass, compilerConfigurationClass, CodeSource.class, groovyClassLoaderClass, groovyClassLoaderClass), compilerConfiguration, null, groovyClassLoader, transformLoader);
580         } else {
581             compilationUnit = invokeConstructor(findConstructor(compilationUnitClass, compilerConfigurationClass, CodeSource.class, groovyClassLoaderClass), compilerConfiguration, null, groovyClassLoader);
582         }
583         log.debug("Adding Groovy to compile:");
584         Method addSourceMethod = findMethod(compilationUnitClass, "addSource", File.class);
585         for (File source : sources) {
586             log.debug("    " + source);
587             invokeMethod(addSourceMethod, compilationUnit, source);
588         }
589 
590         return compilationUnit;
591     }
592 
593     @SuppressWarnings({"rawtypes", "unchecked"})
594     protected Object setupCompilerConfiguration(final GroovyCompileConfiguration configuration, final Class<?> compilerConfigurationClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
595         Object compilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
596         if (configuration.getConfigScript() != null) {
597             if (!configuration.getConfigScript().exists()) {
598                 log.warn("Configuration script file (" + configuration.getConfigScript().getAbsolutePath() + ") doesn't exist. Ignoring configScript parameter.");
599             } else if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_1_0_BETA1)) {
600                 log.warn("Requested to use configScript, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_1_0_BETA1 + " or newer). Ignoring configScript parameter.");
601             } else {
602                 Class<?> bindingClass = classWrangler.getClass("groovy.lang.Binding");
603                 Class<?> importCustomizerClass = classWrangler.getClass("org.codehaus.groovy.control.customizers.ImportCustomizer");
604                 Class<?> groovyShellClass = classWrangler.getClass("groovy.lang.GroovyShell");
605 
606                 Object binding = invokeConstructor(findConstructor(bindingClass));
607                 invokeMethod(findMethod(bindingClass, "setVariable", String.class, Object.class), binding, "configuration", compilerConfiguration);
608                 Object shellCompilerConfiguration = invokeConstructor(findConstructor(compilerConfigurationClass));
609                 Object importCustomizer = invokeConstructor(findConstructor(importCustomizerClass));
610                 invokeMethod(findMethod(importCustomizerClass, "addStaticStar", String.class), importCustomizer, "org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder");
611                 List compilationCustomizers = (List) invokeMethod(findMethod(compilerConfigurationClass, "getCompilationCustomizers"), shellCompilerConfiguration);
612                 compilationCustomizers.add(importCustomizer);
613                 Object shell = invokeConstructor(findConstructor(groovyShellClass, ClassLoader.class, bindingClass, compilerConfigurationClass), classWrangler.getClassLoader(), binding, shellCompilerConfiguration);
614                 log.debug("Using configuration script " + configuration.getConfigScript() + " for compilation.");
615                 invokeMethod(findMethod(groovyShellClass, "evaluate", File.class), shell, configuration.getConfigScript());
616             }
617         }
618         invokeMethod(findMethod(compilerConfigurationClass, "setDebug", boolean.class), compilerConfiguration, configuration.isDebug());
619         invokeMethod(findMethod(compilerConfigurationClass, "setVerbose", boolean.class), compilerConfiguration, configuration.isVerbose());
620         invokeMethod(findMethod(compilerConfigurationClass, "setWarningLevel", int.class), compilerConfiguration, configuration.getWarningLevel());
621         invokeMethod(findMethod(compilerConfigurationClass, "setTolerance", int.class), compilerConfiguration, configuration.getTolerance());
622         invokeMethod(findMethod(compilerConfigurationClass, "setTargetBytecode", String.class), compilerConfiguration, translateJavacTargetToTargetBytecode(configuration.getTargetBytecode()));
623         if (configuration.isPreviewFeatures()) {
624             if (isJavaSupportPreviewFeatures()) {
625                 if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_7) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_BETA1))) {
626                     log.warn("Requested to use preview features, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_5_7 + "/" + GROOVY_3_0_0_BETA1 + " or newer. No 2.6 version is supported. Ignoring previewFeatures parameter.");
627                 } else {
628                     invokeMethod(findMethod(compilerConfigurationClass, "setPreviewFeatures", boolean.class), compilerConfiguration, configuration.isPreviewFeatures());
629                 }
630             } else {
631                 log.warn("Requested to use to use preview features, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring previewFeatures parameter.");
632             }
633         }
634         if (configuration.getSourceEncoding() != null) {
635             invokeMethod(findMethod(compilerConfigurationClass, "setSourceEncoding", String.class), compilerConfiguration, configuration.getSourceEncoding());
636         }
637         invokeMethod(findMethod(compilerConfigurationClass, "setTargetDirectory", String.class), compilerConfiguration, configuration.getCompileOutputDirectory().getAbsolutePath());
638         if (configuration.isInvokeDynamic() || ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA1)) {
639             if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_0_0_BETA3)) {
640                 if (classWrangler.isGroovyIndy()) {
641                     if (isJavaSupportIndy()) {
642                         Map<String, Boolean> optimizationOptions = (Map<String, Boolean>) invokeMethod(findMethod(compilerConfigurationClass, "getOptimizationOptions"), compilerConfiguration);
643                         optimizationOptions.put("indy", true);
644                         optimizationOptions.put("int", false);
645                         log.info("invokedynamic enabled.");
646                     } else {
647                         log.warn("Requested to use to use invokedynamic, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring invokeDynamic parameter.");
648                     }
649                 } else {
650                     log.warn("Requested to use invokedynamic, but your Groovy version doesn't support it (must use have indy classifier). Ignoring invokeDynamic parameter.");
651                 }
652             } else {
653                 log.warn("Requested to use invokeDynamic, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_0_0_BETA3 + " or newer). Ignoring invokeDynamic parameter.");
654             }
655         }
656         if (configuration.isParameters()) {
657             if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_5_0_ALPHA1)) {
658                 if (isJavaSupportParameters()) {
659                     invokeMethod(findMethod(compilerConfigurationClass, "setParameters", boolean.class), compilerConfiguration, configuration.isParameters());
660                 } else {
661                     log.warn("Requested to use to use parameters, but your Java version (" + getJavaVersionString() + ") doesn't support it. Ignoring parameters parameter.");
662                 }
663             } else {
664                 log.warn("Requested to use parameters, but your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support it (must be " + GROOVY_2_5_0_ALPHA1 + " or newer). Ignoring parameters parameter.");
665             }
666         }
667         if (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_3_0_5)) {
668             if ((configuration.getParallelParsing() == null && ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA1)) || (configuration.getParallelParsing() != null && configuration.getParallelParsing())) {
669                 Map<String, Boolean> optimizationOptions = (Map<String, Boolean>) invokeMethod(findMethod(compilerConfigurationClass, "getOptimizationOptions"), compilerConfiguration);
670                 optimizationOptions.put("parallelParse", true);
671                 log.info("Parallel parsing enabled.");
672             } else {
673                 log.info("Parallel parsing disabled.");
674             }
675         }
676 
677         return compilerConfiguration;
678     }
679 
680     protected void verifyGroovyVersionSupportsTargetBytecode(String targetBytecode) {
681         if ("1.5".equals(targetBytecode) || "5".equals(targetBytecode) || "1.6".equals(targetBytecode) || "6".equals(targetBytecode) || "1.7".equals(targetBytecode) || "7".equals(targetBytecode) || "1.8".equals(targetBytecode) || "8".equals(targetBytecode) || "1.9".equals(targetBytecode) || "9".equals(targetBytecode) || "10".equals(targetBytecode)) {
682             if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1)) {
683                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " isn't accepted by Groovy " + GROOVY_5_0_0_ALPHA1 + " or newer.");
684             }
685         }
686 
687         if ("25".equals(targetBytecode)) {
688             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_27)) {
689                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_27 + " or newer.");
690             }
691             if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA13)) {
692                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA13 + " or newer.");
693             }
694         } else if ("24".equals(targetBytecode)) {
695             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_24)) {
696                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_24 + " or newer.");
697             }
698             if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA11)) {
699                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA11 + " or newer.");
700             }
701         } else if ("23".equals(targetBytecode)) {
702             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_21)) {
703                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_21 + " or newer.");
704             }
705             if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA8)) {
706                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA8 + " or newer.");
707             }
708         } else if ("22".equals(targetBytecode)) {
709             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_16)) {
710                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_16 + " or newer.");
711             }
712             if (ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_5_0_0_ALPHA3)) {
713                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_5_0_0_ALPHA3 + " or newer.");
714             }
715         } else if ("21".equals(targetBytecode)) {
716             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_11)) {
717                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_11 + " or newer.");
718             }
719         } else if ("20".equals(targetBytecode)) {
720             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_6)) {
721                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_6 + " or newer.");
722             }
723         } else if ("19".equals(targetBytecode)) {
724             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_2)) {
725                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_2 + " or newer.");
726             }
727         } else if ("18".equals(targetBytecode)) {
728             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_0_BETA1)) {
729                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_4_0_0_BETA1 + " or newer.");
730             }
731         } else if ("17".equals(targetBytecode)) {
732             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_8) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_4_0_0_ALPHA3))) {
733                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_8 + "/" + GROOVY_4_0_0_ALPHA3 + " or newer.");
734             }
735         } else if ("16".equals(targetBytecode)) {
736             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_6)) {
737                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_6 + " or newer.");
738             }
739         } else if ("15".equals(targetBytecode)) {
740             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_3)) {
741                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_3 + " or newer.");
742             }
743         } else if ("14".equals(targetBytecode)) {
744             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_BETA2)) {
745                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_3_0_0_BETA2 + " or newer.");
746             }
747         } else if ("13".equals(targetBytecode)) {
748             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_7) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_BETA1))) {
749                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_7 + "/" + GROOVY_3_0_0_BETA1 + " or newer. No 2.6 version is supported.");
750             }
751         } else if ("12".equals(targetBytecode) || "11".equals(targetBytecode) || "10".equals(targetBytecode)) {
752             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_3) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA4))) {
753                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_3_0_0_ALPHA4 + " or newer. No 2.6 version is supported.");
754             }
755         } else if ("9".equals(targetBytecode) || "1.9".equals(targetBytecode)) {
756             if (!classWrangler.isGroovyIndy() && (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_3)
757                     || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA4))
758                     || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA2)))) {
759                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_2_6_0_ALPHA4 + "/" + GROOVY_3_0_0_ALPHA2 + " or newer.");
760             } else if (classWrangler.isGroovyIndy() && (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_5_3) || (ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), GROOVY_2_6_0_ALPHA1) && ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_3_0_0_ALPHA4)))) {
761                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_5_3 + "/" + GROOVY_3_0_0_ALPHA4 + " or newer. No 2.6 version is supported.");
762             }
763         } else if ("8".equals(targetBytecode) || "1.8".equals(targetBytecode)) {
764             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_3_3)) {
765                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_3_3 + " or newer.");
766             }
767         } else if ("7".equals(targetBytecode) || "1.7".equals(targetBytecode) || "6".equals(targetBytecode) || "1.6".equals(targetBytecode)) {
768             if (ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), GROOVY_2_1_3)) {
769                 throw new IllegalArgumentException("Target bytecode " + targetBytecode + " requires Groovy " + GROOVY_2_1_3 + " or newer.");
770             }
771         } else if (!"5".equals(targetBytecode) && !"1.5".equals(targetBytecode) && !"4".equals(targetBytecode) && !"1.4".equals(targetBytecode)) {
772             throw new IllegalArgumentException("Unrecognized target bytecode: '" + targetBytecode + "'. This check can be skipped with 'skipBytecodeCheck', but this may result in a different target bytecode being used.");
773         }
774     }
775 
776     public static String translateJavacTargetToTargetBytecode(String targetBytecode) {
777         Map<String, String> javacTargetToTargetBytecode = new HashMap<>();
778         javacTargetToTargetBytecode.put("5", "1.5");
779         javacTargetToTargetBytecode.put("6", "1.6");
780         javacTargetToTargetBytecode.put("7", "1.7");
781         javacTargetToTargetBytecode.put("8", "1.8");
782         javacTargetToTargetBytecode.put("1.9", "9");
783         return javacTargetToTargetBytecode.getOrDefault(targetBytecode, targetBytecode);
784     }
785 
786     protected boolean isJavaSupportIndy() {
787         return getJavaVersion().compareTo(JAVA_1_7, false) >= 0;
788     }
789 
790     protected boolean isJavaSupportPreviewFeatures() {
791         return getJavaVersion().compareTo(JAVA_12, false) >= 0;
792     }
793 
794     protected boolean isJavaSupportParameters() {
795         return getJavaVersion().compareTo(JAVA_1_8, false) >= 0;
796     }
797 
798     protected Version getJavaVersion() {
799         return Version.parseFromString(getJavaVersionString());
800     }
801 
802     protected String getJavaVersionString() {
803         return System.getProperty("java.version");
804     }
805 
806 }