File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\util\ArgumentListBuilder.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
5 * Alan Harder, Yahoo! Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 package hudson.util;
26
27 import hudson.Launcher;
28 import hudson.Util;
29
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Arrays;
33 import java.util.Map;
34 import java.util.BitSet;
35 import java.util.Properties;
36 import java.util.Map.Entry;
37 import java.io.Serializable;
38 import java.io.File;
39 import java.io.IOException;
40 import java.util.Set;
41
42 /**
43 * Used to build up arguments for a process invocation.
44 *
45 * @author Kohsuke Kawaguchi
46 */
47 public class ArgumentListBuilder implements Serializable, Cloneable {
48 private final List<String> args = new ArrayList<String>();
49 /**
50 * Bit mask indicating arguments that shouldn't be echoed-back (e.g., password)
51 */
52 private BitSet mask = new BitSet();
53
54 public ArgumentListBuilder() {
55 }
56
57 public ArgumentListBuilder(String... args) {
58 add(args);
59 }
60
61 public ArgumentListBuilder add(Object a) {
62 return add(a.toString(), false);
63 }
64
65 /**
66 * @since 1.378
67 */
68 public ArgumentListBuilder add(Object a, boolean mask) {
69 return add(a.toString(), mask);
70 }
71
72 public ArgumentListBuilder add(File f) {
73 return add(f.getAbsolutePath(), false);
74 }
75
76 public ArgumentListBuilder add(String a) {
77 return add(a,false);
78 }
79
80 /**
81 * Optionally hide this part of the command line from being printed to the log.
82 * @param a a command argument
83 * @param mask true to suppress in output, false to print normally
84 * @return this
85 * @see hudson.Launcher.ProcStarter#masks(boolean[])
86 * @see Launcher#maskedPrintCommandLine(List, boolean[], FilePath)
87 * @since 1.378
88 */
89 public ArgumentListBuilder add(String a, boolean mask) {
90 if(a!=null) {
91 if(mask) {
92 this.mask.set(args.size());
93 }
94 args.add(a);
95 }
96 return this;
97 }
98
99 public ArgumentListBuilder prepend(String... args) {
100 // left-shift the mask
101 BitSet nm = new BitSet(this.args.size()+args.length);
102 for(int i=0; i<this.args.size(); i++)
103 nm.set(i+args.length, mask.get(i));
104 mask = nm;
105
106 this.args.addAll(0, Arrays.asList(args));
107 return this;
108 }
109
110 /**
111 * Adds an argument by quoting it.
112 * This is necessary only in a rare circumstance,
113 * such as when adding argument for ssh and rsh.
114 *
115 * Normal process invocations don't need it, because each
116 * argument is treated as its own string and never merged into one.
117 */
118 public ArgumentListBuilder addQuoted(String a) {
119 return add('"'+a+'"', false);
120 }
121
122 /**
123 * @since 1.378
124 */
125 public ArgumentListBuilder addQuoted(String a, boolean mask) {
126 return add('"'+a+'"', mask);
127 }
128
129 public ArgumentListBuilder add(String... args) {
130 for (String arg : args) {
131 add(arg);
132 }
133 return this;
134 }
135
136 /**
137 * Decomposes the given token into multiple arguments by splitting via whitespace.
138 */
139 public ArgumentListBuilder addTokenized(String s) {
140 if(s==null) return this;
141 add(Util.tokenize(s));
142 return this;
143 }
144
145 /**
146 * @since 1.378
147 */
148 public ArgumentListBuilder addKeyValuePair(String prefix, String key, String value, boolean mask) {
149 if(key==null) return this;
150 add(((prefix==null)?"-D":prefix)+key+'='+value, mask);
151 return this;
152 }
153
154 /**
155 * Adds key value pairs as "-Dkey=value -Dkey=value ..."
156 *
157 * <tt>-D</tt> portion is configurable as the 'prefix' parameter.
158 * @since 1.114
159 */
160 public ArgumentListBuilder addKeyValuePairs(String prefix, Map<String,String> props) {
161 for (Entry<String,String> e : props.entrySet())
162 addKeyValuePair(prefix, e.getKey(), e.getValue(), false);
163 return this;
164 }
165
166 /**
167 * Adds key value pairs as "-Dkey=value -Dkey=value ..." with masking.
168 *
169 * @param prefix
170 * Configures the -D portion of the example. Defaults to -D if null.
171 * @param props
172 * The map of key/value pairs to add
173 * @param propsToMask
174 * Set containing key names to mark as masked in the argument list. Key
175 * names that do not exist in the set will be added unmasked.
176 * @since 1.378
177 */
178 public ArgumentListBuilder addKeyValuePairs(String prefix, Map<String,String> props, Set<String> propsToMask) {
179 for (Entry<String,String> e : props.entrySet()) {
180 addKeyValuePair(prefix, e.getKey(), e.getValue(), (propsToMask == null) ? false : propsToMask.contains(e.getKey()));
181 }
182 return this;
183 }
184
185 /**
186 * Adds key value pairs as "-Dkey=value -Dkey=value ..." by parsing a given string using {@link Properties}.
187 *
188 * @param prefix
189 * The '-D' portion of the example. Defaults to -D if null.
190 * @param properties
191 * The persisted form of {@link Properties}. For example, "abc=def\nghi=jkl". Can be null, in which
192 * case this method becomes no-op.
193 * @param vr
194 * {@link VariableResolver} to be performed on the values.
195 * @since 1.262
196 */
197 public ArgumentListBuilder addKeyValuePairsFromPropertyString(String prefix, String properties, VariableResolver vr) throws IOException {
198 if(properties==null) return this;
199
200 for (Entry<Object,Object> entry : Util.loadProperties(properties).entrySet()) {
201 addKeyValuePair(prefix, (String)entry.getKey(), Util.replaceMacro(entry.getValue().toString(),vr), false);
202 }
203 return this;
204 }
205
206 /**
207 * Adds key value pairs as "-Dkey=value -Dkey=value ..." by parsing a given string using {@link Properties} with masking.
208 *
209 * @param prefix
210 * The '-D' portion of the example. Defaults to -D if null.
211 * @param properties
212 * The persisted form of {@link Properties}. For example, "abc=def\nghi=jkl". Can be null, in which
213 * case this method becomes no-op.
214 * @param vr
215 * {@link VariableResolver} to be performed on the values.
216 * @param propsToMask
217 * Set containing key names to mark as masked in the argument list. Key
218 * names that do not exist in the set will be added unmasked.
219 * @since 1.378
220 */
221 public ArgumentListBuilder addKeyValuePairsFromPropertyString(String prefix, String properties, VariableResolver vr, Set<String> propsToMask) throws IOException {
222 if(properties==null) return this;
223
224 for (Entry<Object,Object> entry : Util.loadProperties(properties).entrySet()) {
225 addKeyValuePair(prefix, (String)entry.getKey(), Util.replaceMacro(entry.getValue().toString(),vr), (propsToMask == null) ? false : propsToMask.contains((String)entry.getKey()));
226 }
227 return this;
228 }
229
230 public String[] toCommandArray() {
231 return args.toArray(new String[args.size()]);
232 }
233
234 @Override
235 public ArgumentListBuilder clone() {
236 ArgumentListBuilder r = new ArgumentListBuilder();
237 r.args.addAll(this.args);
238 r.mask = (BitSet) this.mask.clone();
239 return r;
240 }
241
242 /**
243 * Re-initializes the arguments list.
244 */
245 public void clear() {
246 args.clear();
247 mask.clear();
248 }
249
250 public List<String> toList() {
251 return args;
252 }
253
254 /**
255 * Just adds quotes around args containing spaces, but no other special characters,
256 * so this method should generally be used only for informational/logging purposes.
257 */
258 public String toStringWithQuote() {
259 StringBuilder buf = new StringBuilder();
260 for (String arg : args) {
261 if(buf.length()>0) buf.append(' ');
262
263 if(arg.indexOf(' ')>=0 || arg.length()==0)
264 buf.append('"').append(arg).append('"');
265 else
266 buf.append(arg);
267 }
268 return buf.toString();
269 }
270
271 /**
272 * Wrap command in a CMD.EXE call so we can return the exit code (ERRORLEVEL).
273 * This method takes care of escaping special characters in the command, which
274 * is needed since the command is now passed as a string to the CMD.EXE shell.
275 * This is done as follows:
276 * Wrap arguments in double quotes if they contain any of:
277 * space *?,;^&<>|"
278 * and if escapeVars is true, % followed by a letter.
279 * <br/> When testing from command prompt, these characters also need to be
280 * prepended with a ^ character: ^&<>| -- however, invoking cmd.exe from
281 * Jenkins does not seem to require this extra escaping so it is not added by
282 * this method.
283 * <br/> A " is prepended with another " character. Note: Windows has issues
284 * escaping some combinations of quotes and spaces. Quotes should be avoided.
285 * <br/> If escapeVars is true, a % followed by a letter has that letter wrapped
286 * in double quotes, to avoid possible variable expansion.
287 * ie, %foo% becomes "%"f"oo%". The second % does not need special handling
288 * because it is not followed by a letter. <br/>
289 * Example: "-Dfoo=*abc?def;ghi^jkl&mno<pqr>stu|vwx""yz%"e"nd"
290 * @param escapeVars True to escape %VAR% references; false to leave these alone
291 * so they may be expanded when the command is run
292 * @return new ArgumentListBuilder that runs given command through cmd.exe /C
293 * @since 1.386
294 */
295 public ArgumentListBuilder toWindowsCommand(boolean escapeVars) {
296 StringBuilder quotedArgs = new StringBuilder();
297 boolean quoted, percent;
298 for (String arg : args) {
299 quoted = percent = false;
300 for (int i = 0; i < arg.length(); i++) {
301 char c = arg.charAt(i);
302 if (!quoted && (c == ' ' || c == '*' || c == '?' || c == ',' || c == ';')) {
303 quoted = startQuoting(quotedArgs, arg, i);
304 }
305 else if (c == '^' || c == '&' || c == '<' || c == '>' || c == '|') {
306 if (!quoted) quoted = startQuoting(quotedArgs, arg, i);
307 // quotedArgs.append('^'); See note in javadoc above
308 }
309 else if (c == '"') {
310 if (!quoted) quoted = startQuoting(quotedArgs, arg, i);
311 quotedArgs.append('"');
312 }
313 else if (percent && escapeVars
314 && ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {
315 if (!quoted) quoted = startQuoting(quotedArgs, arg, i);
316 quotedArgs.append('"').append(c);
317 c = '"';
318 }
319 percent = (c == '%');
320 if (quoted) quotedArgs.append(c);
321 }
322 if (quoted) quotedArgs.append('"'); else quotedArgs.append(arg);
323 quotedArgs.append(' ');
324 }
325 // (comment copied from old code in hudson.tasks.Ant)
326 // on Windows, executing batch file can't return the correct error code,
327 // so we need to wrap it into cmd.exe.
328 // double %% is needed because we want ERRORLEVEL to be expanded after
329 // batch file executed, not before. This alone shows how broken Windows is...
330 quotedArgs.append("&& exit %%ERRORLEVEL%%");
331 return new ArgumentListBuilder().add("cmd.exe", "/C").addQuoted(quotedArgs.toString());
332 }
333
334 /**
335 * Calls toWindowsCommand(false)
336 * @see #toWindowsCommand(boolean)
337 */
338 public ArgumentListBuilder toWindowsCommand() {
339 return toWindowsCommand(false);
340 }
341
342 private static boolean startQuoting(StringBuilder buf, String arg, int atIndex) {
343 buf.append('"').append(arg.substring(0, atIndex));
344 return true;
345 }
346
347 /**
348 * Returns true if there are any masked arguments.
349 * @return true if there are any masked arguments; false otherwise
350 */
351 public boolean hasMaskedArguments() {
352 return mask.length()>0;
353 }
354
355 /**
356 * Returns an array of booleans where the masked arguments are marked as true
357 * @return an array of booleans.
358 */
359 public boolean[] toMaskArray() {
360 boolean[] mask = new boolean[args.size()];
361 for( int i=0; i<mask.length; i++)
362 mask[i] = this.mask.get(i);
363 return mask;
364 }
365
366 /**
367 * Add a masked argument
368 * @param string the argument
369 */
370 public void addMasked(String string) {
371 add(string, true);
372 }
373
374 /**
375 * Debug/error message friendly output.
376 */
377 public String toString() {
378 StringBuilder buf = new StringBuilder();
379 for (int i=0; i<args.size(); i++) {
380 String arg = args.get(i);
381 if (mask.get(i))
382 arg = "******";
383
384 if(buf.length()>0) buf.append(' ');
385
386 if(arg.indexOf(' ')>=0 || arg.length()==0)
387 buf.append('"').append(arg).append('"');
388 else
389 buf.append(arg);
390 }
391 return buf.toString();
392 }
393
394 private static final long serialVersionUID = 1L;
395 }
396