View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.rat.tools;
20  
21  import java.io.File;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.nio.charset.StandardCharsets;
27  import java.util.Map;
28  import java.util.function.Supplier;
29  
30  import org.apache.commons.cli.Option;
31  import org.apache.commons.io.IOUtils;
32  import org.apache.commons.io.LineIterator;
33  import org.apache.commons.lang3.StringUtils;
34  import org.apache.commons.text.StringEscapeUtils;
35  import org.apache.commons.text.WordUtils;
36  import org.apache.rat.OptionCollection;
37  import org.apache.rat.documentation.options.MavenOption;
38  import org.apache.rat.documentation.options.MavenOptionCollection;
39  import org.apache.rat.utils.CasedString;
40  import org.apache.rat.utils.CasedString.StringCase;
41  
42  import static java.lang.String.format;
43  
44  /**
45   * A simple tool to convert CLI options to a Maven Mojo base class.
46   */
47  public final class MavenGenerator {
48  
49      private MavenGenerator() {
50      }
51  
52      private static String argsKey(final Option option) {
53          return StringUtils.defaultIfEmpty(option.getLongOpt(), option.getOpt());
54      }
55  
56      /**
57       * Creates the Maven MojoClass
58       * Requires 3 arguments:
59       * <ol>
60       *     <li>the package name for the class</li>
61       *     <li>the simple class name</li>
62       *     <li>the directory in which to write the class file.</li>
63       * </ol>
64       *
65       * @param args the arguments
66       * @throws IOException on error
67       */
68      public static void main(final String[] args) throws IOException {
69          if (args == null || args.length < 3) {
70              System.err.println("At least three arguments are required: package, simple class name, target directory.");
71              return;
72          }
73  
74          String packageName = args[0];
75          String className = args[1];
76          String destDir = args[2];
77          String pkgName = String.join(File.separator, new CasedString(StringCase.DOT, packageName).getSegments());
78          File file = new File(new File(new File(destDir), pkgName), className + ".java");
79          System.out.println("Creating " + file);
80          file.getParentFile().mkdirs();
81          try (InputStream template = MavenGenerator.class.getResourceAsStream("/Maven.tpl");
82               FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8)) {
83              if (template == null) {
84                  throw new RuntimeException("Template /Maven.tpl not found");
85              }
86              LineIterator iter = IOUtils.lineIterator(new InputStreamReader(template, StandardCharsets.UTF_8));
87              while (iter.hasNext()) {
88                  String line = iter.next();
89                  switch (line.trim()) {
90                      case "${static}":
91                          for (Map.Entry<?, ?> entry : MavenOptionCollection.getRenameMap().entrySet()) {
92                              writer.append(format("        xlateName.put(\"%s\", \"%s\");%n", entry.getKey(), entry.getValue()));
93                          }
94                          for (Option option : MavenOptionCollection.INSTANCE.getUnsupportedOptions().getOptions()) {
95                              writer.append(format("        unsupportedArgs.add(\"%s\");%n", argsKey(option)));
96                          }
97                          for (MavenOption option : MavenOptionCollection.INSTANCE.getMappedOptions().filter(MavenOption::isDeprecated).toList()) {
98                              writer.append(format("        deprecatedArgs.put(\"%s\", \"%s\");%n", argsKey(option.getOption()),
99                                      format("Use of deprecated option '%s'. %s", option.getName(), option.getDeprecated())));
100                         }
101                         break;
102                     case "${methods}":
103                         writeMethods(writer);
104                         break;
105                     case "${package}":
106                         writer.append(format("package %s;%n", packageName));
107                         break;
108                     case "${constructor}":
109                         writer.append(format("""
110                                     protected %s() {
111                                         setDeprecationReporter();
112                                     }%n""", className));
113                         break;
114                     case "${class}":
115                         writer.append(format("public abstract class %s extends AbstractMojo {%n", className));
116                         break;
117                     case "${commonArgs}":
118                         try (InputStream argsTpl = MavenGenerator.class.getResourceAsStream("/Args.tpl")) {
119                             if (argsTpl == null) {
120                                 throw new RuntimeException("Args.tpl not found");
121                             }
122                             IOUtils.copy(argsTpl, writer, StandardCharsets.UTF_8);
123                         }
124                         break;
125                     default:
126                         writer.append(line).append(System.lineSeparator());
127                         break;
128                 }
129             }
130         }
131     }
132 
133     private static String getComment(final MavenOption option) {
134         String desc = option.getDescription();
135         if (desc == null) {
136             throw new IllegalStateException(format("Description for %s may not be null", option.getName()));
137         }
138         if (!desc.contains(".")) {
139             throw new IllegalStateException(format("First sentence of description for %s must end with a '.'", option.getName()));
140         }
141         String arg;
142         if (option.hasArg()) {
143             arg = desc.substring(desc.indexOf(" ") + 1, desc.indexOf(".") + 1);
144             arg = WordUtils.capitalize(arg.substring(0, 1)) + arg.substring(1);
145         } else {
146             arg = "The state";
147         }
148         if (option.hasArg() && option.getArgName() != null) {
149             Supplier<String> sup = OptionCollection.getArgumentTypes().get(option.getArgName());
150             if (sup == null) {
151                 throw new IllegalStateException(format("Argument type %s must be in OptionCollection.ARGUMENT_TYPES", option.getArgName()));
152             }
153             desc = format("%s Argument%s should be %s%s. (See Argument Types for clarification)", desc, option.hasArgs() ? "s" : "",
154                     option.hasArgs() ? "" : "a ", option.getArgName());
155         }
156         StringBuilder sb = new StringBuilder()
157             .append(format("    /**%n     * %s%n     * @param %s %s%n", StringEscapeUtils.escapeHtml4(desc),
158                     option.getName(),  StringEscapeUtils.escapeHtml4(arg)));
159         if (option.isDeprecated()) {
160             sb.append(format("     * @deprecated %s%n", StringEscapeUtils.escapeHtml4(option.getDeprecated())));
161         }
162         return sb.append(format("     */%n")).toString();
163     }
164 
165     private static void writeMethods(final FileWriter writer) throws IOException {
166         for (MavenOption option : MavenOptionCollection.INSTANCE.getMappedOptions().toList()) {
167             writer.append(getComment(option))
168                     .append(option.getMethodSignature("    ", option.hasArgs())).append(" {").append(System.lineSeparator())
169                     .append(getBody(option))
170                     .append("    }").append(System.lineSeparator());
171             if (option.hasArgs()) {
172                 // create single argument method
173                 writer.append(getComment(option))
174                         .append(option.getMethodSignature("    ", false)).append(" {").append(System.lineSeparator())
175                         .append(getBody(option))
176                         .append("    }").append(System.lineSeparator());
177             }
178         }
179     }
180 
181     private static String getBody(final MavenOption option) {
182         if (option.hasArg()) {
183             return format("        %sArg(\"%s\", %s);%n", option.hasArgs() ? "add" : "set", option.keyValue(), option.getName());
184         } else {
185             return format("        if (%1$s) {%n            setArg(\"%2$s\", null);%n" +
186                             "        } else {%n            removeArg(\"%2$s\");%n        }%n",
187                     option.getName(), option.keyValue());
188         }
189     }
190 }