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 supportsDebugLogging()
267  {
268    return true;
269  }
270
271
272
273  /**
274   * {@inheritDoc}
275   */
276  @Override()
277  protected boolean logToolInvocationByDefault()
278  {
279    return false;
280  }
281
282
283
284  /**
285   * {@inheritDoc}
286   */
287  @Override()
288  @Nullable()
289  protected String getToolCompletionMessage()
290  {
291    return completionMessage.get();
292  }
293
294
295
296  /**
297   * {@inheritDoc}
298   */
299  @Override()
300  public void addToolArguments(@NotNull final ArgumentParser parser)
301         throws ArgumentException
302  {
303    final boolean pingIdentityDSAvailable =
304         SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_AVAILABLE;
305
306
307    final List<File> defaultSchemaPaths = new ArrayList<>(1);
308    if (pingIdentityDSAvailable &&
309         (SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_SCHEMA_DIR != null))
310    {
311      defaultSchemaPaths.add(
312           SchemaValidator.PING_IDENTITY_DIRECTORY_SERVER_SCHEMA_DIR);
313    }
314
315    schemaPath = new FileArgument(null, "schema-path", true, 0, null,
316         INFO_VALIDATE_SCHEMA_ARG_DESC_SCHEMA_PATH.get(), true, true, false,
317         false, defaultSchemaPaths);
318    schemaPath.addLongIdentifier("schemaPath", true);
319    schemaPath.addLongIdentifier("schema-file", true);
320    schemaPath.addLongIdentifier("schemaFile", true);
321    schemaPath.addLongIdentifier("schema-directory", true);
322    schemaPath.addLongIdentifier("schemaDirectory", true);
323    schemaPath.addLongIdentifier("schema-dir", true);
324    schemaPath.addLongIdentifier("schemaDir", true);
325    schemaPath.addLongIdentifier("file", true);
326    schemaPath.addLongIdentifier("directory", true);
327    schemaPath.addLongIdentifier("path", true);
328    schemaPath.setArgumentGroupName(INFO_VALIDATE_SCHEMA_ARG_GROUP_INPUT.get());
329    parser.addArgument(schemaPath);
330
331
332    allowMultipleEntriesPerSchemaFile = new BooleanArgument(null,
333         "allow-multiple-entries-per-schema-file", 1,
334         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_MULTIPLE_ENTRIES.get());
335    allowMultipleEntriesPerSchemaFile.addLongIdentifier(
336         "allowMultipleEntriesPerSchemaFile", true);
337    allowMultipleEntriesPerSchemaFile.setArgumentGroupName(
338         INFO_VALIDATE_SCHEMA_ARG_GROUP_INPUT.get());
339    parser.addArgument(allowMultipleEntriesPerSchemaFile);
340
341
342    allowSchemaFilesInSubdirectories = new BooleanArgument(null,
343         "allow-schema-files-in-subdirectories", 1,
344         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_SUB_DIRS.get());
345    allowSchemaFilesInSubdirectories.addLongIdentifier(
346         "allow-schema-files-in-sub-directories", true);
347    allowSchemaFilesInSubdirectories.addLongIdentifier(
348         "allowSchemaFilesInSubDirectories", true);
349    allowSchemaFilesInSubdirectories.setArgumentGroupName(
350         INFO_VALIDATE_SCHEMA_ARG_GROUP_INPUT.get());
351    parser.addArgument(allowSchemaFilesInSubdirectories);
352
353
354    allowedElementType = new StringArgument(null, "allowed-element-type", false,
355         0, INFO_VALIDATE_SCHEMA_ARG_PLACEHOLDER_ELEMENT_TYPE.get(),
356         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOWED_ELEMENT_TYPE.get());
357    allowedElementType.addLongIdentifier("allowedElementType", true);
358    allowedElementType.addLongIdentifier("allowed-schema-element-type", true);
359    allowedElementType.addLongIdentifier("allowedSchemaElementType", true);
360    allowedElementType.addLongIdentifier("allow-element-type", true);
361    allowedElementType.addLongIdentifier("allowElementType", true);
362    allowedElementType.addLongIdentifier("allow-schema-element-type", true);
363    allowedElementType.addLongIdentifier("allowSchemaElementType", true);
364    allowedElementType.addLongIdentifier("allowed-element", true);
365    allowedElementType.addLongIdentifier("allowedElement", true);
366    allowedElementType.addLongIdentifier("allow-element", true);
367    allowedElementType.addLongIdentifier("allowElement", true);
368    allowedElementType.setArgumentGroupName(
369         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
370    parser.addArgument(allowedElementType);
371
372
373    prohibitedElementType = new StringArgument(null, "prohibited-element-type",
374         false, 0, INFO_VALIDATE_SCHEMA_ARG_PLACEHOLDER_ELEMENT_TYPE.get(),
375         INFO_VALIDATE_SCHEMA_ARG_DESC_PROHIBITED_ELEMENT_TYPE.get());
376    prohibitedElementType.addLongIdentifier("prohibitedElementType", true);
377    prohibitedElementType.addLongIdentifier("prohibited-schema-element-type",
378         true);
379    prohibitedElementType.addLongIdentifier("prohibitedSchemaElementType",
380         true);
381    prohibitedElementType.addLongIdentifier("prohibit-element-type", true);
382    prohibitedElementType.addLongIdentifier("prohibitElementType", true);
383    prohibitedElementType.addLongIdentifier("prohibit-schema-element-type",
384         true);
385    prohibitedElementType.addLongIdentifier("prohibitSchemaElementType", true);
386    prohibitedElementType.addLongIdentifier("prohibited-element", true);
387    prohibitedElementType.addLongIdentifier("prohibitedElement", true);
388    prohibitedElementType.addLongIdentifier("prohibit-element", true);
389    prohibitedElementType.addLongIdentifier("prohibitElement", true);
390    prohibitedElementType.setArgumentGroupName(
391         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
392    parser.addArgument(prohibitedElementType);
393
394
395    allowRedefiningElements = new BooleanArgument(null,
396         "allow-redefining-elements", 1,
397         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_REDEFINING.get());
398    allowRedefiningElements.addLongIdentifier(
399         "allow-re-defining-elements", true);
400    allowRedefiningElements.addLongIdentifier(
401         "allowRedefiningElements", true);
402    allowRedefiningElements.addLongIdentifier(
403         "allow-redefining-schema-elements", true);
404    allowRedefiningElements.addLongIdentifier(
405         "allow-re-defining-schema-elements", true);
406    allowRedefiningElements.addLongIdentifier(
407         "allowRedefiningSchemaElements", true);
408    allowRedefiningElements.setArgumentGroupName(
409         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
410    parser.addArgument(allowRedefiningElements);
411
412
413    allowUndefinedElementType = new StringArgument(null,
414         "allow-undefined-element-type", false, 0,
415         INFO_VALIDATE_SCHEMA_ARG_PLACEHOLDER_ELEMENT_TYPE.get(),
416         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_UNDEFINED.get());
417    allowUndefinedElementType.addLongIdentifier("allowUndefinedElementType",
418         true);
419    allowUndefinedElementType.addLongIdentifier(
420         "allow-undefined-schema-element-type", true);
421    allowUndefinedElementType.addLongIdentifier(
422         "allowUndefinedSchemaElementType", true);
423    allowUndefinedElementType.addLongIdentifier(
424         "allowed-undefined-element-type", true);
425    allowUndefinedElementType.addLongIdentifier("allowedUndefinedElementType",
426         true);
427    allowUndefinedElementType.addLongIdentifier(
428         "allowed-undefined-schema-element-type", true);
429    allowUndefinedElementType.addLongIdentifier(
430         "allowedUndefinedSchemaElementType", true);
431    allowUndefinedElementType.addLongIdentifier("allow-undefined-element",
432         true);
433    allowUndefinedElementType.addLongIdentifier("allowUndefinedType", true);
434    allowUndefinedElementType.addLongIdentifier("allow-undefined-type", true);
435    allowUndefinedElementType.addLongIdentifier("allowUndefinedElement", true);
436    allowUndefinedElementType.addLongIdentifier("allowed-undefined-element",
437         true);
438    allowUndefinedElementType.addLongIdentifier("allowedUndefinedType", true);
439    allowUndefinedElementType.addLongIdentifier("allowed-undefined-type", true);
440    allowUndefinedElementType.addLongIdentifier("allowedUndefinedElement",
441         true);
442    allowUndefinedElementType.setArgumentGroupName(
443         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
444    parser.addArgument(allowUndefinedElementType);
445
446
447    useLenientOIDValidation = new BooleanArgument(null,
448         "use-lenient-oid-validation", 1,
449         INFO_VALIDATE_SCHEMA_ARG_DESC_LENIENT_OID.get());
450    useLenientOIDValidation.addLongIdentifier(
451         "useLenientOIDValidation", true);
452    useLenientOIDValidation.addLongIdentifier(
453         "allow-lenient-oid-validation", true);
454    useLenientOIDValidation.addLongIdentifier(
455         "allowLenientOIDValidation", true);
456    useLenientOIDValidation.addLongIdentifier(
457         "lenient-oid-validation", true);
458    useLenientOIDValidation.addLongIdentifier(
459         "lenientOIDValidation", true);
460    useLenientOIDValidation.setArgumentGroupName(
461         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
462    parser.addArgument(useLenientOIDValidation);
463
464
465    allowNonNumericOIDs = new BooleanArgument(null, "allow-non-numeric-oids",
466         1, INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_NON_NUMERIC_OID.get());
467    allowNonNumericOIDs.addLongIdentifier("allow-nonnumeric-oids", true);
468    allowNonNumericOIDs.addLongIdentifier("allowNonNumericOIDs", true);
469    allowNonNumericOIDs.addLongIdentifier("allow-non-numeric-oid", true);
470    allowNonNumericOIDs.addLongIdentifier("allow-nonnumeric-oid", true);
471    allowNonNumericOIDs.addLongIdentifier("allowNonNumericOID", true);
472    allowNonNumericOIDs.setArgumentGroupName(
473         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
474    parser.addArgument(allowNonNumericOIDs);
475
476
477    allowElementsWithoutNames = new BooleanArgument(null,
478         "allow-elements-without-names", 1,
479         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_MISSING_NAME.get());
480    allowElementsWithoutNames.addLongIdentifier("allowElementsWithoutNames",
481         true);
482    allowElementsWithoutNames.addLongIdentifier(
483         "allow-schema-elements-without-names", true);
484    allowElementsWithoutNames.addLongIdentifier(
485         "allowSchemaElementsWithoutNames", true);
486    allowElementsWithoutNames.addLongIdentifier("allow-elements-missing-names",
487         true);
488    allowElementsWithoutNames.addLongIdentifier("allowElementsMissingNames",
489         true);
490    allowElementsWithoutNames.addLongIdentifier(
491         "allow-schema-elements-missing-names", true);
492    allowElementsWithoutNames.addLongIdentifier(
493         "allowSchemaElementsMissingNames", true);
494    allowElementsWithoutNames.addLongIdentifier("allow-missing-names",
495         true);
496    allowElementsWithoutNames.addLongIdentifier("allowEMissingNames",
497         true);
498    allowElementsWithoutNames.setArgumentGroupName(
499         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
500    parser.addArgument(allowElementsWithoutNames);
501
502
503    useLenientNameValidation = new BooleanArgument(null,
504         "use-lenient-name-validation", 1,
505         INFO_VALIDATE_SCHEMA_ARG_DESC_LENIENT_NAMES.get());
506    useLenientNameValidation.addLongIdentifier("useLenientNameValidation",
507         true);
508    useLenientNameValidation.addLongIdentifier("allow-lenient-name-validation",
509         true);
510    useLenientNameValidation.addLongIdentifier("allowLenientNameValidation",
511         true);
512    useLenientNameValidation.addLongIdentifier("lenient-name-validation", true);
513    useLenientNameValidation.addLongIdentifier("lenientNameValidation", true);
514    useLenientNameValidation.setArgumentGroupName(
515         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
516    parser.addArgument(useLenientNameValidation);
517
518
519    allowAttributeTypesWithoutSyntax = new BooleanArgument(null,
520         "allow-attribute-types-without-syntax", 1,
521         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_AT_WITHOUT_SYNTAX.get());
522    allowAttributeTypesWithoutSyntax.addLongIdentifier(
523         "allowAttributeTypesWithoutSyntax", true);
524    allowAttributeTypesWithoutSyntax.addLongIdentifier(
525         "allow-attribute-type-without-syntax", true);
526    allowAttributeTypesWithoutSyntax.addLongIdentifier(
527         "allowAttributeTypeWithoutSyntax", true);
528    allowAttributeTypesWithoutSyntax.addLongIdentifier(
529         "allow-attribute-types-missing-Syntax", true);
530    allowAttributeTypesWithoutSyntax.addLongIdentifier(
531         "allowAttributeTypesMissingSyntax", true);
532    allowAttributeTypesWithoutSyntax.addLongIdentifier(
533         "allow-attribute-type-missing-syntax", true);
534    allowAttributeTypesWithoutSyntax.addLongIdentifier(
535         "allowAttributeTypeMissingSyntax", true);
536    allowAttributeTypesWithoutSyntax.setArgumentGroupName(
537         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
538    parser.addArgument(allowAttributeTypesWithoutSyntax);
539
540
541    rejectAttributeTypesWithoutEqualityMatchingRule = new BooleanArgument(null,
542         "reject-attribute-types-without-equality-matching-rule", 1,
543         INFO_VALIDATE_SCHEMA_ARG_DESC_REJECT_AT_WITHOUT_EQ_MR.get());
544    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
545         "rejectAttributeTypesWithoutEqualityMatchingRule", true);
546    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
547         "reject-attribute-type-without-equality-matching-rule", true);
548    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
549         "rejectAttributeTypeWithoutEqualityMatchingRule", true);
550    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
551         "reject-attribute-types-missing-equality-matching-rule", true);
552    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
553         "rejectAttributeTypesMissingEqualityMatchingRule", true);
554    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
555         "reject-attribute-type-missing-equality-matching-rule", true);
556    rejectAttributeTypesWithoutEqualityMatchingRule.addLongIdentifier(
557         "rejectAttributeTypeMissingEqualityMatchingRule", true);
558    rejectAttributeTypesWithoutEqualityMatchingRule.setArgumentGroupName(
559         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
560    parser.addArgument(rejectAttributeTypesWithoutEqualityMatchingRule);
561
562
563    allowStructuralObjectClassesWithoutSuperior = new BooleanArgument(null,
564         "allow-structural-object-classes-without-superior", 1,
565         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_MISSING_MISSING_OC_SUP.get());
566    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
567         "allow-structural-objectclasses-without-superior", true);
568    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
569         "allowStructuralObjectClassesWithoutSuperior", true);
570    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
571         "allow-structural-object-class-without-superior", true);
572    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
573         "allow-structural-objectclass-without-superior", true);
574    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
575         "allowStructuralObjectClassWithoutSuperior", true);
576    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
577         "allow-structural-classes-without-superior", true);
578    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
579         "allowStructuralClassesWithoutSuperior", true);
580    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
581         "allow-structural-class-without-superior", true);
582    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
583         "allowStructuralClassWithoutSuperior", true);
584    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
585         "allow-object-classes-without-superior", true);
586    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
587         "allow-objectclasses-without-superior", true);
588    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
589         "allowObjectClassesWithoutSuperior", true);
590    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
591         "allow-object-class-without-superior", true);
592    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
593         "allow-objectclass-without-superior", true);
594    allowStructuralObjectClassesWithoutSuperior.addLongIdentifier(
595         "allowObjectClassWithoutSuperior", true);
596    allowStructuralObjectClassesWithoutSuperior.setArgumentGroupName(
597         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
598    parser.addArgument(allowStructuralObjectClassesWithoutSuperior);
599
600
601    rejectObjectClassesWithMultipleSuperiors = new BooleanArgument(null,
602         "reject-object-classes-with-multiple-superiors", 1,
603         INFO_VALIDATE_SCHEMA_ARG_DESC_REJECT_MULTIPLE_OC_SUP.get());
604    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
605         "reject-objectclasses-with-multiple-superiors", true);
606    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
607         "rejectObjectClassesWithMultipleSuperiors", true);
608    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
609         "reject-object-class-with-multiple-superiors", true);
610    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
611         "reject-objectclass-with-multiple-superiors", true);
612    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
613         "rejectObjectClassWithMultipleSuperiors", true);
614    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
615         "reject-object-classes-with-multiple-superior-classes", true);
616    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
617         "reject-objectclasses-with-multiple-superior-classes", true);
618    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
619         "rejectObjectClassesWithMultipleSuperiorClasses", true);
620    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
621         "reject-object-class-with-multiple-superior-classes", true);
622    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
623         "reject-objectclass-with-multiple-superior-classes", true);
624    rejectObjectClassesWithMultipleSuperiors.addLongIdentifier(
625         "rejectObjectClassWithMultipleSuperiorClasses", true);
626    rejectObjectClassesWithMultipleSuperiors.setArgumentGroupName(
627         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
628    if (pingIdentityDSAvailable)
629    {
630      rejectObjectClassesWithMultipleSuperiors.setHidden(true);
631    }
632    parser.addArgument(rejectObjectClassesWithMultipleSuperiors);
633
634
635    allowEmptyDescriptions = new BooleanArgument(null,
636         "allow-empty-descriptions", 1,
637         INFO_VALIDATE_SCHEMA_ARG_DESC_ALLOW_EMPTY_DESC.get());
638    allowEmptyDescriptions.addLongIdentifier("allowEmptyDescriptions", true);
639    allowEmptyDescriptions.addLongIdentifier("allow-empty-description", true);
640    allowEmptyDescriptions.addLongIdentifier("allowEmptyDescription", true);
641    allowEmptyDescriptions.addLongIdentifier("allow-empty-desc", true);
642    allowEmptyDescriptions.addLongIdentifier("allowEmptyDESC", true);
643    allowEmptyDescriptions.setArgumentGroupName(
644         INFO_VALIDATE_SCHEMA_ARG_GROUP_VALIDATION.get());
645    parser.addArgument(allowEmptyDescriptions);
646
647
648    // The allowed and prohibited schema element type arguments cannot be used
649    // together.
650    parser.addExclusiveArgumentSet(allowedElementType, prohibitedElementType);
651  }
652
653
654
655  /**
656   * {@inheritDoc}
657   */
658  @Override()
659  public void doExtendedArgumentValidation()
660         throws ArgumentException
661  {
662    // If the allowed element type argument is present, then validate its
663    // values.
664    if (allowedElementType.isPresent())
665    {
666      for (final String value : allowedElementType.getValues())
667      {
668        if (SchemaElementType.forName(value) == null)
669        {
670          final String message = ERR_VALIDATE_SCHEMA_NO_SUCH_ELEMENT_TYPE.get(
671               value, allowedElementType.getIdentifierString());
672          completionMessage.set(message);
673          throw new ArgumentException(message);
674        }
675      }
676    }
677
678
679    // If the prohibited element type argument is present, then validate its
680    // values, and make sure that not all element types are prohibited.
681    if (prohibitedElementType.isPresent())
682    {
683      final Set<SchemaElementType> allowedTypes =
684           EnumSet.allOf(SchemaElementType.class);
685
686      for (final String value : prohibitedElementType.getValues())
687      {
688        final SchemaElementType type = SchemaElementType.forName(value);
689        if (type == null)
690        {
691          final String message = ERR_VALIDATE_SCHEMA_NO_SUCH_ELEMENT_TYPE.get(
692               value, prohibitedElementType.getIdentifierString());
693          completionMessage.set(message);
694          throw new ArgumentException(message);
695        }
696        else
697        {
698          allowedTypes.remove(type);
699        }
700      }
701
702      if (allowedTypes.isEmpty())
703      {
704        final String message =
705             ERR_VALIDATE_SCHEMA_ALL_ELEMENT_TYPES_PROHIBITED.get(
706                  prohibitedElementType.getIdentifierString());
707        completionMessage.set(message);
708        throw new ArgumentException(message);
709      }
710    }
711
712
713    if (allowUndefinedElementType.isPresent())
714    {
715      for (final String value : allowUndefinedElementType.getValues())
716      {
717        if (SchemaElementType.forName(value) == null)
718        {
719          final String message = ERR_VALIDATE_SCHEMA_NO_SUCH_ELEMENT_TYPE.get(
720               value, allowUndefinedElementType.getIdentifierString());
721          completionMessage.set(message);
722          throw new ArgumentException(message);
723        }
724      }
725    }
726  }
727
728
729
730  /**
731   * {@inheritDoc}
732   */
733  @Override()
734  @NotNull()
735  public ResultCode doToolProcessing()
736  {
737    // Create the schema validator instance.
738    final SchemaValidator schemaValidator = new SchemaValidator();
739
740    schemaValidator.setEnsureSchemaEntryIsValid(true);
741    schemaValidator.setAllowInvalidObjectClassInheritance(false);
742    schemaValidator.setAllowCollectiveAttributes(true);
743    schemaValidator.setAllowObsoleteElements(true);
744
745    schemaValidator.setAllowMultipleEntriesPerFile(
746         allowMultipleEntriesPerSchemaFile.isPresent());
747    schemaValidator.setAllowSchemaFilesInSubDirectories(
748         allowSchemaFilesInSubdirectories.isPresent());
749    schemaValidator.setAllowRedefiningElements(
750         allowRedefiningElements.isPresent());
751    schemaValidator.setAllowElementsWithoutNames(
752         allowElementsWithoutNames.isPresent());
753    schemaValidator.setOIDValidation(
754         allowNonNumericOIDs.isPresent(),
755         allowNonNumericOIDs.isPresent(),
756         (! useLenientOIDValidation.isPresent()));
757    schemaValidator.setAllowNamesWithInitialDigit(
758         useLenientNameValidation.isPresent());
759    schemaValidator.setAllowNamesWithInitialHyphen(
760         useLenientNameValidation.isPresent());
761    schemaValidator.setAllowNamesWithUnderscore(
762         useLenientNameValidation.isPresent());
763    schemaValidator.setAllowEmptyDescription(
764         allowEmptyDescriptions.isPresent());
765    schemaValidator.setAllowAttributeTypesWithoutSyntax(
766         allowAttributeTypesWithoutSyntax.isPresent());
767    schemaValidator.setAllowAttributeTypesWithoutEqualityMatchingRule(
768         ! rejectAttributeTypesWithoutEqualityMatchingRule.isPresent());
769    schemaValidator.setAllowStructuralObjectClassWithoutSuperior(
770         allowStructuralObjectClassesWithoutSuperior.isPresent());
771    schemaValidator.setAllowMultipleSuperiorObjectClasses(
772         ! rejectObjectClassesWithMultipleSuperiors.isPresent());
773
774    if (allowedElementType.isPresent())
775    {
776      final Set<SchemaElementType> allowedTypes =
777           EnumSet.noneOf(SchemaElementType.class);
778      for (final String value : allowedElementType.getValues())
779      {
780        allowedTypes.add(SchemaElementType.forName(value));
781      }
782
783      schemaValidator.setAllowedSchemaElementTypes(allowedTypes);
784    }
785    else if (prohibitedElementType.isPresent())
786    {
787      final Set<SchemaElementType> allowedTypes =
788           EnumSet.allOf(SchemaElementType.class);
789      for (final String value : prohibitedElementType.getValues())
790      {
791        allowedTypes.remove(SchemaElementType.forName(value));
792      }
793
794      schemaValidator.setAllowedSchemaElementTypes(allowedTypes);
795    }
796
797    if (allowUndefinedElementType.isPresent())
798    {
799      final Set<SchemaElementType> elementTypes =
800           EnumSet.noneOf(SchemaElementType.class);
801      for (final String value : allowUndefinedElementType.getValues())
802      {
803        elementTypes.add(SchemaElementType.forName(value));
804      }
805
806      schemaValidator.setAllowReferencesToUndefinedElementTypes(elementTypes);
807    }
808
809
810    // Use the schema validator to parse the scheme elements in the provided
811    // paths.
812    Schema schema = null;
813    final List<String> errorMessages = new ArrayList<>();
814    for (final File f : schemaPath.getValues())
815    {
816      schema = schemaValidator.validateSchema(f, schema, errorMessages);
817    }
818
819
820    // If we ended up with an empty set of error messages, then return a success
821    // result.
822    final int numErrors = errorMessages.size();
823    if (numErrors == 0)
824    {
825        completionMessage.set(INFO_VALIDATE_SCHEMA_NO_ERRORS.get());
826        wrapOut(0, WRAP_COLUMN, INFO_VALIDATE_SCHEMA_NO_ERRORS.get());
827        return ResultCode.SUCCESS;
828    }
829
830
831    // If we've gotten here, then there were errors.  Display them and get the
832    // final string to use as the completion message.
833    final String finalMessage;
834    if (numErrors == 1)
835    {
836      wrapErr(0, WRAP_COLUMN, ERR_VALIDATE_SCHEMA_ERROR_FOUND.get());
837      finalMessage = ERR_VALIDATE_SCHEMA_ONE_ERROR.get();
838    }
839    else
840    {
841      wrapErr(0, WRAP_COLUMN, ERR_VALIDATE_SCHEMA_ERRORS_FOUND.get());
842      finalMessage = ERR_VALIDATE_SCHEMA_MULTIPLE_ERRORS.get(numErrors);
843    }
844
845    for (final String errorMessage : errorMessages)
846    {
847      err();
848
849      boolean firstLine = true;
850      for (final String line :
851           StaticUtils.wrapLine(errorMessage, (WRAP_COLUMN - 2)))
852      {
853        if (firstLine)
854        {
855          err("* " + line);
856          firstLine = false;
857        }
858        else
859        {
860          err("  " + line);
861        }
862      }
863    }
864
865    completionMessage.set(finalMessage);
866
867    err();
868    wrapErr(0, WRAP_COLUMN, finalMessage);
869
870    return ResultCode.DECODING_ERROR;
871  }
872
873
874
875  /**
876   * {@inheritDoc}
877   */
878  @Override()
879  @NotNull()
880  public LinkedHashMap<String[],String> getExampleUsages()
881  {
882    final LinkedHashMap<String[],String> examples = new LinkedHashMap<>();
883
884    examples.put(
885         new String[]
886         {
887           "--schema-path", "/path/to/schema"
888         },
889         INFO_VALIDATE_SCHEMA_EXAMPLE_1.get());
890
891    examples.put(
892         new String[]
893         {
894           "--schema-path", "/path/to/schema",
895           "--allow-multiple-entries-per-schema-file",
896           "--allow-schema-files-in-subdirectories",
897           "--allow-redefining-elements",
898           "--allow-undefined-element-type", "attribute-syntax",
899           "--allow-undefined-element-type", "matching-rule",
900           "--use-lenient-oid-validation",
901           "--allow-non-numeric-oids",
902           "--allow-elements-without-names",
903           "--use-lenient-name-validation",
904           "--allow-attribute-types-without-syntax",
905           "--allow-structural-object-classes-without-superior",
906           "--allow-empty-descriptions"
907         },
908         INFO_VALIDATE_SCHEMA_EXAMPLE_2.get());
909
910    return examples;
911  }
912}