View Javadoc
1   package org.codehaus.mojo.jaxb2.shared.arguments;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
23  import org.codehaus.mojo.jaxb2.shared.Validate;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  /**
29   * Utility class to build an array containing method arguments, as received from a command-line invocation of a tool.
30   *
31   * @author <a href="mailto:lj@jguru.se">Lennart J&ouml;relid</a>
32   */
33  public final class ArgumentBuilder {
34  
35      // Internal state
36      private final Object lock = new Object();
37      private static final int NOT_FOUND = -1;
38      private static final char DASH = '-';
39      private List<String> arguments = new ArrayList<String>();
40  
41      /**
42       * <p>Retrieves all arguments as a string array, usable by a method accepting a String[] for argument.
43       * This would be true of {@code public static void main(String[] args)}, as well as the entry points
44       * for both the XJC and the Schemagen tools.</p>
45       *
46       * @return an array holding all arguments in this ArgumentBuilder.
47       */
48      public String[] build() {
49  
50          synchronized (lock) {
51              final String[] toReturn = new String[arguments.size()];
52              return arguments.toArray(toReturn);
53          }
54      }
55  
56      /**
57       * <p>Adds a flag on the form {@code -someflag} to the list of arguments contained within this ArgumentBuilder.
58       * If the {@code flag} argument does not start with a dash ('-'), one will be prepended.</p>
59       * <p>Typical usage:</p>
60       * <pre><code>
61       *     argumentBuilder
62       *      .withFlag(someBooleanParameter, "foobar")
63       *      .withFlag(someOtherBooleanParameter, "gnat")
64       *      .withFlag(someThirdBooleanParameter, "gnu")
65       *      ....
66       * </code></pre>
67       *
68       * @param addFlag if {@code true}, the flag will be added to the underlying list of arguments
69       *                within this ArgumentBuilder.
70       * @param flag    The flag/argument to add. The flag must be a complete word, implying it
71       *                cannot contain whitespace.
72       * @return This ArgumentBuilder, for chaining.
73       */
74      public ArgumentBuilder withFlag(final boolean addFlag, final String flag) {
75  
76          // Bail out?
77          if (!addFlag) {
78              return this;
79          }
80  
81          // Check sanity
82          Validate.notEmpty(flag, "flag");
83          Validate.isTrue(!AbstractJaxbMojo.CONTAINS_WHITESPACE.matcher(flag).matches(),
84                  "Flags cannot contain whitespace. Got: [" + flag + "]");
85  
86          // Trim, and add the flag as an argument.
87          final String trimmed = flag.trim();
88          Validate.notEmpty(trimmed, "flag");
89  
90          // Prepend the DASH if required
91          final String toAdd = trimmed.charAt(0) != DASH ? DASH + trimmed : trimmed;
92  
93          // Assign the argument only if not already set.
94          if (getIndexForFlag(toAdd) == NOT_FOUND) {
95              synchronized (lock) {
96                  arguments.add(toAdd);
97              }
98          }
99  
100         // All done.
101         return this;
102     }
103 
104     /**
105      * <p>Adds a name and an argument on the form {@code -name value} to the list of arguments contained
106      * within this ArgumentBuilder. The two parts will yield 2 elements in the underlying argument list.
107      * If the {@code name} argument does not start with a dash ('-'), one will be prepended.</p>
108      * <p>Typical usage:</p>
109      * <pre><code>
110      *     // These values should be calculated as part of the business logic
111      *     final boolean addFooBar = true;
112      *     final boolean addGnat = true;
113      *     final boolean addGnu = false;
114      *
115      *     // Add all relevant arguments
116      *     argumentBuilder
117      *      .withNamedArgument(addFooBar, "foobar", "foobarValue")
118      *      .withNamedArgument(addGnat, "-gnat", "gnatValue")
119      *      .withNamedArgument(addGnu, "gnu", "gnuValue")
120      *      ....
121      * </code></pre>
122      *
123      * @param addNamedArgument if {@code true}, the named argument (name and value) will be added to
124      *                         the underlying list of arguments within this ArgumentBuilder.
125      * @param name             The name of the namedArgument to add. Cannot be empty.
126      * @param value            The value of the namedArgument to add.
127      * @return This ArgumentBuilder, for chaining.
128      */
129     public ArgumentBuilder withNamedArgument(final boolean addNamedArgument,
130                                              final String name,
131                                              final String value) {
132 
133         // Bail out?
134         if (!addNamedArgument) {
135             return this;
136         }
137 
138         // Check sanity
139         Validate.notEmpty(name, "name");
140         Validate.notEmpty(value, "value");
141 
142         // Trim the arguments, and validate again.
143         final String trimmedName = name.trim();
144         final String trimmedValue = value.trim();
145         Validate.notEmpty(trimmedName, "name");
146         Validate.notEmpty(trimmedValue, "value");
147 
148         // Add or update the name and value.
149         if (!updateValueForNamedArgument(name, value)) {
150             synchronized (lock) {
151                 withFlag(true, trimmedName);
152                 arguments.add(value);
153             }
154         }
155 
156         // All done.
157         return this;
158     }
159 
160     /**
161      * Convenience form for the {@code withNamedArgument} method, where a named argument is only added
162      * if the value is non-null and non-empty after trimming.
163      *
164      * @param name  The name of the namedArgument to add. Cannot be empty.
165      * @param value The value of the namedArgument to add.
166      * @return This ArgumentBuilder, for chaining.
167      * @see #withNamedArgument(boolean, String, String)
168      */
169     public ArgumentBuilder withNamedArgument(final String name, final String value) {
170 
171         // Check sanity
172         Validate.notEmpty(name, "name");
173 
174         // Only add a named argument if the value is non-empty.
175         if (value != null && !value.trim().isEmpty()) {
176             withNamedArgument(true, name, value.trim());
177         }
178 
179         // All done.
180         return this;
181     }
182 
183     /**
184      * Adds the supplied pre-compiled arguments in the same order as they were given.
185      *
186      * @param preCompiledArguments A non-null List holding pre-compiled arguments.
187      * @return This ArgumentBuilder, for chaining.
188      */
189     public ArgumentBuilder withPreCompiledArguments(final List<String> preCompiledArguments) {
190 
191         // Check sanity
192         Validate.notNull(preCompiledArguments, "preCompiledArguments");
193 
194         // Add the preCompiledArguments in the exact order they were given.
195         synchronized (lock) {
196             for (String current : preCompiledArguments) {
197                 arguments.add(current);
198             }
199         }
200 
201         // All done.
202         return this;
203     }
204 
205     //
206     // Private helpers
207     //
208 
209     private int getIndexForFlag(final String name) {
210 
211         // Check sanity
212         Validate.notEmpty(name, "name");
213 
214         for (int i = 0; i < arguments.size(); i++) {
215             if (arguments.get(i).equalsIgnoreCase(name)) {
216                 return i;
217             }
218         }
219 
220         // Not found.
221         return NOT_FOUND;
222     }
223 
224     private boolean updateValueForNamedArgument(final String name, final String newValue) {
225 
226         // Check sanity
227         Validate.notEmpty(name, "name");
228 
229         int flagIndex = getIndexForFlag(name);
230         if (flagIndex == NOT_FOUND) {
231 
232             // Nothing updated
233             return false;
234         }
235 
236         // Updating the value of the named argument.
237         int valueIndex = flagIndex + 1;
238         synchronized (lock) {
239             arguments.set(valueIndex, newValue);
240         }
241         return true;
242     }
243 }