001/*
002 * Copyright 2020-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-2024 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) 2020-2024 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.ldap.sdk.schema;
037
038
039
040import java.io.File;
041import java.io.OutputStream;
042import java.util.ArrayList;
043import java.util.EnumSet;
044import java.util.LinkedHashMap;
045import java.util.List;
046import java.util.Set;
047import java.util.concurrent.atomic.AtomicReference;
048
049import com.unboundid.ldap.sdk.ResultCode;
050import com.unboundid.ldap.sdk.Version;
051import com.unboundid.util.CommandLineTool;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.StaticUtils;
055import com.unboundid.util.ThreadSafety;
056import com.unboundid.util.ThreadSafetyLevel;
057import com.unboundid.util.args.ArgumentException;
058import com.unboundid.util.args.ArgumentParser;
059import com.unboundid.util.args.BooleanArgument;
060import com.unboundid.util.args.FileArgument;
061import com.unboundid.util.args.StringArgument;
062
063import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
064
065
066
067/**
068 * This class provides a command-line tool that may be used to validate
069 * definitions read from one or more schema files.  It uses the
070 * {@link SchemaValidator} to perform the core of the processing.
071 */
072@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
073public final class ValidateLDAPSchema
074       extends CommandLineTool
075{
076  /**
077   * The column at which long lines should be wrapped.
078   */
079  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
080
081
082
083  // A reference to the completion message for this tool.
084  @NotNull private final AtomicReference<String> completionMessage;
085
086  // Arguments used by this tool.
087  @Nullable private BooleanArgument allowAttributeTypesWithoutSyntax;
088  @Nullable private BooleanArgument allowElementsWithoutNames;
089  @Nullable private BooleanArgument allowEmptyDescriptions;
090  @Nullable private BooleanArgument allowMultipleEntriesPerSchemaFile;
091  @Nullable private BooleanArgument allowNonNumericOIDs;
092  @Nullable private BooleanArgument allowRedefiningElements;
093  @Nullable private BooleanArgument allowSchemaFilesInSubdirectories;
094  @Nullable private BooleanArgument allowStructuralObjectClassesWithoutSuperior;
095  @Nullable private BooleanArgument
096       rejectAttributeTypesWithoutEqualityMatchingRule;
097  @Nullable private BooleanArgument rejectObjectClassesWithMultipleSuperiors;
098  @Nullable private BooleanArgument useLenientNameValidation;
099  @Nullable private BooleanArgument useLenientOIDValidation;
100  @Nullable private FileArgument schemaPath;
101  @Nullable private StringArgument allowedElementType;
102  @Nullable private StringArgument allowUndefinedElementType;
103  @Nullable private StringArgument prohibitedElementType;
104
105
106
107  /**
108   * Runs this tool with the provided set of command-line arguments.
109   *
110   * @param  args  The command-line arguments provided to this program.
111   */
112  public static void main(@NotNull final String... args)
113  {
114    final ResultCode resultCode = main(System.out, System.err, args);
115    if (resultCode != ResultCode.SUCCESS)
116    {
117      System.exit(resultCode.intValue());
118    }
119  }
120
121
122
123  /**
124   * Runs this tool with the provided set of command-line arguments.
125   *
126   * @param  out   The output stream to use for standard output.  It may be
127   *               {@code null} if standard output should be suppressed.
128   * @param  err   The output stream to use for standard error.  It may be
129   *               {@code null} if standard error should be suppressed.
130   * @param  args  The command-line arguments provided to this program.
131   *
132   * @return  A result code that indicates whether processing completed
133   *          successfully.
134   */
135  @NotNull()
136  public static ResultCode main(@Nullable final OutputStream out,
137                                @Nullable final OutputStream err,
138                                @NotNull final String... args)
139  {
140    final ValidateLDAPSchema tool = new ValidateLDAPSchema(out, err);
141    return tool.runTool(args);
142  }
143
144
145
146  /**
147   * Creates a new instance of this tool with the provided output and error
148   * streams.
149   *
150   * @param  out  The output stream to use for standard output.  It may be
151   *              {@code null} if standard output should be suppressed.
152   * @param  err  The output stream to use for standard error.  It may be
153   *              {@code null} if standard error should be suppressed.
154   */
155  public ValidateLDAPSchema(@Nullable final OutputStream out,
156                            @Nullable final OutputStream err)
157  {
158    super(out, err);
159
160    completionMessage = new AtomicReference<>();
161
162    allowAttributeTypesWithoutSyntax = null;
163    allowElementsWithoutNames = null;
164    allowEmptyDescriptions = null;
165    allowMultipleEntriesPerSchemaFile = null;
166    allowNonNumericOIDs = null;
167    allowRedefiningElements = null;
168    allowSchemaFilesInSubdirectories = null;
169    allowStructuralObjectClassesWithoutSuperior = null;
170    rejectAttributeTypesWithoutEqualityMatchingRule = null;
171    rejectObjectClassesWithMultipleSuperiors = null;
172    useLenientNameValidation = null;
173    useLenientOIDValidation = null;
174    schemaPath = null;
175    allowedElementType = null;
176    allowUndefinedElementType = null;
177    prohibitedElementType = null;
178  }
179
180
181
182  /**
183   * {@inheritDoc}
184   */
185  @Override()
186  @NotNull()
187  public String getToolName()
188  {
189    return "validate-ldap-schema";
190  }
191
192
193
194  /**
195   * {@inheritDoc}
196   */
197  @Override()
198  @NotNull()
199  public String getToolDescription()
200  {
201    return INFO_VALIDATE_SCHEMA_TOOL_DESCRIPTION.get();
202  }
203
204
205
206  /**
207   * {@inheritDoc}
208   */
209  @Override()
210  @NotNull()
211  public String getToolVersion()
212  {
213    return Version.NUMERIC_VERSION_STRING;
214  }
215
216
217
218  /**
219   * {@inheritDoc}
220   */
221  @Override()
222  public boolean supportsInteractiveMode()
223  {
224    return true;
225  }
226
227
228
229  /**
230   * {@inheritDoc}
231   */
232  @Override()
233  public boolean defaultsToInteractiveMode()
234  {
235    return (! SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_AVAILABLE);
236  }
237
238
239
240  /**
241   * {@inheritDoc}
242   */
243  @Override()
244  public boolean supportsPropertiesFile()
245  {
246    return true;
247  }
248
249
250
251  /**
252   * {@inheritDoc}
253   */
254  @Override()
255  protected boolean supportsOutputFile()
256  {
257    return true;
258  }
259
260
261
262  /**
263   * {@inheritDoc}
264   */
265  @Override()
266  protected boolean logToolInvocationByDefault()
267  {
268    return false;
269  }
270
271
272
273  /**
274   * {@inheritDoc}
275   */
276  @Override()
277  @Nullable()
278  protected String getToolCompletionMessage()
279  {
280    return completionMessage.get();
281  }
282
283
284
285  /**
286   * {@inheritDoc}
287   */
288  @Override()
289  public void addToolArguments(@NotNull final ArgumentParser parser)
290         throws ArgumentException
291  {
292    final boolean pingIdentityDSAvailable =
293         SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_AVAILABLE;
294
295
296    final List<File> defaultSchemaPaths = new ArrayList<>(1);
297    if (pingIdentityDSAvailable &&
298         (SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_SCHEMA_DIR != null))
299    {
300      defaultSchemaPaths.add(
301           SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_SCHEMA_DIR);
302    }
303
304    schemaPath = new FileArgument(null, "schema-path", true, 0, null,
305         INFO_VALIDATE_SCHEMA_ARG_DESC_SCHEMA_PATH.get(), true, true, false,
306         false, defaultSchemaPaths);
307    schemaPath.addLongIdentifier("schemaPath", true);
308    schemaPath.addLongIdentifier("schema-file", true);
309    schemaPath.addLongIdentifier("schemaFile", true);
310    schemaPath.addLongIdentifier("schema-directory", true);
311    schemaPath.addLongIdentifier("schemaDirectory", true);
312    schemaPath.addLongIdentifier("schema-dir", true);
313    schemaPath.addLongIdentifier("schemaDir", true);
314    schemaPath.addLongIdentifier("file", true);
315    schemaPath.addLongIdentifier("directory", true);
316    schemaPath.addLongIdentifier("path", true);
317    schemaPath.setArgumentGroupName(INFO_VALIDATE_SCHEMA_ARG_GROUP_INPUT.get());
318    parser.addArgument(schemaPath);
319
320
321    allowMultipleEntriesPerSchemaFile = new BooleanArgument(null,
322         "allow-multiple-entries-per-schema-file", 1,
323         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_MULTIPLE_ENTRIES.get());
324    allowMultipleEntriesPerSchemaFile.addLongIdentifier(
325         "allowMultipleEntriesPerSchemaFile", true);
326    allowMultipleEntriesPerSchemaFile.setArgumentGroupName(
327         INFO_VALIDATE_SCHEMA_ARG_GROUP_INPUT.get());
328    parser.addArgument(allowMultipleEntriesPerSchemaFile);
329
330
331    allowSchemaFilesInSubdirectories = new BooleanArgument(null,
332         "allow-schema-files-in-subdirectories", 1,
333         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_SUB_DIRS.get());
334    allowSchemaFilesInSubdirectories.addLongIdentifier(
335         "allow-schema-files-in-sub-directories", true);
336    allowSchemaFilesInSubdirectories.addLongIdentifier(
337         "allowSchemaFilesInSubDirectories", true);
338    allowSchemaFilesInSubdirectories.setArgumentGroupName(
339         INFO_VALIDATE_SCHEMA_ARG_GROUP_INPUT.get());
340    parser.addArgument(allowSchemaFilesInSubdirectories);
341
342
343    allowedElementType = new StringArgument(null, "allowed-element-type", false,
344         0, INFO_VALIDATE_SCHEMA_ARG_PLACEHOLDER_ELEMENT_TYPE.get(),
345         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOWED_ELEMENT_TYPE.get());
346    allowedElementType.addLongIdentifier("allowedElementType", true);
347    allowedElementType.addLongIdentifier("allowed-schema-element-type", true);
348    allowedElementType.addLongIdentifier("allowedSchemaElementType", true);
349    allowedElementType.addLongIdentifier("allow-element-type", true);
350    allowedElementType.addLongIdentifier("allowElementType", true);
351    allowedElementType.addLongIdentifier("allow-schema-element-type", true);
352    allowedElementType.addLongIdentifier("allowSchemaElementType", true);
353    allowedElementType.addLongIdentifier("allowed-element", true);
354    allowedElementType.addLongIdentifier("allowedElement", true);
355    allowedElementType.addLongIdentifier("allow-element", true);
356    allowedElementType.addLongIdentifier("allowElement", true);
357    allowedElementType.setArgumentGroupName(
358         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
359    parser.addArgument(allowedElementType);
360
361
362    prohibitedElementType = new StringArgument(null, "prohibited-element-type",
363         false, 0, INFO_VALIDATE_SCHEMA_ARG_PLACEHOLDER_ELEMENT_TYPE.get(),
364         INFO_VALIDATE_SCHEMA_ARG_DESC_PROHIBITED_ELEMENT_TYPE.get());
365    prohibitedElementType.addLongIdentifier("prohibitedElementType", true);
366    prohibitedElementType.addLongIdentifier("prohibited-schema-element-type",
367         true);
368    prohibitedElementType.addLongIdentifier("prohibitedSchemaElementType",
369         true);
370    prohibitedElementType.addLongIdentifier("prohibit-element-type", true);
371    prohibitedElementType.addLongIdentifier("prohibitElementType", true);
372    prohibitedElementType.addLongIdentifier("prohibit-schema-element-type",
373         true);
374    prohibitedElementType.addLongIdentifier("prohibitSchemaElementType", true);
375    prohibitedElementType.addLongIdentifier("prohibited-element", true);
376    prohibitedElementType.addLongIdentifier("prohibitedElement", true);
377    prohibitedElementType.addLongIdentifier("prohibit-element", true);
378    prohibitedElementType.addLongIdentifier("prohibitElement", true);
379    prohibitedElementType.setArgumentGroupName(
380         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
381    parser.addArgument(prohibitedElementType);
382
383
384    allowRedefiningElements = new BooleanArgument(null,
385         "allow-redefining-elements", 1,
386         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_REDEFINING.get());
387    allowRedefiningElements.addLongIdentifier(
388         "allow-re-defining-elements", true);
389    allowRedefiningElements.addLongIdentifier(
390         "allowRedefiningElements", true);
391    allowRedefiningElements.addLongIdentifier(
392         "allow-redefining-schema-elements", true);
393    allowRedefiningElements.addLongIdentifier(
394         "allow-re-defining-schema-elements", true);
395    allowRedefiningElements.addLongIdentifier(
396         "allowRedefiningSchemaElements", true);
397    allowRedefiningElements.setArgumentGroupName(
398         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
399    parser.addArgument(allowRedefiningElements);
400
401
402    allowUndefinedElementType = new StringArgument(null,
403         "allow-undefined-element-type", false, 0,
404         INFO_VALIDATE_SCHEMA_ARG_PLACEHOLDER_ELEMENT_TYPE.get(),
405         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_UNDEFINED.get());
406    allowUndefinedElementType.addLongIdentifier("allowUndefinedElementType",
407         true);
408    allowUndefinedElementType.addLongIdentifier(
409         "allow-undefined-schema-element-type", true);
410    allowUndefinedElementType.addLongIdentifier(
411         "allowUndefinedSchemaElementType", true);
412    allowUndefinedElementType.addLongIdentifier(
413         "allowed-undefined-element-type", true);
414    allowUndefinedElementType.addLongIdentifier("allowedUndefinedElementType",
415         true);
416    allowUndefinedElementType.addLongIdentifier(
417         "allowed-undefined-schema-element-type", true);
418    allowUndefinedElementType.addLongIdentifier(
419         "allowedUndefinedSchemaElementType", true);
420    allowUndefinedElementType.addLongIdentifier("allow-undefined-element",
421         true);
422    allowUndefinedElementType.addLongIdentifier("allowUndefinedType", true);
423    allowUndefinedElementType.addLongIdentifier("allow-undefined-type", true);
424    allowUndefinedElementType.addLongIdentifier("allowUndefinedElement", true);
425    allowUndefinedElementType.addLongIdentifier("allowed-undefined-element",
426         true);
427    allowUndefinedElementType.addLongIdentifier("allowedUndefinedType", true);
428    allowUndefinedElementType.addLongIdentifier("allowed-undefined-type", true);
429    allowUndefinedElementType.addLongIdentifier("allowedUndefinedElement",
430         true);
431    allowUndefinedElementType.setArgumentGroupName(
432         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
433    parser.addArgument(allowUndefinedElementType);
434
435
436    useLenientOIDValidation = new BooleanArgument(null,
437         "use-lenient-oid-validation", 1,
438         INFO_VALIDATE_SCHEMA_ARG_DESC_LENIENT_OID.get());
439    useLenientOIDValidation.addLongIdentifier(
440         "useLenientOIDValidation", true);
441    useLenientOIDValidation.addLongIdentifier(
442         "allow-lenient-oid-validation", true);
443    useLenientOIDValidation.addLongIdentifier(
444         "allowLenientOIDValidation", true);
445    useLenientOIDValidation.addLongIdentifier(
446         "lenient-oid-validation", true);
447    useLenientOIDValidation.addLongIdentifier(
448         "lenientOIDValidation", true);
449    useLenientOIDValidation.setArgumentGroupName(
450         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
451    parser.addArgument(useLenientOIDValidation);
452
453
454    allowNonNumericOIDs = new BooleanArgument(null, "allow-non-numeric-oids",
455         1, INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_NON_NUMERIC_OID.get());
456    allowNonNumericOIDs.addLongIdentifier("allow-nonnumeric-oids", true);
457    allowNonNumericOIDs.addLongIdentifier("allowNonNumericOIDs", true);
458    allowNonNumericOIDs.addLongIdentifier("allow-non-numeric-oid", true);
459    allowNonNumericOIDs.addLongIdentifier("allow-nonnumeric-oid", true);
460    allowNonNumericOIDs.addLongIdentifier("allowNonNumericOID", true);
461    allowNonNumericOIDs.setArgumentGroupName(
462         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
463    parser.addArgument(allowNonNumericOIDs);
464
465
466    allowElementsWithoutNames = new BooleanArgument(null,
467         "allow-elements-without-names", 1,
468         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_MISSING_NAME.get());
469    allowElementsWithoutNames.addLongIdentifier("allowElementsWithoutNames",
470         true);
471    allowElementsWithoutNames.addLongIdentifier(
472         "allow-schema-elements-without-names", true);
473    allowElementsWithoutNames.addLongIdentifier(
474         "allowSchemaElementsWithoutNames", true);
475    allowElementsWithoutNames.addLongIdentifier("allow-elements-missing-names",
476         true);
477    allowElementsWithoutNames.addLongIdentifier("allowElementsMissingNames",
478         true);
479    allowElementsWithoutNames.addLongIdentifier(
480         "allow-schema-elements-missing-names", true);
481    allowElementsWithoutNames.addLongIdentifier(
482         "allowSchemaElementsMissingNames", true);
483    allowElementsWithoutNames.addLongIdentifier("allow-missing-names",
484         true);
485    allowElementsWithoutNames.addLongIdentifier("allowEMissingNames",
486         true);
487    allowElementsWithoutNames.setArgumentGroupName(
488         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
489    parser.addArgument(allowElementsWithoutNames);
490
491
492    useLenientNameValidation = new BooleanArgument(null,
493         "use-lenient-name-validation", 1,
494         INFO_VALIDATE_SCHEMA_ARG_DESC_LENIENT_NAMES.get());
495    useLenientNameValidation.addLongIdentifier("useLenientNameValidation",
496         true);
497    useLenientNameValidation.addLongIdentifier("allow-lenient-name-validation",
498         true);
499    useLenientNameValidation.addLongIdentifier("allowLenientNameValidation",
500         true);
501    useLenientNameValidation.addLongIdentifier("lenient-name-validation", true);
502    useLenientNameValidation.addLongIdentifier("lenientNameValidation", true);
503    useLenientNameValidation.setArgumentGroupName(
504         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
505    parser.addArgument(useLenientNameValidation);
506
507
508    allowAttributeTypesWithoutSyntax = new BooleanArgument(null,
509         "allow-attribute-types-without-syntax", 1,
510         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_AT_WITHOUT_SYNTAX.get());
511    allowAttributeTypesWithoutSyntax.addLongIdentifier(
512         "allowAttributeTypesWithoutSyntax", true);
513    allowAttributeTypesWithoutSyntax.addLongIdentifier(
514         "allow-attribute-type-without-syntax", true);
515    allowAttributeTypesWithoutSyntax.addLongIdentifier(
516         "allowAttributeTypeWithoutSyntax", true);
517    allowAttributeTypesWithoutSyntax.addLongIdentifier(
518         "allow-attribute-types-missing-Syntax", true);
519    allowAttributeTypesWithoutSyntax.addLongIdentifier(
520         "allowAttributeTypesMissingSyntax", true);
521    allowAttributeTypesWithoutSyntax.addLongIdentifier(
522         "allow-attribute-type-missing-syntax", true);
523    allowAttributeTypesWithoutSyntax.addLongIdentifier(
524         "allowAttributeTypeMissingSyntax", true);
525    allowAttributeTypesWithoutSyntax.setArgumentGroupName(
526         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
527    parser.addArgument(allowAttributeTypesWithoutSyntax);
528
529
530    rejectAttributeTypesWithoutEqualityMatchingRule = new BooleanArgument(null,
531         "reject-attribute-types-without-equality-matching-rule", 1,
532         INFO_VALIDATE_SCHEMA_ARG_DESC_REJECT_AT_WITHOUT_EQ_MR.get());
533    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
534         "rejectAttributeTypesWithoutEqualityMatchingRule", true);
535    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
536         "reject-attribute-type-without-equality-matching-rule", true);
537    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
538         "rejectAttributeTypeWithoutEqualityMatchingRule", true);
539    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
540         "reject-attribute-types-missing-equality-matching-rule", true);
541    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
542         "rejectAttributeTypesMissingEqualityMatchingRule", true);
543    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
544         "reject-attribute-type-missing-equality-matching-rule", true);
545    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
546         "rejectAttributeTypeMissingEqualityMatchingRule", true);
547    rejectAttributeTypesWithoutEqualityMatchingRule.setArgumentGroupName(
548         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
549    parser.addArgument(rejectAttributeTypesWithoutEqualityMatchingRule);
550
551
552    allowStructuralObjectClassesWithoutSuperior = new BooleanArgument(null,
553         "allow-structural-object-classes-without-superior", 1,
554         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_MISSING_MISSING_OC_SUP.get());
555    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
556         "allow-structural-objectclasses-without-superior", true);
557    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
558         "allowStructuralObjectClassesWithoutSuperior", true);
559    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
560         "allow-structural-object-class-without-superior", true);
561    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
562         "allow-structural-objectclass-without-superior", true);
563    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
564         "allowStructuralObjectClassWithoutSuperior", true);
565    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
566         "allow-structural-classes-without-superior", true);
567    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
568         "allowStructuralClassesWithoutSuperior", true);
569    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
570         "allow-structural-class-without-superior", true);
571    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
572         "allowStructuralClassWithoutSuperior", true);
573    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
574         "allow-object-classes-without-superior", true);
575    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
576         "allow-objectclasses-without-superior", true);
577    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
578         "allowObjectClassesWithoutSuperior", true);
579    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
580         "allow-object-class-without-superior", true);
581    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
582         "allow-objectclass-without-superior", true);
583    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
584         "allowObjectClassWithoutSuperior", true);
585    allowStructuralObjectClassesWithoutSuperior.setArgumentGroupName(
586         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
587    parser.addArgument(allowStructuralObjectClassesWithoutSuperior);
588
589
590    rejectObjectClassesWithMultipleSuperiors = new BooleanArgument(null,
591         "reject-object-classes-with-multiple-superiors", 1,
592         INFO_VALIDATE_SCHEMA_ARG_DESC_REJECT_MULTIPLE_OC_SUP.get());
593    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
594         "reject-objectclasses-with-multiple-superiors", true);
595    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
596         "rejectObjectClassesWithMultipleSuperiors", true);
597    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
598         "reject-object-class-with-multiple-superiors", true);
599    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
600         "reject-objectclass-with-multiple-superiors", true);
601    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
602         "rejectObjectClassWithMultipleSuperiors", true);
603    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
604         "reject-object-classes-with-multiple-superior-classes", true);
605    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
606         "reject-objectclasses-with-multiple-superior-classes", true);
607    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
608         "rejectObjectClassesWithMultipleSuperiorClasses", true);
609    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
610         "reject-object-class-with-multiple-superior-classes", true);
611    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
612         "reject-objectclass-with-multiple-superior-classes", true);
613    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
614         "rejectObjectClassWithMultipleSuperiorClasses", true);
615    rejectObjectClassesWithMultipleSuperiors.setArgumentGroupName(
616         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
617    if (pingIdentityDSAvailable)
618    {
619      rejectObjectClassesWithMultipleSuperiors.setHidden(true);
620    }
621    parser.addArgument(rejectObjectClassesWithMultipleSuperiors);
622
623
624    allowEmptyDescriptions = new BooleanArgument(null,
625         "allow-empty-descriptions", 1,
626         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_EMPTY_DESC.get());
627    allowEmptyDescriptions.addLongIdentifier("allowEmptyDescriptions", true);
628    allowEmptyDescriptions.addLongIdentifier("allow-empty-description", true);
629    allowEmptyDescriptions.addLongIdentifier("allowEmptyDescription", true);
630    allowEmptyDescriptions.addLongIdentifier("allow-empty-desc", true);
631    allowEmptyDescriptions.addLongIdentifier("allowEmptyDESC", true);
632    allowEmptyDescriptions.setArgumentGroupName(
633         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
634    parser.addArgument(allowEmptyDescriptions);
635
636
637    // The allowed and prohibited schema element type arguments cannot be used
638    // together.
639    parser.addExclusiveArgumentSet(allowedElementType, prohibitedElementType);
640  }
641
642
643
644  /**
645   * {@inheritDoc}
646   */
647  @Override()
648  public void doExtendedArgumentValidation()
649         throws ArgumentException
650  {
651    // If the allowed element type argument is present, then validate its
652    // values.
653    if (allowedElementType.isPresent())
654    {
655      for (final String value : allowedElementType.getValues())
656      {
657        if (SchemaElementType.forName(value) == null)
658        {
659          final String message = ERR_VALIDATE_SCHEMA_NO_SUCH_ELEMENT_TYPE.get(
660               value, allowedElementType.getIdentifierString());
661          completionMessage.set(message);
662          throw new ArgumentException(message);
663        }
664      }
665    }
666
667
668    // If the prohibited element type argument is present, then validate its
669    // values, and make sure that not all element types are prohibited.
670    if (prohibitedElementType.isPresent())
671    {
672      final Set<SchemaElementType> allowedTypes =
673           EnumSet.allOf(SchemaElementType.class);
674
675      for (final String value : prohibitedElementType.getValues())
676      {
677        final SchemaElementType type = SchemaElementType.forName(value);
678        if (type == null)
679        {
680          final String message = ERR_VALIDATE_SCHEMA_NO_SUCH_ELEMENT_TYPE.get(
681               value, prohibitedElementType.getIdentifierString());
682          completionMessage.set(message);
683          throw new ArgumentException(message);
684        }
685        else
686        {
687          allowedTypes.remove(type);
688        }
689      }
690
691      if (allowedTypes.isEmpty())
692      {
693        final String message =
694             ERR_VALIDATE_SCHEMA_ALL_ELEMENT_TYPES_PROHIBITED.get(
695                  prohibitedElementType.getIdentifierString());
696        completionMessage.set(message);
697        throw new ArgumentException(message);
698      }
699    }
700
701
702    if (allowUndefinedElementType.isPresent())
703    {
704      for (final String value : allowUndefinedElementType.getValues())
705      {
706        if (SchemaElementType.forName(value) == null)
707        {
708          final String message = ERR_VALIDATE_SCHEMA_NO_SUCH_ELEMENT_TYPE.get(
709               value, allowUndefinedElementType.getIdentifierString());
710          completionMessage.set(message);
711          throw new ArgumentException(message);
712        }
713      }
714    }
715  }
716
717
718
719  /**
720   * {@inheritDoc}
721   */
722  @Override()
723  @NotNull()
724  public ResultCode doToolProcessing()
725  {
726    // Create the schema validator instance.
727    final SchemaValidator schemaValidator = new SchemaValidator();
728
729    schemaValidator.setEnsureSchemaEntryIsValid(true);
730    schemaValidator.setAllowInvalidObjectClassInheritance(false);
731    schemaValidator.setAllowCollectiveAttributes(true);
732    schemaValidator.setAllowObsoleteElements(true);
733
734    schemaValidator.setAllowMultipleEntriesPerFile(
735         allowMultipleEntriesPerSchemaFile.isPresent());
736    schemaValidator.setAllowSchemaFilesInSubDirectories(
737         allowSchemaFilesInSubdirectories.isPresent());
738    schemaValidator.setAllowRedefiningElements(
739         allowRedefiningElements.isPresent());
740    schemaValidator.setAllowElementsWithoutNames(
741         allowElementsWithoutNames.isPresent());
742    schemaValidator.setOIDValidation(
743         allowNonNumericOIDs.isPresent(),
744         allowNonNumericOIDs.isPresent(),
745         (! useLenientOIDValidation.isPresent()));
746    schemaValidator.setAllowNamesWithInitialDigit(
747         useLenientNameValidation.isPresent());
748    schemaValidator.setAllowNamesWithInitialHyphen(
749         useLenientNameValidation.isPresent());
750    schemaValidator.setAllowNamesWithUnderscore(
751         useLenientNameValidation.isPresent());
752    schemaValidator.setAllowEmptyDescription(
753         allowEmptyDescriptions.isPresent());
754    schemaValidator.setAllowAttributeTypesWithoutSyntax(
755         allowAttributeTypesWithoutSyntax.isPresent());
756    schemaValidator.setAllowAttributeTypesWithoutEqualityMatchingRule(
757         ! rejectAttributeTypesWithoutEqualityMatchingRule.isPresent());
758    schemaValidator.setAllowStructuralObjectClassWithoutSuperior(
759         allowStructuralObjectClassesWithoutSuperior.isPresent());
760    schemaValidator.setAllowMultipleSuperiorObjectClasses(
761         ! rejectObjectClassesWithMultipleSuperiors.isPresent());
762
763    if (allowedElementType.isPresent())
764    {
765      final Set<SchemaElementType> allowedTypes =
766           EnumSet.noneOf(SchemaElementType.class);
767      for (final String value : allowedElementType.getValues())
768      {
769        allowedTypes.add(SchemaElementType.forName(value));
770      }
771
772      schemaValidator.setAllowedSchemaElementTypes(allowedTypes);
773    }
774    else if (prohibitedElementType.isPresent())
775    {
776      final Set<SchemaElementType> allowedTypes =
777           EnumSet.allOf(SchemaElementType.class);
778      for (final String value : prohibitedElementType.getValues())
779      {
780        allowedTypes.remove(SchemaElementType.forName(value));
781      }
782
783      schemaValidator.setAllowedSchemaElementTypes(allowedTypes);
784    }
785
786    if (allowUndefinedElementType.isPresent())
787    {
788      final Set<SchemaElementType> elementTypes =
789           EnumSet.noneOf(SchemaElementType.class);
790      for (final String value : allowUndefinedElementType.getValues())
791      {
792        elementTypes.add(SchemaElementType.forName(value));
793      }
794
795      schemaValidator.setAllowReferencesToUndefinedElementTypes(elementTypes);
796    }
797
798
799    // Use the schema validator to parse the scheme elements in the provided
800    // paths.
801    Schema schema = null;
802    final List<String> errorMessages = new ArrayList<>();
803    for (final File f : schemaPath.getValues())
804    {
805      schema = schemaValidator.validateSchema(f, schema, errorMessages);
806    }
807
808
809    // If we ended up with an empty set of error messages, then return a success
810    // result.
811    final int numErrors = errorMessages.size();
812    if (numErrors == 0)
813    {
814        completionMessage.set(INFO_VALIDATE_SCHEMA_NO_ERRORS.get());
815        wrapOut(0, WRAP_COLUMN, INFO_VALIDATE_SCHEMA_NO_ERRORS.get());
816        return ResultCode.SUCCESS;
817    }
818
819
820    // If we've gotten here, then there were errors.  Display them and get the
821    // final string to use as the completion message.
822    final String finalMessage;
823    if (numErrors == 1)
824    {
825      wrapErr(0, WRAP_COLUMN, ERR_VALIDATE_SCHEMA_ERROR_FOUND.get());
826      finalMessage = ERR_VALIDATE_SCHEMA_ONE_ERROR.get();
827    }
828    else
829    {
830      wrapErr(0, WRAP_COLUMN, ERR_VALIDATE_SCHEMA_ERRORS_FOUND.get());
831      finalMessage = ERR_VALIDATE_SCHEMA_MULTIPLE_ERRORS.get(numErrors);
832    }
833
834    for (final String errorMessage : errorMessages)
835    {
836      err();
837
838      boolean firstLine = true;
839      for (final String line :
840           StaticUtils.wrapLine(errorMessage, (WRAP_COLUMN - 2)))
841      {
842        if (firstLine)
843        {
844          err("* " + line);
845          firstLine = false;
846        }
847        else
848        {
849          err("  " + line);
850        }
851      }
852    }
853
854    completionMessage.set(finalMessage);
855
856    err();
857    wrapErr(0, WRAP_COLUMN, finalMessage);
858
859    return ResultCode.DECODING_ERROR;
860  }
861
862
863
864  /**
865   * {@inheritDoc}
866   */
867  @Override()
868  @NotNull()
869  public LinkedHashMap<String[],String> getExampleUsages()
870  {
871    final LinkedHashMap<String[],String> examples = new LinkedHashMap<>();
872
873    examples.put(
874         new String[]
875         {
876           "--schema-path", "/path/to/schema"
877         },
878         INFO_VALIDATE_SCHEMA_EXAMPLE_1.get());
879
880    examples.put(
881         new String[]
882         {
883           "--schema-path", "/path/to/schema",
884           "--allow-multiple-entries-per-schema-file",
885           "--allow-schema-files-in-subdirectories",
886           "--allow-redefining-elements",
887           "--allow-undefined-element-type", "attribute-syntax",
888           "--allow-undefined-element-type", "matching-rule",
889           "--use-lenient-oid-validation",
890           "--allow-non-numeric-oids",
891           "--allow-elements-without-names",
892           "--use-lenient-name-validation",
893           "--allow-attribute-types-without-syntax",
894           "--allow-structural-object-classes-without-superior",
895           "--allow-empty-descriptions"
896         },
897         INFO_VALIDATE_SCHEMA_EXAMPLE_2.get());
898
899    return examples;
900  }
901}