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.ui;
20  
21  import java.util.HashMap;
22  import java.util.Locale;
23  import java.util.Map;
24  import java.util.Optional;
25  import java.util.regex.Matcher;
26  import java.util.regex.Pattern;
27  
28  import org.apache.commons.cli.Option;
29  import org.apache.commons.lang3.StringUtils;
30  import org.apache.rat.OptionCollection;
31  import org.apache.rat.utils.CasedString;
32  
33  import static java.lang.String.format;
34  
35  /**
36   * Abstract class that provides the framework for UI-specific RAT options.
37   * In this context UI option means an option expressed in the specific UI.
38   * @param <T> the concrete implementation of AbstractOption.
39   */
40  public abstract class UIOption<T extends UIOption<T>> {
41      /** The pattern to match CLI options in text */
42      protected static final Pattern PATTERN = Pattern.compile("-?-([A-Za-z0-9]+-?)+"); // NOSONAR
43      /** The actual UI-specific name for the option */
44      protected final Option option;
45      /** The name for the option */
46      protected final CasedString name;
47      /** The argument type for this option */
48      protected final OptionCollection.ArgumentType argumentType;
49      /** The AbstractOptionCollection associated with this AbstractOption */
50      protected final UIOptionCollection<T> optionCollection;
51  
52      /**
53       * Constructor.
54       *
55       * @param option The CLI option
56       * @param name the UI-specific name for the option
57       */
58      protected <C extends UIOptionCollection<T>> UIOption(final C optionCollection, final Option option, final CasedString name) {
59          this.optionCollection = optionCollection;
60          this.option = option;
61          this.name = name;
62          OptionCollection.ArgumentType argType;
63          if (option.hasArg()) {
64              if (option.getArgName() == null) {
65                  argType = OptionCollection.ArgumentType.ARG;
66              } else {
67                  // extract the name of the argument type.
68                  try {
69                      argType = OptionCollection.ArgumentType.valueOf(option.getArgName().toUpperCase(Locale.ROOT));
70                  } catch (IllegalArgumentException e) {
71                      argType = OptionCollection.ArgumentType.ARG;
72                  }
73              }
74          } else {
75              argType = OptionCollection.ArgumentType.NONE;
76          }
77          this.argumentType = argType;
78      }
79  
80      /**
81       * Gets the AbstractOptionCollection that this option is a member of.
82       * @return the AbstractOptionCollection that this option is a member of.
83       */
84      public final <X extends UIOptionCollection<T>> X getOptionCollection() {
85          return (X) optionCollection;
86      }
87  
88      /**
89       * Gets the option this abstract option is wrapping.
90       * @return the original Option.
91       */
92      public final Option getOption() {
93          return option;
94      }
95  
96      /**
97       * Return default value.
98       * @return default value or {@code null} if no argument given.
99       */
100     public final String getDefaultValue() {
101         return optionCollection.defaultValue(option);
102     }
103 
104     /**
105      * Provide means to wrap the given option depending on the UI-specific option implementation.
106      * @param option The CLI option
107      * @return the cleaned up option name.
108      */
109     protected abstract String cleanupName(Option option);
110 
111     /**
112      * Gets an example of how to use this option in the native UI.
113      * @return An example of how to use this option in the native UI.
114      */
115     public abstract String getExample();
116 
117     /**
118      * Replaces CLI pattern options with implementation specific pattern options.
119      * @param str the string to clean.
120      * @return the string with CLI names replaced with implementation specific names.
121      */
122     public String cleanup(final String str) {
123         String workingStr = str;
124         if (StringUtils.isNotBlank(workingStr)) {
125             Map<String, String> maps = new HashMap<>();
126             Matcher matcher = PATTERN.matcher(workingStr);
127             while (matcher.find()) {
128                 String key = matcher.group();
129                 String optKey = (1 == key.indexOf('-', 1)) ?  key.substring(2) : key.substring(1);
130                 Optional<Option> maybeResult = getOptionCollection().getOptions().getOptions().stream()
131                                 .filter(o -> optKey.equals(o.getOpt()) || optKey.equals(o.getLongOpt())).findFirst();
132                 maybeResult.ifPresent(value -> maps.put(key, cleanupName(value)));
133             }
134             for (Map.Entry<String, String> entry : maps.entrySet()) {
135                 workingStr = workingStr.replaceAll(Pattern.quote(format("%s", entry.getKey())), entry.getValue());
136             }
137         }
138         return workingStr;
139     }
140 
141     /**
142      * Gets the implementation specific name for the native UI.
143      * @return The implementation specific name for the native UI.
144      */
145     public final String getName() {
146         return name.toString();
147     }
148 
149     /**
150      * Gets the CasedString version of the name for the native UI.
151      * @return the CasedString version of the name for the native UI.
152      */
153     public final CasedString getCasedName() {
154         return name;
155     }
156 
157     /**
158      * return a string showing long and short options if they are available. Will return
159      * a string.
160      * @return A string showing long and short options if they are available. Never {@code null}.
161      */
162     public abstract String getText();
163 
164     /**
165      * Gets the description in implementation specific format.
166      *
167      * @return the description or an empty string.
168      */
169     public final String getDescription() {
170         return cleanup(option.getDescription());
171     }
172 
173     /**
174      * Gets the simple class name for the data type for this option.
175      * Normally "String".
176      * @return the simple class name for the type.
177      */
178     public final Class<?> getType() {
179         return option.hasArg() ? ((Class<?>) option.getType()) : boolean.class;
180     }
181 
182     /**
183      * Gets the argument name if there is one.
184      * @return the Argument name
185      */
186     public final String getArgName() {
187         return argumentType.getDisplayName();
188     }
189 
190     /**
191      * Gets the argument type if there is one.
192      * @return the Argument name
193      */
194     public final OptionCollection.ArgumentType getArgType() {
195         return argumentType;
196     }
197 
198     /**
199      * Determines if the option is deprecated.
200      * @return {@code true} if the option is deprecated
201      */
202     public final boolean isDeprecated() {
203         return option.isDeprecated();
204     }
205 
206     /**
207      * Determines if the option is required.
208      * @return {@code true} if the option is required.
209      */
210     public final boolean isRequired() {
211         return option.isRequired();
212     }
213 
214     /**
215      * Determine if the enclosed option expects an argument.
216      * @return {@code true} if the enclosed option expects at least one argument.
217      */
218     public final boolean hasArg() {
219         return option.hasArg();
220     }
221 
222     /**
223      * Returns {@code true} if the option has multiple arguments.
224      * @return {@code true} if the option has multiple arguments.
225      */
226     public final boolean hasArgs() {
227         return option.hasArgs();
228     }
229 
230     /**
231      * The key value for the option.
232      * @return the key value for the CLI argument map.
233      */
234     public final String keyValue() {
235         return StringUtils.defaultIfEmpty(option.getLongOpt(), option.getOpt());
236     }
237 
238     /**
239      * Gets the deprecated string if the option is deprecated, or an empty string otherwise.
240      * @return the deprecated string if the option is deprecated, or an empty string otherwise.
241      */
242     public final String getDeprecated() {
243         return  option.isDeprecated() ? cleanup(StringUtils.defaultIfEmpty(option.getDeprecated().toString(), StringUtils.EMPTY)) : StringUtils.EMPTY;
244     }
245 }