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 }