001 /*
002 * Copyright 2008-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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 argument value validators that have been registered for this argument.
067 private final List<ArgumentValueValidator> validators;
068
069 // The list of default values that will be used if no values were provided.
070 private final List<String> defaultValues;
071
072 // A regular expression that may be enforced for values of this argument.
073 private volatile Pattern valueRegex;
074
075 // The set of allowed values for this argument.
076 private final Set<String> allowedValues;
077
078 // A human-readable explanation of the regular expression pattern.
079 private volatile String valueRegexExplanation;
080
081
082
083 /**
084 * Creates a new string argument with the provided information. It will not
085 * be required, will permit at most one value, will use a default placeholder,
086 * will not have any default value, and will not place any restriction on
087 * values that may be assigned.
088 *
089 * @param shortIdentifier The short identifier for this argument. It may
090 * not be {@code null} if the long identifier is
091 * {@code null}.
092 * @param longIdentifier The long identifier for this argument. It may
093 * not be {@code null} if the short identifier is
094 * {@code null}.
095 * @param description A human-readable description for this argument.
096 * It must not be {@code null}.
097 *
098 * @throws ArgumentException If there is a problem with the definition of
099 * this argument.
100 */
101 public StringArgument(final Character shortIdentifier,
102 final String longIdentifier, final String description)
103 throws ArgumentException
104 {
105 this(shortIdentifier, longIdentifier, false, 1, null, description);
106 }
107
108
109
110 /**
111 * Creates a new string argument with the provided information. There will
112 * not be any default values, nor will there be any restriction on values that
113 * may be assigned to this argument.
114 *
115 * @param shortIdentifier The short identifier for this argument. It may
116 * not be {@code null} if the long identifier is
117 * {@code null}.
118 * @param longIdentifier The long identifier for this argument. It may
119 * not be {@code null} if the short identifier is
120 * {@code null}.
121 * @param isRequired Indicates whether this argument is required to
122 * be provided.
123 * @param maxOccurrences The maximum number of times this argument may be
124 * provided on the command line. A value less than
125 * or equal to zero indicates that it may be present
126 * any number of times.
127 * @param valuePlaceholder A placeholder to display in usage information to
128 * indicate that a value must be provided. It may
129 * be {@code null} if a default placeholder should
130 * be used.
131 * @param description A human-readable description for this argument.
132 * It must not be {@code null}.
133 *
134 * @throws ArgumentException If there is a problem with the definition of
135 * this argument.
136 */
137 public StringArgument(final Character shortIdentifier,
138 final String longIdentifier, final boolean isRequired,
139 final int maxOccurrences, final String valuePlaceholder,
140 final String description)
141 throws ArgumentException
142 {
143 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
144 valuePlaceholder, description, null, (List<String>) null);
145 }
146
147
148
149 /**
150 * Creates a new string argument with the provided information. There will
151 * not be any default values.
152 *
153 * @param shortIdentifier The short identifier for this argument. It may
154 * not be {@code null} if the long identifier is
155 * {@code null}.
156 * @param longIdentifier The long identifier for this argument. It may
157 * not be {@code null} if the short identifier is
158 * {@code null}.
159 * @param isRequired Indicates whether this argument is required to
160 * be provided.
161 * @param maxOccurrences The maximum number of times this argument may be
162 * provided on the command line. A value less than
163 * or equal to zero indicates that it may be present
164 * any number of times.
165 * @param valuePlaceholder A placeholder to display in usage information to
166 * indicate that a value must be provided. It may
167 * be {@code null} if a default placeholder should
168 * be used.
169 * @param description A human-readable description for this argument.
170 * It must not be {@code null}.
171 * @param allowedValues The set of allowed values for this argument, or
172 * {@code null} if it should not be restricted.
173 *
174 * @throws ArgumentException If there is a problem with the definition of
175 * this argument.
176 */
177 public StringArgument(final Character shortIdentifier,
178 final String longIdentifier, final boolean isRequired,
179 final int maxOccurrences, final String valuePlaceholder,
180 final String description,
181 final Set<String> allowedValues)
182 throws ArgumentException
183 {
184 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
185 valuePlaceholder, description, allowedValues, (List<String>) null);
186 }
187
188
189
190 /**
191 * Creates a new string argument with the provided information. There will
192 * not be any restriction on values that may be assigned to this argument.
193 *
194 * @param shortIdentifier The short identifier for this argument. It may
195 * not be {@code null} if the long identifier is
196 * {@code null}.
197 * @param longIdentifier The long identifier for this argument. It may
198 * not be {@code null} if the short identifier is
199 * {@code null}.
200 * @param isRequired Indicates whether this argument is required to
201 * be provided.
202 * @param maxOccurrences The maximum number of times this argument may be
203 * provided on the command line. A value less than
204 * or equal to zero indicates that it may be present
205 * any number of times.
206 * @param valuePlaceholder A placeholder to display in usage information to
207 * indicate that a value must be provided. It may
208 * be {@code null} if a default placeholder should
209 * be used.
210 * @param description A human-readable description for this argument.
211 * It must not be {@code null}.
212 * @param defaultValue The default value that will be used for this
213 * argument if no values are provided. It may be
214 * {@code null} if there should not be a default
215 * value.
216 *
217 * @throws ArgumentException If there is a problem with the definition of
218 * this argument.
219 */
220 public StringArgument(final Character shortIdentifier,
221 final String longIdentifier, final boolean isRequired,
222 final int maxOccurrences, final String valuePlaceholder,
223 final String description,
224 final String defaultValue)
225 throws ArgumentException
226 {
227 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
228 valuePlaceholder, description, null,
229 ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
230 }
231
232
233
234 /**
235 * Creates a new string argument with the provided information. There will
236 * not be any restriction on values that may be assigned to this argument.
237 *
238 * @param shortIdentifier The short identifier for this argument. It may
239 * not be {@code null} if the long identifier is
240 * {@code null}.
241 * @param longIdentifier The long identifier for this argument. It may
242 * not be {@code null} if the short identifier is
243 * {@code null}.
244 * @param isRequired Indicates whether this argument is required to
245 * be provided.
246 * @param maxOccurrences The maximum number of times this argument may be
247 * provided on the command line. A value less than
248 * or equal to zero indicates that it may be present
249 * any number of times.
250 * @param valuePlaceholder A placeholder to display in usage information to
251 * indicate that a value must be provided. It may
252 * be {@code null} if a default placeholder should
253 * be used.
254 * @param description A human-readable description for this argument.
255 * It must not be {@code null}.
256 * @param defaultValues The set of default values that will be used for
257 * this argument if no values are provided.
258 *
259 * @throws ArgumentException If there is a problem with the definition of
260 * this argument.
261 */
262 public StringArgument(final Character shortIdentifier,
263 final String longIdentifier, final boolean isRequired,
264 final int maxOccurrences, final String valuePlaceholder,
265 final String description,
266 final List<String> defaultValues)
267 throws ArgumentException
268 {
269 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
270 valuePlaceholder, description, null, defaultValues);
271 }
272
273
274
275 /**
276 * Creates a new string argument with the provided information.
277 *
278 * @param shortIdentifier The short identifier for this argument. It may
279 * not be {@code null} if the long identifier is
280 * {@code null}.
281 * @param longIdentifier The long identifier for this argument. It may
282 * not be {@code null} if the short identifier is
283 * {@code null}.
284 * @param isRequired Indicates whether this argument is required to
285 * be provided.
286 * @param maxOccurrences The maximum number of times this argument may be
287 * provided on the command line. A value less than
288 * or equal to zero indicates that it may be present
289 * any number of times.
290 * @param valuePlaceholder A placeholder to display in usage information to
291 * indicate that a value must be provided. It may
292 * be {@code null} if a default placeholder should
293 * be used.
294 * @param description A human-readable description for this argument.
295 * It must not be {@code null}.
296 * @param allowedValues The set of allowed values for this argument, or
297 * {@code null} if it should not be restricted.
298 * @param defaultValue The default value that will be used for this
299 * argument if no values are provided. It may be
300 * {@code null} if there should not be a default
301 * value.
302 *
303 * @throws ArgumentException If there is a problem with the definition of
304 * this argument.
305 */
306 public StringArgument(final Character shortIdentifier,
307 final String longIdentifier, final boolean isRequired,
308 final int maxOccurrences, final String valuePlaceholder,
309 final String description,
310 final Set<String> allowedValues,
311 final String defaultValue)
312 throws ArgumentException
313 {
314 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
315 valuePlaceholder, description, allowedValues,
316 ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
317 }
318
319
320
321 /**
322 * Creates a new string argument with the provided information.
323 *
324 * @param shortIdentifier The short identifier for this argument. It may
325 * not be {@code null} if the long identifier is
326 * {@code null}.
327 * @param longIdentifier The long identifier for this argument. It may
328 * not be {@code null} if the short identifier is
329 * {@code null}.
330 * @param isRequired Indicates whether this argument is required to
331 * be provided.
332 * @param maxOccurrences The maximum number of times this argument may be
333 * provided on the command line. A value less than
334 * or equal to zero indicates that it may be present
335 * any number of times.
336 * @param valuePlaceholder A placeholder to display in usage information to
337 * indicate that a value must be provided. It may
338 * be {@code null} if a default placeholder should
339 * be used.
340 * @param description A human-readable description for this argument.
341 * It must not be {@code null}.
342 * @param allowedValues The set of allowed values for this argument, or
343 * {@code null} if it should not be restricted.
344 * @param defaultValues The set of default values that will be used for
345 * this argument if no values are provided.
346 *
347 * @throws ArgumentException If there is a problem with the definition of
348 * this argument.
349 */
350 public StringArgument(final Character shortIdentifier,
351 final String longIdentifier, final boolean isRequired,
352 final int maxOccurrences, final String valuePlaceholder,
353 final String description,
354 final Set<String> allowedValues,
355 final List<String> defaultValues)
356 throws ArgumentException
357 {
358 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
359 (valuePlaceholder == null)
360 ? INFO_PLACEHOLDER_VALUE.get()
361 : valuePlaceholder,
362 description);
363
364 if ((allowedValues == null) || allowedValues.isEmpty())
365 {
366 this.allowedValues = null;
367 }
368 else
369 {
370 final HashSet<String> lowerValues =
371 new HashSet<String>(allowedValues.size());
372 for (final String s : allowedValues)
373 {
374 lowerValues.add(toLowerCase(s));
375 }
376 this.allowedValues = Collections.unmodifiableSet(lowerValues);
377 }
378
379 if ((defaultValues == null) || defaultValues.isEmpty())
380 {
381 this.defaultValues = null;
382 }
383 else
384 {
385 this.defaultValues = Collections.unmodifiableList(defaultValues);
386 }
387
388 if ((this.allowedValues != null) && (this.defaultValues != null))
389 {
390 for (final String s : this.defaultValues)
391 {
392 final String lowerDefault = toLowerCase(s);
393 if (! this.allowedValues.contains(lowerDefault))
394 {
395 throw new ArgumentException(
396 ERR_ARG_DEFAULT_VALUE_NOT_ALLOWED.get(s, getIdentifierString()));
397 }
398 }
399 }
400
401 values = new ArrayList<String>(5);
402 validators = new ArrayList<ArgumentValueValidator>(5);
403 valueRegex = null;
404 valueRegexExplanation = null;
405 }
406
407
408
409 /**
410 * Creates a new string argument that is a "clean" copy of the provided source
411 * argument.
412 *
413 * @param source The source argument to use for this argument.
414 */
415 private StringArgument(final StringArgument source)
416 {
417 super(source);
418
419 allowedValues = source.allowedValues;
420 defaultValues = source.defaultValues;
421 valueRegex = source.valueRegex;
422 valueRegexExplanation = source.valueRegexExplanation;
423 values = new ArrayList<String>(5);
424 validators =
425 new ArrayList<ArgumentValueValidator>(source.validators);
426 }
427
428
429
430 /**
431 * Retrieves the set of allowed values for this argument, if applicable.
432 *
433 * @return The set of allowed values for this argument, or {@code null} if
434 * there is no restriction on the allowed values.
435 */
436 public Set<String> getAllowedValues()
437 {
438 return allowedValues;
439 }
440
441
442
443 /**
444 * Retrieves the list of default values for this argument, which will be used
445 * if no values were provided.
446 *
447 * @return The list of default values for this argument, or {@code null} if
448 * there are no default values.
449 */
450 public List<String> getDefaultValues()
451 {
452 return defaultValues;
453 }
454
455
456
457 /**
458 * Retrieves the regular expression that values of this argument will be
459 * required to match, if any.
460 *
461 * @return The regular expression that values of this argument will be
462 * required to match, or {@code null} if none is defined.
463 */
464 public Pattern getValueRegex()
465 {
466 return valueRegex;
467 }
468
469
470
471 /**
472 * Retrieves a human-readable explanation of the regular expression pattern
473 * that may be required to match any provided values, if any.
474 *
475 * @return A human-readable explanation of the regular expression pattern, or
476 * {@code null} if none is available.
477 */
478 public String getValueRegexExplanation()
479 {
480 return valueRegexExplanation;
481 }
482
483
484
485 /**
486 * Specifies the regular expression that values of this argument will be
487 * required to match, if any.
488 *
489 * @param valueRegex The regular expression that values of this argument
490 * will be required to match. It may be {@code null} if
491 * no pattern matching should be required.
492 * @param explanation A human-readable explanation for the pattern which may
493 * be used to clarify the kinds of values that are
494 * acceptable. It may be {@code null} if no pattern
495 * matching should be required, or if the regular
496 * expression pattern should be sufficiently clear for
497 * the target audience.
498 */
499 public void setValueRegex(final Pattern valueRegex,
500 final String explanation)
501 {
502 this.valueRegex = valueRegex;
503 valueRegexExplanation = explanation;
504 }
505
506
507
508 /**
509 * Updates this argument to ensure that the provided validator will be invoked
510 * for any values provided to this argument. This validator will be invoked
511 * after all other validation has been performed for this argument.
512 *
513 * @param validator The argument value validator to be invoked. It must not
514 * be {@code null}.
515 */
516 public void addValueValidator(final ArgumentValueValidator validator)
517 {
518 validators.add(validator);
519 }
520
521
522
523 /**
524 * {@inheritDoc}
525 */
526 @Override()
527 protected void addValue(final String valueString)
528 throws ArgumentException
529 {
530 final String lowerValue = toLowerCase(valueString);
531 if (allowedValues != null)
532 {
533 if (! allowedValues.contains(lowerValue))
534 {
535 throw new ArgumentException(ERR_ARG_VALUE_NOT_ALLOWED.get(
536 valueString, getIdentifierString()));
537 }
538 }
539
540 if (values.size() >= getMaxOccurrences())
541 {
542 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
543 getIdentifierString()));
544 }
545
546 if (valueRegex != null)
547 {
548 final Matcher matcher = valueRegex.matcher(valueString);
549 if (! matcher.matches())
550 {
551 final String pattern = valueRegex.pattern();
552 if (valueRegexExplanation == null)
553 {
554 throw new ArgumentException(
555 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITHOUT_EXPLANATION.get(
556 valueString, getIdentifierString(), pattern));
557 }
558 else
559 {
560 throw new ArgumentException(
561 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITH_EXPLANATION.get(
562 valueString, getIdentifierString(), pattern,
563 valueRegexExplanation));
564 }
565 }
566 }
567
568 for (final ArgumentValueValidator v : validators)
569 {
570 v.validateArgumentValue(this, valueString);
571 }
572
573 values.add(valueString);
574 }
575
576
577
578 /**
579 * Retrieves the value for this argument, or the default value if none was
580 * provided. If this argument has multiple values, then the first will be
581 * returned.
582 *
583 * @return The value for this argument, or the default value if none was
584 * provided, or {@code null} if it does not have any values or
585 * default values.
586 */
587 public String getValue()
588 {
589 if (values.isEmpty())
590 {
591 if ((defaultValues == null) || defaultValues.isEmpty())
592 {
593 return null;
594 }
595 else
596 {
597 return defaultValues.get(0);
598 }
599 }
600
601 return values.get(0);
602 }
603
604
605
606 /**
607 * Retrieves the set of values for this argument, or the default values if
608 * none were provided.
609 *
610 * @return The set of values for this argument, or the default values if none
611 * were provided.
612 */
613 public List<String> getValues()
614 {
615 if (values.isEmpty() && (defaultValues != null))
616 {
617 return defaultValues;
618 }
619
620 return Collections.unmodifiableList(values);
621 }
622
623
624
625 /**
626 * {@inheritDoc}
627 */
628 @Override()
629 public List<String> getValueStringRepresentations(final boolean useDefault)
630 {
631 if (! values.isEmpty())
632 {
633 return Collections.unmodifiableList(values);
634 }
635 else if (useDefault && (defaultValues != null))
636 {
637 return Collections.unmodifiableList(defaultValues);
638 }
639 else
640 {
641 return Collections.emptyList();
642 }
643 }
644
645
646
647 /**
648 * {@inheritDoc}
649 */
650 @Override()
651 protected boolean hasDefaultValue()
652 {
653 return ((defaultValues != null) && (! defaultValues.isEmpty()));
654 }
655
656
657
658 /**
659 * {@inheritDoc}
660 */
661 @Override()
662 public String getDataTypeName()
663 {
664 return INFO_STRING_TYPE_NAME.get();
665 }
666
667
668
669 /**
670 * {@inheritDoc}
671 */
672 @Override()
673 public String getValueConstraints()
674 {
675 StringBuilder buffer = null;
676
677 if (valueRegex != null)
678 {
679 buffer = new StringBuilder();
680 final String pattern = valueRegex.pattern();
681 if ((valueRegexExplanation == null) ||
682 (valueRegexExplanation.length() == 0))
683 {
684 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get(
685 pattern));
686 }
687 else
688 {
689 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get(
690 pattern, valueRegexExplanation));
691 }
692 }
693
694 if ((allowedValues != null) && (! allowedValues.isEmpty()))
695 {
696 if (buffer == null)
697 {
698 buffer = new StringBuilder();
699 }
700 else
701 {
702 buffer.append(" ");
703 }
704
705 buffer.append(INFO_STRING_CONSTRAINTS_ALLOWED_VALUE.get());
706 buffer.append(" ");
707
708 final Iterator<String> iterator = allowedValues.iterator();
709 while (iterator.hasNext())
710 {
711 buffer.append('\'');
712 buffer.append(iterator.next());
713 buffer.append('\'');
714
715 if (iterator.hasNext())
716 {
717 buffer.append(", ");
718 }
719 }
720 buffer.append('.');
721 }
722
723 if (buffer == null)
724 {
725 return null;
726 }
727 else
728 {
729 return buffer.toString();
730 }
731 }
732
733
734
735 /**
736 * {@inheritDoc}
737 */
738 @Override()
739 protected void reset()
740 {
741 super.reset();
742 values.clear();
743 }
744
745
746
747 /**
748 * {@inheritDoc}
749 */
750 @Override()
751 public StringArgument getCleanCopy()
752 {
753 return new StringArgument(this);
754 }
755
756
757
758 /**
759 * {@inheritDoc}
760 */
761 @Override()
762 protected void addToCommandLine(final List<String> argStrings)
763 {
764 if (values != null)
765 {
766 for (final String s : values)
767 {
768 argStrings.add(getIdentifierString());
769 if (isSensitive())
770 {
771 argStrings.add("***REDACTED***");
772 }
773 else
774 {
775 argStrings.add(s);
776 }
777 }
778 }
779 }
780
781
782
783 /**
784 * {@inheritDoc}
785 */
786 @Override()
787 public void toString(final StringBuilder buffer)
788 {
789 buffer.append("StringArgument(");
790 appendBasicToStringInfo(buffer);
791
792 if ((allowedValues != null) && (! allowedValues.isEmpty()))
793 {
794 buffer.append(", allowedValues={");
795 final Iterator<String> iterator = allowedValues.iterator();
796 while (iterator.hasNext())
797 {
798 buffer.append('\'');
799 buffer.append(iterator.next());
800 buffer.append('\'');
801
802 if (iterator.hasNext())
803 {
804 buffer.append(", ");
805 }
806 }
807 buffer.append('}');
808 }
809
810 if (valueRegex != null)
811 {
812 buffer.append(", valueRegex='");
813 buffer.append(valueRegex.pattern());
814 buffer.append('\'');
815
816 if (valueRegexExplanation != null)
817 {
818 buffer.append(", valueRegexExplanation='");
819 buffer.append(valueRegexExplanation);
820 buffer.append('\'');
821 }
822 }
823
824 if ((defaultValues != null) && (! defaultValues.isEmpty()))
825 {
826 if (defaultValues.size() == 1)
827 {
828 buffer.append(", defaultValue='");
829 buffer.append(defaultValues.get(0));
830 }
831 else
832 {
833 buffer.append(", defaultValues={");
834
835 final Iterator<String> iterator = defaultValues.iterator();
836 while (iterator.hasNext())
837 {
838 buffer.append('\'');
839 buffer.append(iterator.next());
840 buffer.append('\'');
841
842 if (iterator.hasNext())
843 {
844 buffer.append(", ");
845 }
846 }
847
848 buffer.append('}');
849 }
850 }
851
852 buffer.append(')');
853 }
854 }