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ö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 }