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