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.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.FileWriter;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.OutputStreamWriter;
28  import java.io.Writer;
29  import java.nio.charset.StandardCharsets;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import org.apache.commons.cli.Option;
37  import org.apache.commons.io.IOUtils;
38  import org.apache.commons.io.LineIterator;
39  import org.apache.commons.lang3.StringUtils;
40  import org.apache.commons.text.WordUtils;
41  import org.apache.rat.OptionCollection;
42  import org.apache.rat.documentation.options.AntOption;
43  import org.apache.rat.utils.CasedString;
44  import org.apache.rat.utils.CasedString.StringCase;
45  
46  import static java.lang.String.format;
47  
48  /**
49   * A simple tool to convert CLI options into an Ant report base class.
50   */
51  public final class AntGenerator {
52  
53      /**
54       * A map of type patterns for that type.
55       */
56      private static final Map<OptionCollection.ArgumentType, GenerateType> GENERATE_TYPE_MAP = new HashMap<>();
57  
58      static {
59          String defaultFmt = "        public void add%$1s(String %2$s) {%n" +
60                  "            addArg(%%1$s, %2$s);%n" +
61                  "        }%n%n";
62          GenerateType generateType;
63          for (OptionCollection.ArgumentType type : OptionCollection.ArgumentType.values()) {
64              switch (type) {
65                  case FILE:
66                  case DIRORARCHIVE:
67                      generateType = new GenerateType("fileset") {
68                          protected String getMethodFormat(final AntOption antOption) {
69                              return "        public void addConfiguredFileset(FileSet fileSet) {\n" +
70                                      "            for (Resource resource : fileSet) {\n" +
71                                      "                if (resource.isFilesystemOnly()) {\n" +
72                                      "                    addArg(%1$s, ((FileResource) resource).getFile().getAbsolutePath());\n" +
73                                      "                }\n" +
74                                      "            }\n" +
75                                      "        }\n\n";
76                          }
77                      };
78                      break;
79                  case NONE:
80                      generateType = new GenerateType("") {
81                          protected String getMethodFormat(final AntOption antOption) {
82                              return "";
83                          }
84                      };
85                      break;
86                  case STANDARDCOLLECTION:
87                      generateType = new GenerateType("Std");
88                      break;
89                  case EXPRESSION:
90                      generateType = new GenerateType("Expr");
91                      break;
92                  case COUNTERPATTERN:
93                      generateType = new GenerateType("Cntr");
94                      break;
95                  case LICENSEID:
96                  case FAMILYID:
97                      generateType = new GenerateType("Lst");
98                      break;
99                  default:
100                     generateType = new GenerateType(type.getDisplayName()) {
101 
102                         protected String getMethodFormat(final AntOption antOption) {
103                             return String.format(defaultFmt, innerClass, WordUtils.uncapitalize(antOption.getArgName()));
104                         }
105                     };
106             }
107             GENERATE_TYPE_MAP.put(type, generateType);
108         }
109     }
110 
111     private AntGenerator() { }
112 
113 
114     /**
115      * Gets the key for the Args array.
116      * @param option the option to get the key for.
117      * @return the key for the option.
118      */
119     private static String argsKey(final Option option) {
120         return StringUtils.defaultIfEmpty(option.getLongOpt(), option.getOpt());
121     }
122 
123     /**
124      * Creates a base class for an Ant task.
125      * Requires 3 arguments:
126      * <ol>
127      *     <li>the package name for the class</li>
128      *     <li>the simple class name</li>
129      *     <li>the directory in which to write the class file.</li>
130      * </ol>
131      * @param args the arguments.
132      * @throws IOException on error.
133      */
134     public static void main(final String[] args) throws IOException {
135         if (args == null || args.length < 3) {
136             System.err.println("At least three arguments are required: package, simple class name, target directory.");
137             return;
138         }
139 
140         String packageName = args[0];
141         String className = args[1];
142         String destDir = args[2];
143 
144         List<AntOption> options = AntOption.getAntOptions();
145 
146         String pkgName = String.join(File.separator, new CasedString(StringCase.DOT, packageName).getSegments());
147         File file = new File(new File(new File(destDir), pkgName), className + ".java");
148         file.getParentFile().mkdirs();
149         try (InputStream template = AntGenerator.class.getResourceAsStream("/Ant.tpl");
150              FileWriter writer = new FileWriter(file);
151              ByteArrayOutputStream bos = new ByteArrayOutputStream();
152              OutputStreamWriter customClasses = new OutputStreamWriter(bos)) {
153             if (template == null) {
154                 throw new RuntimeException("Template /Ant.tpl not found");
155             }
156             LineIterator iter = IOUtils.lineIterator(new InputStreamReader(template, StandardCharsets.UTF_8));
157             while (iter.hasNext()) {
158                 String line = iter.next();
159                 switch (line.trim()) {
160                     case "${static}":
161                         for (Map.Entry<String, String> entry : AntOption.getRenameMap().entrySet()) {
162                             writer.append(format("        xlateName.put(\"%s\", \"%s\");%n", entry.getKey(), entry.getValue()));
163                         }
164                         for (Option option : AntOption.getFilteredOptions()) {
165                             writer.append(format("        unsupportedArgs.add(\"%s\");%n", argsKey(option)));
166                         }
167                         for (AntOption option : options) {
168                             if (option.isDeprecated()) {
169                                 writer.append(format("        deprecatedArgs.put(\"%s\", \"%s\");%n", argsKey(option.getOption()),
170                                         format("Use of deprecated option '%s'. %s", option.getName(), option.getDeprecated())));
171                             }
172                         }
173                         break;
174                     case "${methods}":
175                         writeMethods(writer, options, customClasses);
176                         break;
177                     case "${package}":
178                         writer.append(format("package %s;%n", packageName));
179                         break;
180                     case "${constructor}":
181                         writer.append(format("    protected %s() {\n" +
182                                 "        setDeprecationReporter();\n" +
183                                 "    }%n", className));
184                         break;
185                     case "${class}":
186                         writer.append(format("public abstract class %s extends Task {%n", className));
187                         break;
188                     case "${classes}":
189                         customClasses.flush();
190                         customClasses.close();
191                         writer.write(bos.toString());
192                         break;
193                     case "${commonArgs}":
194                         try (InputStream argsTpl = MavenGenerator.class.getResourceAsStream("/Args.tpl")) {
195                             if (argsTpl == null) {
196                                 throw new RuntimeException("Args.tpl not found");
197                             }
198                             IOUtils.copy(argsTpl, writer, StandardCharsets.UTF_8);
199                         }
200                         break;
201                     default:
202                         writer.append(line).append(System.lineSeparator());
203                         break;
204                 }
205             }
206         }
207     }
208 
209     private static void writeMethods(final FileWriter writer, final List<AntOption> options, final Writer customClasses) throws IOException {
210         for (AntOption option : options) {
211 
212             if (option.isAttribute()) {
213                 writer.append(option.getComment(true));
214                 writer.append(format("    public void %s {%n%s%n    }%n%n", option.getAttributeFunctionName(), getAttributeBody(option)));
215             } else {
216                 customClasses.append(option.getComment(false));
217                 customClasses.append(format("    public %1$s create%1$s() {%n        return new %1$s();%n    }%n%n",
218                         WordUtils.capitalize(option.getName())));
219                 customClasses.append(getElementClass(option));
220             }
221         }
222     }
223 
224     private static String getAttributeBody(final AntOption option) {
225         return option.hasArg() ? format("        setArg(%s, %s);%n", option.keyValue(), option.getName())
226             : format("        if (%1$s) { setArg(%2$s, null); } else { removeArg(%2$s); }", option.getName(), option.keyValue());
227     }
228 
229     private static String getElementClass(final AntOption option) {
230 
231         String elementConstructor =
232                 "    public class %1$s {\n" +
233                         "        %1$s() { }\n\n";
234 
235         String funcName = WordUtils.capitalize(option.getName());
236         StringBuilder result = new StringBuilder(format(elementConstructor, funcName));
237         Set<AntOption> implementedOptions = new HashSet<>();
238         implementedOptions.add(option);
239         option.convertedFrom().stream().filter(o -> !AntOption.getUnsupportedOptions().contains(o)).forEach(opt -> implementedOptions.add(new AntOption(opt)));
240         implementedOptions.forEach(o -> result.append(GENERATE_TYPE_MAP.get(o.getArgType()).getPattern(option, o)));
241         result.append(format("    }%n"));
242 
243         return result.toString();
244     }
245 
246     public static class GenerateType {
247         /** the inner class name text */
248         protected final String innerClass;
249 
250         GenerateType(final String innerClass) {
251             this.innerClass = innerClass;
252         }
253 
254         protected String getMethodFormat(final AntOption antOption) {
255             return String.format("        public void addConfigured%1$s(%1$s %%2$s) {\n" +
256                     "            addArg(%%1$s, %%2$s.value);\n" +
257                     "        }\n\n", innerClass);
258         }
259 
260         public String getPattern(final AntOption delegateOption, final AntOption antOption) {
261             if (delegateOption.isAttribute()) {
262                 String fmt = "<rat:report %s='%s' />";
263                 return format(fmt, delegateOption.getName(), antOption.hasArg() ? antOption.getArgName() : "true");
264             } else {
265                 return format(getMethodFormat(antOption), antOption.keyValue(),
266                         WordUtils.uncapitalize(antOption.getArgName()));
267             }
268         }
269     }
270 
271 }