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