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