001/*
002 * Copyright 2015-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 Ping Identity Corporation
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 */
021package com.unboundid.util.args;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.List;
030import java.util.Map;
031
032import com.unboundid.asn1.ASN1OctetString;
033import com.unboundid.ldap.sdk.Control;
034import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
035import com.unboundid.ldap.sdk.controls.DontUseCopyRequestControl;
036import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl;
037import com.unboundid.ldap.sdk.controls.PermissiveModifyRequestControl;
038import com.unboundid.ldap.sdk.controls.SubentriesRequestControl;
039import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl;
040import com.unboundid.ldap.sdk.experimental.
041            DraftBeheraLDAPPasswordPolicy10RequestControl;
042import com.unboundid.ldap.sdk.experimental.
043            DraftZeilengaLDAPNoOp12RequestControl;
044import com.unboundid.util.Base64;
045import com.unboundid.util.Debug;
046import com.unboundid.util.Mutable;
047import com.unboundid.util.StaticUtils;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050
051import static com.unboundid.util.args.ArgsMessages.*;
052
053
054
055/**
056 * This class defines an argument that is intended to hold information about one
057 * or more LDAP controls.  Values for this argument must be in one of the
058 * following formats:
059 * <UL>
060 *   <LI>
061 *     oid -- The numeric OID for the control.  The control will not be critical
062 *     and will not have a value.
063 *   </LI>
064 *   <LI>
065 *     oid:criticality -- The numeric OID followed by a colon and the
066 *     criticality.  The control will be critical if the criticality value is
067 *     any of the following:  {@code true}, {@code t}, {@code yes}, {@code y},
068 *     {@code on}, or {@code 1}.  The control will be non-critical if the
069 *     criticality value is any of the following:  {@code false}, {@code f},
070 *     {@code no}, {@code n}, {@code off}, or {@code 0}.  No other criticality
071 *     values will be accepted.
072 *   </LI>
073 *   <LI>
074 *     oid:criticality:value -- The numeric OID followed by a colon and the
075 *     criticality, then a colon and then a string that represents the value for
076 *     the control.
077 *   </LI>
078 *   <LI>
079 *     oid:criticality::base64value -- The numeric OID  followed by a colon and
080 *     the criticality, then two colons and then a string that represents the
081 *     base64-encoded value for the control.
082 *   </LI>
083 * </UL>
084 */
085@Mutable()
086@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
087public final class ControlArgument
088       extends Argument
089{
090  /**
091   * A map of human-readable names to the corresponding numeric OIDs.
092   */
093  private static final Map<String,String> OIDS_BY_NAME;
094  static
095  {
096    final HashMap<String,String> oidsByName = new HashMap<>(100);
097
098    // The authorization identity request control.
099    oidsByName.put("authzid",
100         AuthorizationIdentityRequestControl.
101              AUTHORIZATION_IDENTITY_REQUEST_OID);
102    oidsByName.put("authorizationidentity",
103         AuthorizationIdentityRequestControl.
104              AUTHORIZATION_IDENTITY_REQUEST_OID);
105    oidsByName.put("authorization-identity",
106         AuthorizationIdentityRequestControl.
107              AUTHORIZATION_IDENTITY_REQUEST_OID);
108
109    // The don't use copy request control.
110    oidsByName.put("nocopy",
111         DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID);
112    oidsByName.put("dontusecopy",
113         DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID);
114    oidsByName.put("no-copy",
115         DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID);
116    oidsByName.put("dont-use-copy",
117         DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID);
118
119    // The LDAP no-operation request control.
120    oidsByName.put("noop",
121         DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID);
122    oidsByName.put("nooperation",
123         DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID);
124    oidsByName.put("no-op",
125         DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID);
126    oidsByName.put("no-operation",
127         DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID);
128
129    // The LDAP subentries request control.
130    oidsByName.put("subentries",
131         SubentriesRequestControl.SUBENTRIES_REQUEST_OID);
132    oidsByName.put("ldapsubentries",
133         SubentriesRequestControl.SUBENTRIES_REQUEST_OID);
134    oidsByName.put("ldap-subentries",
135         SubentriesRequestControl.SUBENTRIES_REQUEST_OID);
136
137    // The manage DSA IT request control.
138    oidsByName.put("managedsait",
139         ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID);
140    oidsByName.put("manage-dsa-it",
141         ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID);
142
143    // The permissive modify request control.
144    oidsByName.put("permissivemodify",
145         PermissiveModifyRequestControl.PERMISSIVE_MODIFY_REQUEST_OID);
146    oidsByName.put("permissive-modify",
147         PermissiveModifyRequestControl.PERMISSIVE_MODIFY_REQUEST_OID);
148
149    // The password policy request control.
150    oidsByName.put("pwpolicy",
151         DraftBeheraLDAPPasswordPolicy10RequestControl.
152              PASSWORD_POLICY_REQUEST_OID);
153    oidsByName.put("passwordpolicy",
154         DraftBeheraLDAPPasswordPolicy10RequestControl.
155              PASSWORD_POLICY_REQUEST_OID);
156    oidsByName.put("pw-policy",
157         DraftBeheraLDAPPasswordPolicy10RequestControl.
158              PASSWORD_POLICY_REQUEST_OID);
159    oidsByName.put("password-policy",
160         DraftBeheraLDAPPasswordPolicy10RequestControl.
161              PASSWORD_POLICY_REQUEST_OID);
162
163    // The subtree delete request control.
164    oidsByName.put("subtreedelete",
165         SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID);
166    oidsByName.put("treedelete",
167         SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID);
168    oidsByName.put("subtree-delete",
169         SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID);
170    oidsByName.put("tree-delete",
171         SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID);
172
173    // The account usable request control.
174    oidsByName.put("accountusable", "1.3.6.1.4.1.42.2.27.9.5.8");
175    oidsByName.put("accountusability", "1.3.6.1.4.1.42.2.27.9.5.8");
176    oidsByName.put("account-usable", "1.3.6.1.4.1.42.2.27.9.5.8");
177    oidsByName.put("account-usability", "1.3.6.1.4.1.42.2.27.9.5.8");
178
179    // The get backend set ID request control.
180    oidsByName.put("backendsetid", "1.3.6.1.4.1.30221.2.5.33");
181    oidsByName.put("getbackendsetid", "1.3.6.1.4.1.30221.2.5.33");
182    oidsByName.put("backendset-id", "1.3.6.1.4.1.30221.2.5.33");
183    oidsByName.put("backend-set-id", "1.3.6.1.4.1.30221.2.5.33");
184    oidsByName.put("get-backendset-id", "1.3.6.1.4.1.30221.2.5.33");
185    oidsByName.put("get-backend-set-id", "1.3.6.1.4.1.30221.2.5.33");
186
187    // The get effective rights request control.
188    oidsByName.put("effectiverights", "1.3.6.1.4.1.42.2.27.9.5.2");
189    oidsByName.put("geteffectiverights", "1.3.6.1.4.1.42.2.27.9.5.2");
190    oidsByName.put("effective-rights", "1.3.6.1.4.1.42.2.27.9.5.2");
191    oidsByName.put("get-effective-rights", "1.3.6.1.4.1.42.2.27.9.5.2");
192
193    // The get password policy state issues request control.
194    oidsByName.put("pwpolicystateissues", "1.3.6.1.4.1.30221.2.5.46");
195    oidsByName.put("getpwpolicystateissues", "1.3.6.1.4.1.30221.2.5.46");
196    oidsByName.put("passwordpolicystateissues", "1.3.6.1.4.1.30221.2.5.46");
197    oidsByName.put("getpasswordpolicystateissues", "1.3.6.1.4.1.30221.2.5.46");
198    oidsByName.put("pw-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46");
199    oidsByName.put("get-pw-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46");
200    oidsByName.put("password-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46");
201    oidsByName.put("get-password-policy-state-issues",
202         "1.3.6.1.4.1.30221.2.5.46");
203
204    // The get server ID request control.
205    oidsByName.put("serverid", "1.3.6.1.4.1.30221.2.5.14");
206    oidsByName.put("getserverid", "1.3.6.1.4.1.30221.2.5.14");
207    oidsByName.put("server-id", "1.3.6.1.4.1.30221.2.5.14");
208    oidsByName.put("get-server-id", "1.3.6.1.4.1.30221.2.5.14");
209
210    // The get user resource limits request control.
211    oidsByName.put("userresourcelimits", "1.3.6.1.4.1.30221.2.5.25");
212    oidsByName.put("getuserresourcelimits", "1.3.6.1.4.1.30221.2.5.25");
213    oidsByName.put("user-resource-limits", "1.3.6.1.4.1.30221.2.5.25");
214    oidsByName.put("get-user-resource-limits", "1.3.6.1.4.1.30221.2.5.25");
215
216    // The hard delete request control.
217    oidsByName.put("harddelete", "1.3.6.1.4.1.30221.2.5.22");
218    oidsByName.put("hard-delete", "1.3.6.1.4.1.30221.2.5.22");
219
220    // The ignore NO-USER-MODIFICATION request control.
221    oidsByName.put("ignorenousermod", "1.3.6.1.4.1.30221.2.5.5");
222    oidsByName.put("ignorenousermodification", "1.3.6.1.4.1.30221.2.5.5");
223    oidsByName.put("ignore-no-user-mod", "1.3.6.1.4.1.30221.2.5.5");
224    oidsByName.put("ignore-no-user-modification", "1.3.6.1.4.1.30221.2.5.5");
225
226    // The purge retired password request control.
227    oidsByName.put("purgepassword", "1.3.6.1.4.1.30221.2.5.32");
228    oidsByName.put("purgeretiredpassword", "1.3.6.1.4.1.30221.2.5.32");
229    oidsByName.put("purge-password", "1.3.6.1.4.1.30221.2.5.32");
230    oidsByName.put("purge-retired-password", "1.3.6.1.4.1.30221.2.5.32");
231
232    // The real attributes only request control.
233    oidsByName.put("realattrsonly", "2.16.840.1.113730.3.4.17");
234    oidsByName.put("realattributesonly", "2.16.840.1.113730.3.4.17");
235    oidsByName.put("real-attrs-only", "2.16.840.1.113730.3.4.17");
236    oidsByName.put("real-attributes-only", "2.16.840.1.113730.3.4.17");
237
238    // The replication repair request control.
239    oidsByName.put("replrepair", "1.3.6.1.4.1.30221.1.5.2");
240    oidsByName.put("replicationrepair", "1.3.6.1.4.1.30221.1.5.2");
241    oidsByName.put("repl-repair", "1.3.6.1.4.1.30221.1.5.2");
242    oidsByName.put("replication-repair", "1.3.6.1.4.1.30221.1.5.2");
243
244    // The retain identity request control.
245    oidsByName.put("retainidentity", "1.3.6.1.4.1.30221.2.5.3");
246    oidsByName.put("retain-identity", "1.3.6.1.4.1.30221.2.5.3");
247
248    // The retire password request control.
249    oidsByName.put("retirepassword", "1.3.6.1.4.1.30221.2.5.31");
250    oidsByName.put("retire-password", "1.3.6.1.4.1.30221.2.5.31");
251
252    // The return conflict entries request control.
253    oidsByName.put("returnconflictentries", "1.3.6.1.4.1.30221.2.5.13");
254    oidsByName.put("return-conflict-entries", "1.3.6.1.4.1.30221.2.5.13");
255
256    // The soft delete request control.
257    oidsByName.put("softdelete", "1.3.6.1.4.1.30221.2.5.20");
258    oidsByName.put("soft-delete", "1.3.6.1.4.1.30221.2.5.20");
259
260    // The soft-deleted entry access request control.
261    oidsByName.put("softdeleteentryaccess", "1.3.6.1.4.1.30221.2.5.24");
262    oidsByName.put("softdeletedentryaccess", "1.3.6.1.4.1.30221.2.5.24");
263    oidsByName.put("soft-delete-entry-access", "1.3.6.1.4.1.30221.2.5.24");
264    oidsByName.put("soft-deleted-entry-access", "1.3.6.1.4.1.30221.2.5.24");
265
266    // The suppress referential integrity updates request control.
267    oidsByName.put("suppressreferentialintegrity", "1.3.6.1.4.1.30221.2.5.30");
268    oidsByName.put("suppressreferentialintegrityupdates",
269         "1.3.6.1.4.1.30221.2.5.30");
270    oidsByName.put("suppress-referential-integrity",
271         "1.3.6.1.4.1.30221.2.5.30");
272    oidsByName.put("suppress-referential-integrity-updates",
273         "1.3.6.1.4.1.30221.2.5.30");
274
275    // The undelete request control.
276    oidsByName.put("undelete", "1.3.6.1.4.1.30221.2.5.23");
277
278    // The virtual attributes only request control.
279    oidsByName.put("virtualattrsonly", "2.16.840.1.113730.3.4.19");
280    oidsByName.put("virtualattributesonly", "2.16.840.1.113730.3.4.19");
281    oidsByName.put("virtual-attrs-only", "2.16.840.1.113730.3.4.19");
282    oidsByName.put("virtual-attributes-only", "2.16.840.1.113730.3.4.19");
283
284    OIDS_BY_NAME = Collections.unmodifiableMap(oidsByName);
285  }
286
287
288
289  /**
290   * The serial version UID for this serializable class.
291   */
292  private static final long serialVersionUID = -1889200072476038957L;
293
294
295
296  // The argument value validators that have been registered for this argument.
297  private final List<ArgumentValueValidator> validators;
298
299  // The list of default values for this argument.
300  private final List<Control> defaultValues;
301
302  // The set of values assigned to this argument.
303  private final List<Control> values;
304
305
306
307  /**
308   * Creates a new control argument with the provided information.  It will not
309   * be required, will be allowed any number of times, will use a default
310   * placeholder, and will not have a default value.
311   *
312   * @param  shortIdentifier   The short identifier for this argument.  It may
313   *                           not be {@code null} if the long identifier is
314   *                           {@code null}.
315   * @param  longIdentifier    The long identifier for this argument.  It may
316   *                           not be {@code null} if the short identifier is
317   *                           {@code null}.
318   * @param  description       A human-readable description for this argument.
319   *                           It must not be {@code null}.
320   *
321   * @throws  ArgumentException  If there is a problem with the definition of
322   *                             this argument.
323   */
324  public ControlArgument(final Character shortIdentifier,
325                         final String longIdentifier, final String description)
326         throws ArgumentException
327  {
328    this(shortIdentifier, longIdentifier, false, 0, null, description);
329  }
330
331
332
333  /**
334   * Creates a new control argument with the provided information.  It will not
335   * have a default value.
336   *
337   * @param  shortIdentifier   The short identifier for this argument.  It may
338   *                           not be {@code null} if the long identifier is
339   *                           {@code null}.
340   * @param  longIdentifier    The long identifier for this argument.  It may
341   *                           not be {@code null} if the short identifier is
342   *                           {@code null}.
343   * @param  isRequired        Indicates whether this argument is required to
344   *                           be provided.
345   * @param  maxOccurrences    The maximum number of times this argument may be
346   *                           provided on the command line.  A value less than
347   *                           or equal to zero indicates that it may be present
348   *                           any number of times.
349   * @param  valuePlaceholder  A placeholder to display in usage information to
350   *                           indicate that a value must be provided.  It may
351   *                           be {@code null} to use a default placeholder that
352   *                           describes the expected syntax for values.
353   * @param  description       A human-readable description for this argument.
354   *                           It must not be {@code null}.
355   *
356   * @throws  ArgumentException  If there is a problem with the definition of
357   *                             this argument.
358   */
359  public ControlArgument(final Character shortIdentifier,
360                         final String longIdentifier, final boolean isRequired,
361                         final int maxOccurrences,
362                         final String valuePlaceholder,
363                         final String description)
364         throws ArgumentException
365  {
366    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
367         valuePlaceholder, description, (List<Control>) null);
368  }
369
370
371
372  /**
373   * Creates a new control argument with the provided information.
374   *
375   * @param  shortIdentifier   The short identifier for this argument.  It may
376   *                           not be {@code null} if the long identifier is
377   *                           {@code null}.
378   * @param  longIdentifier    The long identifier for this argument.  It may
379   *                           not be {@code null} if the short identifier is
380   *                           {@code null}.
381   * @param  isRequired        Indicates whether this argument is required to
382   *                           be provided.
383   * @param  maxOccurrences    The maximum number of times this argument may be
384   *                           provided on the command line.  A value less than
385   *                           or equal to zero indicates that it may be present
386   *                           any number of times.
387   * @param  valuePlaceholder  A placeholder to display in usage information to
388   *                           indicate that a value must be provided.  It may
389   *                           be {@code null} to use a default placeholder that
390   *                           describes the expected syntax for values.
391   * @param  description       A human-readable description for this argument.
392   *                           It must not be {@code null}.
393   * @param  defaultValue      The default value to use for this argument if no
394   *                           values were provided.  It may be {@code null} if
395   *                           there should be no default values.
396   *
397   * @throws  ArgumentException  If there is a problem with the definition of
398   *                             this argument.
399   */
400  public ControlArgument(final Character shortIdentifier,
401                         final String longIdentifier, final boolean isRequired,
402                         final int maxOccurrences,
403                         final String valuePlaceholder,
404                         final String description, final Control defaultValue)
405         throws ArgumentException
406  {
407    this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
408         valuePlaceholder, description,
409         ((defaultValue == null)
410              ? null :
411              Collections.singletonList(defaultValue)));
412  }
413
414
415
416  /**
417   * Creates a new control argument with the provided information.
418   *
419   * @param  shortIdentifier   The short identifier for this argument.  It may
420   *                           not be {@code null} if the long identifier is
421   *                           {@code null}.
422   * @param  longIdentifier    The long identifier for this argument.  It may
423   *                           not be {@code null} if the short identifier is
424   *                           {@code null}.
425   * @param  isRequired        Indicates whether this argument is required to
426   *                           be provided.
427   * @param  maxOccurrences    The maximum number of times this argument may be
428   *                           provided on the command line.  A value less than
429   *                           or equal to zero indicates that it may be present
430   *                           any number of times.
431   * @param  valuePlaceholder  A placeholder to display in usage information to
432   *                           indicate that a value must be provided.  It may
433   *                           be {@code null} to use a default placeholder that
434   *                           describes the expected syntax for values.
435   * @param  description       A human-readable description for this argument.
436   *                           It must not be {@code null}.
437   * @param  defaultValues     The set of default values to use for this
438   *                           argument if no values were provided.
439   *
440   * @throws  ArgumentException  If there is a problem with the definition of
441   *                             this argument.
442   */
443  public ControlArgument(final Character shortIdentifier,
444                         final String longIdentifier, final boolean isRequired,
445                         final int maxOccurrences,
446                         final String valuePlaceholder,
447                         final String description,
448                         final List<Control> defaultValues)
449         throws ArgumentException
450  {
451    super(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
452         (valuePlaceholder == null)
453              ? INFO_PLACEHOLDER_CONTROL.get()
454              : valuePlaceholder,
455         description);
456
457    if ((defaultValues == null) || defaultValues.isEmpty())
458    {
459      this.defaultValues = null;
460    }
461    else
462    {
463      this.defaultValues = Collections.unmodifiableList(defaultValues);
464    }
465
466    values = new ArrayList<>(5);
467    validators = new ArrayList<>(5);
468  }
469
470
471
472  /**
473   * Creates a new control argument that is a "clean" copy of the provided
474   * source argument.
475   *
476   * @param  source  The source argument to use for this argument.
477   */
478  private ControlArgument(final ControlArgument source)
479  {
480    super(source);
481
482    defaultValues = source.defaultValues;
483    validators    = new ArrayList<>(source.validators);
484    values        = new ArrayList<>(5);
485  }
486
487
488
489  /**
490   * Retrieves the list of default values for this argument, which will be used
491   * if no values were provided.
492   *
493   * @return   The list of default values for this argument, or {@code null} if
494   *           there are no default values.
495   */
496  public List<Control> getDefaultValues()
497  {
498    return defaultValues;
499  }
500
501
502
503  /**
504   * Updates this argument to ensure that the provided validator will be invoked
505   * for any values provided to this argument.  This validator will be invoked
506   * after all other validation has been performed for this argument.
507   *
508   * @param  validator  The argument value validator to be invoked.  It must not
509   *                    be {@code null}.
510   */
511  public void addValueValidator(final ArgumentValueValidator validator)
512  {
513    validators.add(validator);
514  }
515
516
517
518  /**
519   * {@inheritDoc}
520   */
521  @Override()
522  protected void addValue(final String valueString)
523            throws ArgumentException
524  {
525    String oid = null;
526    boolean isCritical = false;
527    ASN1OctetString value = null;
528
529    final int firstColonPos = valueString.indexOf(':');
530    if (firstColonPos < 0)
531    {
532      oid = valueString;
533    }
534    else
535    {
536      oid = valueString.substring(0, firstColonPos);
537
538      final String criticalityStr;
539      final int secondColonPos = valueString.indexOf(':', (firstColonPos+1));
540      if (secondColonPos < 0)
541      {
542        criticalityStr = valueString.substring(firstColonPos+1);
543      }
544      else
545      {
546        criticalityStr = valueString.substring(firstColonPos+1, secondColonPos);
547
548        final int doubleColonPos = valueString.indexOf("::");
549        if (doubleColonPos == secondColonPos)
550        {
551          try
552          {
553            value = new ASN1OctetString(
554                 Base64.decode(valueString.substring(doubleColonPos+2)));
555          }
556          catch (final Exception e)
557          {
558            Debug.debugException(e);
559            throw new ArgumentException(
560                 ERR_CONTROL_ARG_INVALID_BASE64_VALUE.get(valueString,
561                      getIdentifierString(),
562                      valueString.substring(doubleColonPos+2)),
563                 e);
564          }
565        }
566        else
567        {
568          value = new ASN1OctetString(valueString.substring(secondColonPos+1));
569        }
570      }
571
572      final String lowerCriticalityStr =
573           StaticUtils.toLowerCase(criticalityStr);
574      if (lowerCriticalityStr.equals("true") ||
575          lowerCriticalityStr.equals("t") ||
576          lowerCriticalityStr.equals("yes") ||
577          lowerCriticalityStr.equals("y") ||
578          lowerCriticalityStr.equals("on") ||
579          lowerCriticalityStr.equals("1"))
580      {
581        isCritical = true;
582      }
583      else if (lowerCriticalityStr.equals("false") ||
584               lowerCriticalityStr.equals("f") ||
585               lowerCriticalityStr.equals("no") ||
586               lowerCriticalityStr.equals("n") ||
587               lowerCriticalityStr.equals("off") ||
588               lowerCriticalityStr.equals("0"))
589      {
590        isCritical = false;
591      }
592      else
593      {
594        throw new ArgumentException(ERR_CONTROL_ARG_INVALID_CRITICALITY.get(
595             valueString, getIdentifierString(), criticalityStr));
596      }
597    }
598
599    if (! StaticUtils.isNumericOID(oid))
600    {
601      final String providedOID = oid;
602      oid = OIDS_BY_NAME.get(StaticUtils.toLowerCase(providedOID));
603      if (oid == null)
604      {
605        throw new ArgumentException(ERR_CONTROL_ARG_INVALID_OID.get(
606             valueString, getIdentifierString(), providedOID));
607      }
608    }
609
610    if (values.size() >= getMaxOccurrences())
611    {
612      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
613                                       getIdentifierString()));
614    }
615
616    for (final ArgumentValueValidator v : validators)
617    {
618      v.validateArgumentValue(this, valueString);
619    }
620
621    values.add(new Control(oid, isCritical, value));
622  }
623
624
625
626  /**
627   * Retrieves the value for this argument, or the default value if none was
628   * provided.  If there are multiple values, then the first will be returned.
629   *
630   * @return  The value for this argument, or the default value if none was
631   *          provided, or {@code null} if there is no value and no default
632   *          value.
633   */
634  public Control getValue()
635  {
636    if (values.isEmpty())
637    {
638      if ((defaultValues == null) || defaultValues.isEmpty())
639      {
640        return null;
641      }
642      else
643      {
644        return defaultValues.get(0);
645      }
646    }
647    else
648    {
649      return values.get(0);
650    }
651  }
652
653
654
655  /**
656   * Retrieves the set of values for this argument, or the default values if
657   * none were provided.
658   *
659   * @return  The set of values for this argument, or the default values if none
660   *          were provided.
661   */
662  public List<Control> getValues()
663  {
664    if (values.isEmpty() && (defaultValues != null))
665    {
666      return defaultValues;
667    }
668
669    return Collections.unmodifiableList(values);
670  }
671
672
673
674  /**
675   * {@inheritDoc}
676   */
677  @Override()
678  public List<String> getValueStringRepresentations(final boolean useDefault)
679  {
680    final List<Control> controls;
681    if (values.isEmpty())
682    {
683      if (useDefault)
684      {
685        controls = defaultValues;
686      }
687      else
688      {
689        return Collections.emptyList();
690      }
691    }
692    else
693    {
694      controls = values;
695    }
696
697    if ((controls == null) || controls.isEmpty())
698    {
699      return Collections.emptyList();
700    }
701
702    final StringBuilder buffer = new StringBuilder();
703    final ArrayList<String> valueStrings = new ArrayList<>(controls.size());
704    for (final Control c : controls)
705    {
706      buffer.setLength(0);
707      buffer.append(c.getOID());
708      buffer.append(':');
709      buffer.append(c.isCritical());
710
711      if (c.hasValue())
712      {
713        final byte[] valueBytes = c.getValue().getValue();
714        if (StaticUtils.isPrintableString(valueBytes))
715        {
716          buffer.append(':');
717          buffer.append(c.getValue().stringValue());
718        }
719        else
720        {
721          buffer.append("::");
722          Base64.encode(valueBytes, buffer);
723        }
724      }
725
726      valueStrings.add(buffer.toString());
727    }
728
729    return Collections.unmodifiableList(valueStrings);
730  }
731
732
733
734  /**
735   * {@inheritDoc}
736   */
737  @Override()
738  protected boolean hasDefaultValue()
739  {
740    return ((defaultValues != null) && (! defaultValues.isEmpty()));
741  }
742
743
744
745  /**
746   * {@inheritDoc}
747   */
748  @Override()
749  public String getDataTypeName()
750  {
751    return INFO_CONTROL_TYPE_NAME.get();
752  }
753
754
755
756  /**
757   * {@inheritDoc}
758   */
759  @Override()
760  public String getValueConstraints()
761  {
762    return INFO_CONTROL_CONSTRAINTS.get();
763  }
764
765
766
767  /**
768   * {@inheritDoc}
769   */
770  @Override()
771  protected void reset()
772  {
773    super.reset();
774    values.clear();
775  }
776
777
778
779  /**
780   * {@inheritDoc}
781   */
782  @Override()
783  public ControlArgument getCleanCopy()
784  {
785    return new ControlArgument(this);
786  }
787
788
789
790  /**
791   * {@inheritDoc}
792   */
793  @Override()
794  protected void addToCommandLine(final List<String> argStrings)
795  {
796    if (values != null)
797    {
798      final StringBuilder buffer = new StringBuilder();
799      for (final Control c : values)
800      {
801        argStrings.add(getIdentifierString());
802
803        if (isSensitive())
804        {
805          argStrings.add("***REDACTED***");
806          continue;
807        }
808
809        buffer.setLength(0);
810        buffer.append(c.getOID());
811        buffer.append(':');
812        buffer.append(c.isCritical());
813
814        if (c.hasValue())
815        {
816          final byte[] valueBytes = c.getValue().getValue();
817          if (StaticUtils.isPrintableString(valueBytes))
818          {
819            buffer.append(':');
820            buffer.append(c.getValue().stringValue());
821          }
822          else
823          {
824            buffer.append("::");
825            Base64.encode(valueBytes, buffer);
826          }
827        }
828
829        argStrings.add(buffer.toString());
830      }
831    }
832  }
833
834
835
836  /**
837   * {@inheritDoc}
838   */
839  @Override()
840  public void toString(final StringBuilder buffer)
841  {
842    buffer.append("ControlArgument(");
843    appendBasicToStringInfo(buffer);
844
845    if ((defaultValues != null) && (! defaultValues.isEmpty()))
846    {
847      if (defaultValues.size() == 1)
848      {
849        buffer.append(", defaultValue='");
850        buffer.append(defaultValues.get(0).toString());
851      }
852      else
853      {
854        buffer.append(", defaultValues={");
855
856        final Iterator<Control> iterator = defaultValues.iterator();
857        while (iterator.hasNext())
858        {
859          buffer.append('\'');
860          buffer.append(iterator.next().toString());
861          buffer.append('\'');
862
863          if (iterator.hasNext())
864          {
865            buffer.append(", ");
866          }
867        }
868
869        buffer.append('}');
870      }
871    }
872
873    buffer.append(')');
874  }
875}