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