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.anttasks;
20  
21  import java.util.ArrayList;
22  import java.util.Comparator;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.stream.Collectors;
26  
27  import org.apache.commons.cli.HelpFormatter;
28  import org.apache.commons.cli.Option;
29  import org.apache.commons.cli.Options;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.rat.commandline.Arg;
32  import org.apache.rat.config.exclusion.StandardCollection;
33  import org.apache.rat.help.AbstractHelp;
34  import org.apache.rat.tools.AbstractOption;
35  import org.apache.rat.tools.AntOption;
36  import org.apache.rat.utils.DefaultLog;
37  import org.apache.rat.utils.Log;
38  
39  import static java.lang.String.format;
40  
41  /**
42   * A basic Ant task that generates a report on all files specified by the nested
43   * resource collection(s).
44   *
45   * <p>
46   * IHeaderMatcher(s) can be specified as nested elements as well.
47   * </p>
48   *
49   * <p>
50   * The attribute <code>format</code> defines the output format and can take the
51   * values
52   * <ul>
53   * <li>xml - Rat's native XML output.</li>
54   * <li>styled - transforms the XML output using the given stylesheet. The
55   * stylesheet attribute must be set as well if this attribute is used.</li>
56   * <li>plain - plain text using Rat's built-in stylesheet. This is the
57   * default.</li>
58   * </ul>
59   */
60  public class Help extends BaseAntTask {
61  
62      /**
63       * Constructor.
64       */
65      public Help() {
66          // replace the logger only if it has not already been set.
67          Log oldLog = DefaultLog.getInstance();
68          if (oldLog instanceof DefaultLog) {
69              DefaultLog.setInstance(new Logger());
70              DefaultLog.getInstance().setLevel(oldLog.getLevel());
71          }
72      }
73  
74      /**
75       * Generates the help.
76       */
77      @Override
78      public void execute() {
79          org.apache.rat.help.Help helpObj = new org.apache.rat.help.Help(System.out) {
80              /**
81               * Print the usage to the specific PrintWriter.
82               * @param opts The defined options.
83               */
84              public void printUsage(final Options opts) {
85                  String syntax = "ant {target executing task <rat:help/>}";
86                  AntHelpFormatter helpFormatter = new AntHelpFormatter();
87                  helpFormatter.setOptPrefix("<");
88  
89                  helpFormatter.printHelp(writer, helpFormatter.getWidth(), syntax, AbstractHelp.header("Available options"), opts,
90                          helpFormatter.getLeftPadding(), helpFormatter.getDescPadding(), header("Argument Types"));
91  
92                  String argumentPadding = printArgumentTypes();
93  
94                  writer.println(header("Standard Collections"));
95                  for (StandardCollection sc : StandardCollection.values()) {
96                      writer.format("%n<%s>%n", sc.name());
97                      helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
98                              argumentPadding + sc.desc());
99                      helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
100                             argumentPadding + "File patterns: " + (sc.patterns().isEmpty() ? "<none>" : String.join(", ", sc.patterns())));
101                     helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
102                             argumentPadding + "Provides a path matcher: " + sc.hasStaticDocumentNameMatcher());
103                     helpFormatter.printWrapped(writer, helpFormatter.getWidth(), helpFormatter.getLeftPadding() + HELP_PADDING + HELP_PADDING,
104                             argumentPadding + "Provides a file processor: " + sc.fileProcessorBuilder().hasNext());
105                 }
106                 writer.println("\nA path matcher will match specific information about the file.");
107                 writer.println("\nA file processor will process the associated \"ignore\" file for include and exclude directives");
108 
109                 writer.println(header("Notes"));
110                 int idx = 1;
111                 for (String note : NOTES) {
112                     writer.format("%d. %s%n", idx++, note);
113                 }
114                 writer.flush();
115             }
116         };
117 
118         helpObj.printUsage(Arg.getOptions());
119     }
120 
121     @Override
122     public void log(final String msg, final int msgLevel) {
123         if (getProject() != null) {
124             getProject().log(msg, msgLevel);
125         } else {
126             DefaultLog.createDefault().log(Report.fromProjectLevel(msgLevel), msg);
127         }
128     }
129 
130     @Override
131     public void log(final String msg, final Throwable t, final int msgLevel) {
132         if (getProject() == null) {
133             log(Log.formatLogEntry(msg, t), msgLevel);
134         } else {
135             getProject().log(this, msg, t, msgLevel);
136         }
137     }
138 
139     /**
140      * A facade for the Logger provided by Ant.
141      */
142     private final class Logger implements Log {
143         @Override
144         public Level getLevel() {
145             return Level.DEBUG;
146         }
147 
148         @Override
149         public void log(final Level level, final String message, final Throwable throwable) {
150             log(level, Log.formatLogEntry(message, throwable));
151         }
152 
153         @Override
154         public void log(final Level level, final String msg) {
155             Help.this.log(msg, Report.toProjectLevel(level));
156         }
157     }
158 
159     public static class AntHelpFormatter extends HelpFormatter {
160         public AntHelpFormatter() {
161             super();
162             setWidth(180);
163         }
164         @Override
165         public Comparator<Option> getOptionComparator() {
166             return Comparator.comparing(Option::getLongOpt);
167         }
168 
169         @Override
170         protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad) {
171             final String lpad = createPadding(leftPad);
172             final String dpad = createPadding(descPad);
173             // first create list containing only <lpad>-a,--aaa where
174             // -a is opt and --aaa is long opt; in parallel look for
175             // the longest opt string this list will be then used to
176             // sort options ascending
177             String optionTitle = " -- Option --";
178             String exampleTitle = " -- Example --";
179             String descriptionTitle = " -- Description --";
180             int max = optionTitle.length();
181             int maxExample = exampleTitle.length();
182             final List<AntOption> optList = options.getOptions().stream().filter(Option::hasLongOpt)
183                     .map(AntOption::new).collect(Collectors.toList());
184             if (getOptionComparator() != null) {
185                 optList.sort(Comparator.comparing(AbstractOption::getName));
186             }
187             List<String> exampleList = new ArrayList<>();
188             for (final AntOption option : optList) {
189                 String argName = StringUtils.defaultIfEmpty(option.getArgName(), "value");
190                 String fmt = option.isAttribute() ? "<rat:report %s='%s'>" : "<%1$s>%2$s</%1$s>";
191                 String example = format(fmt, option.getName(), argName);
192                 exampleList.add(example);
193                 max = Math.max(option.cleanupName().length(), max);
194                 maxExample = Math.max(example.length(), maxExample);
195             }
196 
197             sb.append(optionTitle).append(createPadding(max - optionTitle.length()))
198                     .append(dpad)
199                     .append(exampleTitle).append(createPadding(maxExample - exampleTitle.length()))
200                     .append(dpad)
201                     .append(descriptionTitle)
202                     .append(getNewLine());
203 
204             int x = 0;
205             for (final Iterator<AntOption> it = optList.iterator(); it.hasNext();) {
206                 final AntOption option = it.next();
207                 String name = option.cleanupName();
208                 String example = exampleList.get(x++);
209 
210                 final StringBuilder optBuf = new StringBuilder(name);
211                 if (name.length() < max) {
212                     optBuf.append(createPadding(max - name.length()));
213                 }
214                 optBuf.append(dpad).append(example);
215                 if (example.length() < maxExample) {
216                     optBuf.append(createPadding(maxExample - example.length()));
217                 }
218                 optBuf.append(dpad);
219                 final int nextLineTabStop = max + maxExample + 2 * descPad;
220                 if (option.isDeprecated()) {
221                     optBuf.append(option.getDeprecated());
222                 } else if (option.getDescription() != null) {
223                     optBuf.append(option.getDescription());
224                 }
225                 renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
226                 if (it.hasNext()) {
227                     sb.append(getNewLine());
228                 }
229             }
230             return sb;
231         }
232     }
233 }