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