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