001 /*
002 * Copyright 2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 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.Collections;
027 import java.util.Iterator;
028 import java.util.List;
029
030 import com.unboundid.asn1.ASN1OctetString;
031 import com.unboundid.ldap.sdk.Control;
032 import com.unboundid.util.Base64;
033 import com.unboundid.util.Debug;
034 import com.unboundid.util.Mutable;
035 import com.unboundid.util.StaticUtils;
036 import com.unboundid.util.ThreadSafety;
037 import com.unboundid.util.ThreadSafetyLevel;
038
039 import static com.unboundid.util.args.ArgsMessages.*;
040
041
042
043 /**
044 * This class defines an argument that is intended to hold information about one
045 * or more LDAP controls. Values for this argument must be in one of the
046 * following formats:
047 * <UL>
048 * <LI>
049 * oid -- The numeric OID for the control. The control will not be critical
050 * and will not have a value.
051 * </LI>
052 * <LI>
053 * oid:criticality -- The numeric OID followed by a colon and the
054 * criticality. The control will be critical if the criticality value is
055 * any of the following: {@code true}, {@code t}, {@code yes}, {@code y},
056 * {@code on}, or {@code 1}. The control will be non-critical if the
057 * criticality value is any of the following: {@code false}, {@code f},
058 * {@code no}, {@code n}, {@code off}, or {@code 0}. No other criticality
059 * values will be accepted.
060 * </LI>
061 * <LI>
062 * oid:criticality:value -- The numeric OID followed by a colon and the
063 * criticality, then a colon and then a string that represents the value for
064 * the control.
065 * </LI>
066 * <LI>
067 * oid:criticality::base64value -- The numeric OID followed by a colon and
068 * the criticality, then two colons and then a string that represents the
069 * base64-encoded value for the control.
070 * </LI>
071 * </UL>
072 */
073 @Mutable()
074 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
075 public final class ControlArgument
076 extends Argument
077 {
078 /**
079 * The serial version UID for this serializable class.
080 */
081 private static final long serialVersionUID = -1889200072476038957L;
082
083
084
085 // The argument value validators that have been registered for this argument.
086 private final List<ArgumentValueValidator> validators;
087
088 // The list of default values for this argument.
089 private final List<Control> defaultValues;
090
091 // The set of values assigned to this argument.
092 private final List<Control> values;
093
094
095
096 /**
097 * Creates a new control argument with the provided information. It will not
098 * have a default value.
099 *
100 * @param shortIdentifier The short identifier for this argument. It may
101 * not be {@code null} if the long identifier is
102 * {@code null}.
103 * @param longIdentifier The long identifier for this argument. It may
104 * not be {@code null} if the short identifier is
105 * {@code null}.
106 * @param isRequired Indicates whether this argument is required to
107 * be provided.
108 * @param maxOccurrences The maximum number of times this argument may be
109 * provided on the command line. A value less than
110 * or equal to zero indicates that it may be present
111 * any number of times.
112 * @param valuePlaceholder A placeholder to display in usage information to
113 * indicate that a value must be provided. It may
114 * be {@code null} to use a default placeholder that
115 * describes the expected syntax for values.
116 * @param description A human-readable description for this argument.
117 * It must not be {@code null}.
118 *
119 * @throws ArgumentException If there is a problem with the definition of
120 * this argument.
121 */
122 public ControlArgument(final Character shortIdentifier,
123 final String longIdentifier, final boolean isRequired,
124 final int maxOccurrences,
125 final String valuePlaceholder,
126 final String description)
127 throws ArgumentException
128 {
129 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
130 valuePlaceholder, description, (List<Control>) null);
131 }
132
133
134
135 /**
136 * Creates a new control argument with the provided information.
137 *
138 * @param shortIdentifier The short identifier for this argument. It may
139 * not be {@code null} if the long identifier is
140 * {@code null}.
141 * @param longIdentifier The long identifier for this argument. It may
142 * not be {@code null} if the short identifier is
143 * {@code null}.
144 * @param isRequired Indicates whether this argument is required to
145 * be provided.
146 * @param maxOccurrences The maximum number of times this argument may be
147 * provided on the command line. A value less than
148 * or equal to zero indicates that it may be present
149 * any number of times.
150 * @param valuePlaceholder A placeholder to display in usage information to
151 * indicate that a value must be provided. It may
152 * be {@code null} to use a default placeholder that
153 * describes the expected syntax for values.
154 * @param description A human-readable description for this argument.
155 * It must not be {@code null}.
156 * @param defaultValue The default value to use for this argument if no
157 * values were provided. It may be {@code null} if
158 * there should be no default values.
159 *
160 * @throws ArgumentException If there is a problem with the definition of
161 * this argument.
162 */
163 public ControlArgument(final Character shortIdentifier,
164 final String longIdentifier, final boolean isRequired,
165 final int maxOccurrences,
166 final String valuePlaceholder,
167 final String description, final Control defaultValue)
168 throws ArgumentException
169 {
170 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
171 valuePlaceholder, description,
172 ((defaultValue == null)
173 ? null :
174 Collections.singletonList(defaultValue)));
175 }
176
177
178
179 /**
180 * Creates a new control argument with the provided information.
181 *
182 * @param shortIdentifier The short identifier for this argument. It may
183 * not be {@code null} if the long identifier is
184 * {@code null}.
185 * @param longIdentifier The long identifier for this argument. It may
186 * not be {@code null} if the short identifier is
187 * {@code null}.
188 * @param isRequired Indicates whether this argument is required to
189 * be provided.
190 * @param maxOccurrences The maximum number of times this argument may be
191 * provided on the command line. A value less than
192 * or equal to zero indicates that it may be present
193 * any number of times.
194 * @param valuePlaceholder A placeholder to display in usage information to
195 * indicate that a value must be provided. It may
196 * be {@code null} to use a default placeholder that
197 * describes the expected syntax for values.
198 * @param description A human-readable description for this argument.
199 * It must not be {@code null}.
200 * @param defaultValues The set of default values to use for this
201 * argument if no values were provided.
202 *
203 * @throws ArgumentException If there is a problem with the definition of
204 * this argument.
205 */
206 public ControlArgument(final Character shortIdentifier,
207 final String longIdentifier, final boolean isRequired,
208 final int maxOccurrences,
209 final String valuePlaceholder,
210 final String description,
211 final List<Control> defaultValues)
212 throws ArgumentException
213 {
214 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
215 (valuePlaceholder == null)
216 ? "{oid}[:{criticality}[:{stringValue}|::{base64Value}]]"
217 : valuePlaceholder,
218 description);
219
220 if ((defaultValues == null) || defaultValues.isEmpty())
221 {
222 this.defaultValues = null;
223 }
224 else
225 {
226 this.defaultValues = Collections.unmodifiableList(defaultValues);
227 }
228
229 values = new ArrayList<Control>(5);
230 validators = new ArrayList<ArgumentValueValidator>(5);
231 }
232
233
234
235 /**
236 * Creates a new control argument that is a "clean" copy of the provided
237 * source argument.
238 *
239 * @param source The source argument to use for this argument.
240 */
241 private ControlArgument(final ControlArgument source)
242 {
243 super(source);
244
245 defaultValues = source.defaultValues;
246 validators = new ArrayList<ArgumentValueValidator>(source.validators);
247 values = new ArrayList<Control>(5);
248 }
249
250
251
252 /**
253 * Retrieves the list of default values for this argument, which will be used
254 * if no values were provided.
255 *
256 * @return The list of default values for this argument, or {@code null} if
257 * there are no default values.
258 */
259 public List<Control> getDefaultValues()
260 {
261 return defaultValues;
262 }
263
264
265
266 /**
267 * Updates this argument to ensure that the provided validator will be invoked
268 * for any values provided to this argument. This validator will be invoked
269 * after all other validation has been performed for this argument.
270 *
271 * @param validator The argument value validator to be invoked. It must not
272 * be {@code null}.
273 */
274 public void addValueValidator(final ArgumentValueValidator validator)
275 {
276 validators.add(validator);
277 }
278
279
280
281 /**
282 * {@inheritDoc}
283 */
284 @Override()
285 protected void addValue(final String valueString)
286 throws ArgumentException
287 {
288 final String oid;
289 boolean isCritical = false;
290 ASN1OctetString value = null;
291
292 final int firstColonPos = valueString.indexOf(':');
293 if (firstColonPos < 0)
294 {
295 oid = valueString;
296 }
297 else
298 {
299 oid = valueString.substring(0, firstColonPos);
300
301 final String criticalityStr;
302 final int secondColonPos = valueString.indexOf(':', (firstColonPos+1));
303 if (secondColonPos < 0)
304 {
305 criticalityStr = valueString.substring(firstColonPos+1);
306 }
307 else
308 {
309 criticalityStr = valueString.substring(firstColonPos+1, secondColonPos);
310
311 final int doubleColonPos = valueString.indexOf("::");
312 if (doubleColonPos == secondColonPos)
313 {
314 try
315 {
316 value = new ASN1OctetString(
317 Base64.decode(valueString.substring(doubleColonPos+2)));
318 }
319 catch (final Exception e)
320 {
321 Debug.debugException(e);
322 throw new ArgumentException(
323 ERR_CONTROL_ARG_INVALID_BASE64_VALUE.get(valueString,
324 getIdentifierString(),
325 valueString.substring(doubleColonPos+2)),
326 e);
327 }
328 }
329 else
330 {
331 value = new ASN1OctetString(valueString.substring(secondColonPos+1));
332 }
333 }
334
335 final String lowerCriticalityStr =
336 StaticUtils.toLowerCase(criticalityStr);
337 if (lowerCriticalityStr.equals("true") ||
338 lowerCriticalityStr.equals("t") ||
339 lowerCriticalityStr.equals("yes") ||
340 lowerCriticalityStr.equals("y") ||
341 lowerCriticalityStr.equals("on") ||
342 lowerCriticalityStr.equals("1"))
343 {
344 isCritical = true;
345 }
346 else if (lowerCriticalityStr.equals("false") ||
347 lowerCriticalityStr.equals("f") ||
348 lowerCriticalityStr.equals("no") ||
349 lowerCriticalityStr.equals("n") ||
350 lowerCriticalityStr.equals("off") ||
351 lowerCriticalityStr.equals("0"))
352 {
353 isCritical = false;
354 }
355 else
356 {
357 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_CRITICALITY.get(
358 valueString, getIdentifierString(), criticalityStr));
359 }
360 }
361
362 if (! StaticUtils.isNumericOID(oid))
363 {
364 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_OID.get(
365 valueString, getIdentifierString(), oid));
366 }
367
368 if (values.size() >= getMaxOccurrences())
369 {
370 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
371 getIdentifierString()));
372 }
373
374 for (final ArgumentValueValidator v : validators)
375 {
376 v.validateArgumentValue(this, valueString);
377 }
378
379 values.add(new Control(oid, isCritical, value));
380 }
381
382
383
384 /**
385 * Retrieves the value for this argument, or the default value if none was
386 * provided. If there are multiple values, then the first will be returned.
387 *
388 * @return The value for this argument, or the default value if none was
389 * provided, or {@code null} if there is no value and no default
390 * value.
391 */
392 public Control getValue()
393 {
394 if (values.isEmpty())
395 {
396 if ((defaultValues == null) || defaultValues.isEmpty())
397 {
398 return null;
399 }
400 else
401 {
402 return defaultValues.get(0);
403 }
404 }
405 else
406 {
407 return values.get(0);
408 }
409 }
410
411
412
413 /**
414 * Retrieves the set of values for this argument, or the default values if
415 * none were provided.
416 *
417 * @return The set of values for this argument, or the default values if none
418 * were provided.
419 */
420 public List<Control> getValues()
421 {
422 if (values.isEmpty() && (defaultValues != null))
423 {
424 return defaultValues;
425 }
426
427 return Collections.unmodifiableList(values);
428 }
429
430
431
432 /**
433 * {@inheritDoc}
434 */
435 @Override()
436 protected boolean hasDefaultValue()
437 {
438 return ((defaultValues != null) && (! defaultValues.isEmpty()));
439 }
440
441
442
443 /**
444 * {@inheritDoc}
445 */
446 @Override()
447 public String getDataTypeName()
448 {
449 return INFO_CONTROL_TYPE_NAME.get();
450 }
451
452
453
454 /**
455 * {@inheritDoc}
456 */
457 @Override()
458 public String getValueConstraints()
459 {
460 return INFO_CONTROL_CONSTRAINTS.get();
461 }
462
463
464
465 /**
466 * {@inheritDoc}
467 */
468 @Override()
469 public ControlArgument getCleanCopy()
470 {
471 return new ControlArgument(this);
472 }
473
474
475
476 /**
477 * {@inheritDoc}
478 */
479 @Override()
480 public void toString(final StringBuilder buffer)
481 {
482 buffer.append("ControlArgument(");
483 appendBasicToStringInfo(buffer);
484
485 if ((defaultValues != null) && (! defaultValues.isEmpty()))
486 {
487 if (defaultValues.size() == 1)
488 {
489 buffer.append(", defaultValue='");
490 buffer.append(defaultValues.get(0).toString());
491 }
492 else
493 {
494 buffer.append(", defaultValues={");
495
496 final Iterator<Control> iterator = defaultValues.iterator();
497 while (iterator.hasNext())
498 {
499 buffer.append('\'');
500 buffer.append(iterator.next().toString());
501 buffer.append('\'');
502
503 if (iterator.hasNext())
504 {
505 buffer.append(", ");
506 }
507 }
508
509 buffer.append('}');
510 }
511 }
512
513 buffer.append(')');
514 }
515 }