001 /*
002 * Copyright 2008-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2013 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.util.args;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Arrays;
027 import java.util.Collections;
028 import java.util.HashSet;
029 import java.util.Iterator;
030 import java.util.List;
031 import java.util.Set;
032 import java.util.regex.Matcher;
033 import java.util.regex.Pattern;
034
035 import com.unboundid.util.Mutable;
036 import com.unboundid.util.ThreadSafety;
037 import com.unboundid.util.ThreadSafetyLevel;
038
039 import static com.unboundid.util.StaticUtils.*;
040 import static com.unboundid.util.args.ArgsMessages.*;
041
042
043
044 /**
045 * This class defines an argument that is intended to hold one or more string
046 * values. String arguments must take values. By default, any value will be
047 * allowed, but it is possible to restrict the set of values so that only values
048 * from a specified set (ignoring differences in capitalization) will be
049 * allowed.
050 */
051 @Mutable()
052 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
053 public final class StringArgument
054 extends Argument
055 {
056 /**
057 * The serial version UID for this serializable class.
058 */
059 private static final long serialVersionUID = 1088032496970585118L;
060
061
062
063 // The set of values assigned to this argument.
064 private final ArrayList<String> values;
065
066 // The list of default values that will be used if no values were provided.
067 private final List<String> defaultValues;
068
069 // A regular expression that may be enforced for values of this argument.
070 private volatile Pattern valueRegex;
071
072 // The set of allowed values for this argument.
073 private final Set<String> allowedValues;
074
075 // A human-readable explanation of the regular expression pattern.
076 private volatile String valueRegexExplanation;
077
078
079
080 /**
081 * Creates a new string argument with the provided information. There will
082 * not be any default values, nor will there be any restriction on values that
083 * may be assigned to this argument.
084 *
085 * @param shortIdentifier The short identifier for this argument. It may
086 * not be {@code null} if the long identifier is
087 * {@code null}.
088 * @param longIdentifier The long identifier for this argument. It may
089 * not be {@code null} if the short identifier is
090 * {@code null}.
091 * @param isRequired Indicates whether this argument is required to
092 * be provided.
093 * @param maxOccurrences The maximum number of times this argument may be
094 * provided on the command line. A value less than
095 * or equal to zero indicates that it may be present
096 * any number of times.
097 * @param valuePlaceholder A placeholder to display in usage information to
098 * indicate that a value must be provided. It must
099 * not be {@code null}.
100 * @param description A human-readable description for this argument.
101 * It must not be {@code null}.
102 *
103 * @throws ArgumentException If there is a problem with the definition of
104 * this argument.
105 */
106 public StringArgument(final Character shortIdentifier,
107 final String longIdentifier, final boolean isRequired,
108 final int maxOccurrences, final String valuePlaceholder,
109 final String description)
110 throws ArgumentException
111 {
112 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
113 valuePlaceholder, description, null, (List<String>) null);
114 }
115
116
117
118 /**
119 * Creates a new string argument with the provided information. There will
120 * not be any default values.
121 *
122 * @param shortIdentifier The short identifier for this argument. It may
123 * not be {@code null} if the long identifier is
124 * {@code null}.
125 * @param longIdentifier The long identifier for this argument. It may
126 * not be {@code null} if the short identifier is
127 * {@code null}.
128 * @param isRequired Indicates whether this argument is required to
129 * be provided.
130 * @param maxOccurrences The maximum number of times this argument may be
131 * provided on the command line. A value less than
132 * or equal to zero indicates that it may be present
133 * any number of times.
134 * @param valuePlaceholder A placeholder to display in usage information to
135 * indicate that a value must be provided. It must
136 * not be {@code null}.
137 * @param description A human-readable description for this argument.
138 * It must not be {@code null}.
139 * @param allowedValues The set of allowed values for this argument, or
140 * {@code null} if it should not be restricted.
141 *
142 * @throws ArgumentException If there is a problem with the definition of
143 * this argument.
144 */
145 public StringArgument(final Character shortIdentifier,
146 final String longIdentifier, final boolean isRequired,
147 final int maxOccurrences, final String valuePlaceholder,
148 final String description,
149 final Set<String> allowedValues)
150 throws ArgumentException
151 {
152 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
153 valuePlaceholder, description, allowedValues, (List<String>) null);
154 }
155
156
157
158 /**
159 * Creates a new string argument with the provided information. There will
160 * not be any restriction on values that may be assigned to this argument.
161 *
162 * @param shortIdentifier The short identifier for this argument. It may
163 * not be {@code null} if the long identifier is
164 * {@code null}.
165 * @param longIdentifier The long identifier for this argument. It may
166 * not be {@code null} if the short identifier is
167 * {@code null}.
168 * @param isRequired Indicates whether this argument is required to
169 * be provided.
170 * @param maxOccurrences The maximum number of times this argument may be
171 * provided on the command line. A value less than
172 * or equal to zero indicates that it may be present
173 * any number of times.
174 * @param valuePlaceholder A placeholder to display in usage information to
175 * indicate that a value must be provided. It must
176 * not be {@code null}.
177 * @param description A human-readable description for this argument.
178 * It must not be {@code null}.
179 * @param defaultValue The default value that will be used for this
180 * argument if no values are provided. It may be
181 * {@code null} if there should not be a default
182 * value.
183 *
184 * @throws ArgumentException If there is a problem with the definition of
185 * this argument.
186 */
187 public StringArgument(final Character shortIdentifier,
188 final String longIdentifier, final boolean isRequired,
189 final int maxOccurrences, final String valuePlaceholder,
190 final String description,
191 final String defaultValue)
192 throws ArgumentException
193 {
194 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
195 valuePlaceholder, description, null,
196 ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
197 }
198
199
200
201 /**
202 * Creates a new string argument with the provided information. There will
203 * not be any restriction on values that may be assigned to this argument.
204 *
205 * @param shortIdentifier The short identifier for this argument. It may
206 * not be {@code null} if the long identifier is
207 * {@code null}.
208 * @param longIdentifier The long identifier for this argument. It may
209 * not be {@code null} if the short identifier is
210 * {@code null}.
211 * @param isRequired Indicates whether this argument is required to
212 * be provided.
213 * @param maxOccurrences The maximum number of times this argument may be
214 * provided on the command line. A value less than
215 * or equal to zero indicates that it may be present
216 * any number of times.
217 * @param valuePlaceholder A placeholder to display in usage information to
218 * indicate that a value must be provided. It must
219 * not be {@code null}.
220 * @param description A human-readable description for this argument.
221 * It must not be {@code null}.
222 * @param defaultValues The set of default values that will be used for
223 * this argument if no values are provided.
224 *
225 * @throws ArgumentException If there is a problem with the definition of
226 * this argument.
227 */
228 public StringArgument(final Character shortIdentifier,
229 final String longIdentifier, final boolean isRequired,
230 final int maxOccurrences, final String valuePlaceholder,
231 final String description,
232 final List<String> defaultValues)
233 throws ArgumentException
234 {
235 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
236 valuePlaceholder, description, null, defaultValues);
237 }
238
239
240
241 /**
242 * Creates a new string argument with the provided information.
243 *
244 * @param shortIdentifier The short identifier for this argument. It may
245 * not be {@code null} if the long identifier is
246 * {@code null}.
247 * @param longIdentifier The long identifier for this argument. It may
248 * not be {@code null} if the short identifier is
249 * {@code null}.
250 * @param isRequired Indicates whether this argument is required to
251 * be provided.
252 * @param maxOccurrences The maximum number of times this argument may be
253 * provided on the command line. A value less than
254 * or equal to zero indicates that it may be present
255 * any number of times.
256 * @param valuePlaceholder A placeholder to display in usage information to
257 * indicate that a value must be provided. It must
258 * not be {@code null}.
259 * @param description A human-readable description for this argument.
260 * It must not be {@code null}.
261 * @param allowedValues The set of allowed values for this argument, or
262 * {@code null} if it should not be restricted.
263 * @param defaultValue The default value that will be used for this
264 * argument if no values are provided. It may be
265 * {@code null} if there should not be a default
266 * value.
267 *
268 * @throws ArgumentException If there is a problem with the definition of
269 * this argument.
270 */
271 public StringArgument(final Character shortIdentifier,
272 final String longIdentifier, final boolean isRequired,
273 final int maxOccurrences, final String valuePlaceholder,
274 final String description,
275 final Set<String> allowedValues,
276 final String defaultValue)
277 throws ArgumentException
278 {
279 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
280 valuePlaceholder, description, allowedValues,
281 ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
282 }
283
284
285
286 /**
287 * Creates a new string argument with the provided information.
288 *
289 * @param shortIdentifier The short identifier for this argument. It may
290 * not be {@code null} if the long identifier is
291 * {@code null}.
292 * @param longIdentifier The long identifier for this argument. It may
293 * not be {@code null} if the short identifier is
294 * {@code null}.
295 * @param isRequired Indicates whether this argument is required to
296 * be provided.
297 * @param maxOccurrences The maximum number of times this argument may be
298 * provided on the command line. A value less than
299 * or equal to zero indicates that it may be present
300 * any number of times.
301 * @param valuePlaceholder A placeholder to display in usage information to
302 * indicate that a value must be provided. It must
303 * not be {@code null}.
304 * @param description A human-readable description for this argument.
305 * It must not be {@code null}.
306 * @param allowedValues The set of allowed values for this argument, or
307 * {@code null} if it should not be restricted.
308 * @param defaultValues The set of default values that will be used for
309 * this argument if no values are provided.
310 *
311 * @throws ArgumentException If there is a problem with the definition of
312 * this argument.
313 */
314 public StringArgument(final Character shortIdentifier,
315 final String longIdentifier, final boolean isRequired,
316 final int maxOccurrences, final String valuePlaceholder,
317 final String description,
318 final Set<String> allowedValues,
319 final List<String> defaultValues)
320 throws ArgumentException
321 {
322 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
323 valuePlaceholder, description);
324
325 if (valuePlaceholder == null)
326 {
327 throw new ArgumentException(ERR_ARG_MUST_TAKE_VALUE.get(
328 getIdentifierString()));
329 }
330
331 if ((allowedValues == null) || allowedValues.isEmpty())
332 {
333 this.allowedValues = null;
334 }
335 else
336 {
337 final HashSet<String> lowerValues =
338 new HashSet<String>(allowedValues.size());
339 for (final String s : allowedValues)
340 {
341 lowerValues.add(toLowerCase(s));
342 }
343 this.allowedValues = Collections.unmodifiableSet(lowerValues);
344 }
345
346 if ((defaultValues == null) || defaultValues.isEmpty())
347 {
348 this.defaultValues = null;
349 }
350 else
351 {
352 this.defaultValues = Collections.unmodifiableList(defaultValues);
353 }
354
355 values = new ArrayList<String>();
356 valueRegex = null;
357 valueRegexExplanation = null;
358 }
359
360
361
362 /**
363 * Creates a new string argument that is a "clean" copy of the provided source
364 * argument.
365 *
366 * @param source The source argument to use for this argument.
367 */
368 private StringArgument(final StringArgument source)
369 {
370 super(source);
371
372 allowedValues = source.allowedValues;
373 defaultValues = source.defaultValues;
374 valueRegex = source.valueRegex;
375 valueRegexExplanation = source.valueRegexExplanation;
376 values = new ArrayList<String>();
377 }
378
379
380
381 /**
382 * Retrieves the set of allowed values for this argument, if applicable.
383 *
384 * @return The set of allowed values for this argument, or {@code null} if
385 * there is no restriction on the allowed values.
386 */
387 public Set<String> getAllowedValues()
388 {
389 return allowedValues;
390 }
391
392
393
394 /**
395 * Retrieves the list of default values for this argument, which will be used
396 * if no values were provided.
397 *
398 * @return The list of default values for this argument, or {@code null} if
399 * there are no default values.
400 */
401 public List<String> getDefaultValues()
402 {
403 return defaultValues;
404 }
405
406
407
408 /**
409 * Retrieves the regular expression that values of this argument will be
410 * required to match, if any.
411 *
412 * @return The regular expression that values of this argument will be
413 * required to match, or {@code null} if none is defined.
414 */
415 public Pattern getValueRegex()
416 {
417 return valueRegex;
418 }
419
420
421
422 /**
423 * Retrieves a human-readable explanation of the regular expression pattern
424 * that may be required to match any provided values, if any.
425 *
426 * @return A human-readable explanation of the regular expression pattern, or
427 * {@code null} if none is available.
428 */
429 public String getValueRegexExplanation()
430 {
431 return valueRegexExplanation;
432 }
433
434
435
436 /**
437 * Specifies the regular expression that values of this argument will be
438 * required to match, if any.
439 *
440 * @param valueRegex The regular expression that values of this argument
441 * will be required to match. It may be {@code null} if
442 * no pattern matching should be required.
443 * @param explanation A human-readable explanation for the pattern which may
444 * be used to clarify the kinds of values that are
445 * acceptable. It may be {@code null} if no pattern
446 * matching should be required, or if the regular
447 * expression pattern should be sufficiently clear for
448 * the target audience.
449 */
450 public void setValueRegex(final Pattern valueRegex,
451 final String explanation)
452 {
453 this.valueRegex = valueRegex;
454 valueRegexExplanation = explanation;
455 }
456
457
458
459 /**
460 * {@inheritDoc}
461 */
462 @Override()
463 protected void addValue(final String valueString)
464 throws ArgumentException
465 {
466 final String lowerValue = toLowerCase(valueString);
467 if (allowedValues != null)
468 {
469 if (! allowedValues.contains(lowerValue))
470 {
471 throw new ArgumentException(ERR_ARG_VALUE_NOT_ALLOWED.get(
472 valueString, getIdentifierString()));
473 }
474 }
475
476 if (values.size() >= getMaxOccurrences())
477 {
478 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
479 getIdentifierString()));
480 }
481
482 if (valueRegex != null)
483 {
484 final Matcher matcher = valueRegex.matcher(valueString);
485 if (! matcher.matches())
486 {
487 final String pattern = valueRegex.pattern();
488 if (valueRegexExplanation == null)
489 {
490 throw new ArgumentException(
491 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITHOUT_EXPLANATION.get(
492 valueString, getIdentifierString(), pattern));
493 }
494 else
495 {
496 throw new ArgumentException(
497 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITH_EXPLANATION.get(
498 valueString, getIdentifierString(), pattern,
499 valueRegexExplanation));
500 }
501 }
502 }
503
504 values.add(valueString);
505 }
506
507
508
509 /**
510 * Retrieves the value for this argument, or the default value if none was
511 * provided. If this argument has multiple values, then the first will be
512 * returned.
513 *
514 * @return The value for this argument, or the default value if none was
515 * provided, or {@code null} if it does not have any values or
516 * default values.
517 */
518 public String getValue()
519 {
520 if (values.isEmpty())
521 {
522 if ((defaultValues == null) || defaultValues.isEmpty())
523 {
524 return null;
525 }
526 else
527 {
528 return defaultValues.get(0);
529 }
530 }
531
532 return values.get(0);
533 }
534
535
536
537 /**
538 * Retrieves the set of values for this argument, or the default values if
539 * none were provided.
540 *
541 * @return The set of values for this argument, or the default values if none
542 * were provided.
543 */
544 public List<String> getValues()
545 {
546 if (values.isEmpty() && (defaultValues != null))
547 {
548 return defaultValues;
549 }
550
551 return Collections.unmodifiableList(values);
552 }
553
554
555
556 /**
557 * {@inheritDoc}
558 */
559 @Override()
560 protected boolean hasDefaultValue()
561 {
562 return ((defaultValues != null) && (! defaultValues.isEmpty()));
563 }
564
565
566
567 /**
568 * {@inheritDoc}
569 */
570 @Override()
571 public String getDataTypeName()
572 {
573 return INFO_STRING_TYPE_NAME.get();
574 }
575
576
577
578 /**
579 * {@inheritDoc}
580 */
581 @Override()
582 public String getValueConstraints()
583 {
584 StringBuilder buffer = null;
585
586 if (valueRegex != null)
587 {
588 buffer = new StringBuilder();
589 final String pattern = valueRegex.pattern();
590 if ((valueRegexExplanation == null) ||
591 (valueRegexExplanation.length() == 0))
592 {
593 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get(
594 pattern));
595 }
596 else
597 {
598 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get(
599 pattern, valueRegexExplanation));
600 }
601 }
602
603 if ((allowedValues != null) && (! allowedValues.isEmpty()))
604 {
605 if (buffer == null)
606 {
607 buffer = new StringBuilder();
608 }
609 else
610 {
611 buffer.append(" ");
612 }
613
614 buffer.append(INFO_STRING_CONSTRAINTS_ALLOWED_VALUE.get());
615 buffer.append(" ");
616
617 final Iterator<String> iterator = allowedValues.iterator();
618 while (iterator.hasNext())
619 {
620 buffer.append('\'');
621 buffer.append(iterator.next());
622 buffer.append('\'');
623
624 if (iterator.hasNext())
625 {
626 buffer.append(", ");
627 }
628 }
629 buffer.append('.');
630 }
631
632 if (buffer == null)
633 {
634 return null;
635 }
636 else
637 {
638 return buffer.toString();
639 }
640 }
641
642
643
644 /**
645 * {@inheritDoc}
646 */
647 @Override()
648 public StringArgument getCleanCopy()
649 {
650 return new StringArgument(this);
651 }
652
653
654
655 /**
656 * {@inheritDoc}
657 */
658 @Override()
659 public void toString(final StringBuilder buffer)
660 {
661 buffer.append("StringArgument(");
662 appendBasicToStringInfo(buffer);
663
664 if ((allowedValues != null) && (! allowedValues.isEmpty()))
665 {
666 buffer.append(", allowedValues={");
667 final Iterator<String> iterator = allowedValues.iterator();
668 while (iterator.hasNext())
669 {
670 buffer.append('\'');
671 buffer.append(iterator.next());
672 buffer.append('\'');
673
674 if (iterator.hasNext())
675 {
676 buffer.append(", ");
677 }
678 }
679 buffer.append('}');
680 }
681
682 if (valueRegex != null)
683 {
684 buffer.append(", valueRegex='");
685 buffer.append(valueRegex.pattern());
686 buffer.append('\'');
687
688 if (valueRegexExplanation != null)
689 {
690 buffer.append(", valueRegexExplanation='");
691 buffer.append(valueRegexExplanation);
692 buffer.append('\'');
693 }
694 }
695
696 if ((defaultValues != null) && (! defaultValues.isEmpty()))
697 {
698 if (defaultValues.size() == 1)
699 {
700 buffer.append(", defaultValue='");
701 buffer.append(defaultValues.get(0));
702 }
703 else
704 {
705 buffer.append(", defaultValues={");
706
707 final Iterator<String> iterator = defaultValues.iterator();
708 while (iterator.hasNext())
709 {
710 buffer.append('\'');
711 buffer.append(iterator.next());
712 buffer.append('\'');
713
714 if (iterator.hasNext())
715 {
716 buffer.append(", ");
717 }
718 }
719
720 buffer.append('}');
721 }
722 }
723
724 buffer.append(')');
725 }
726 }