001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-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) 2009-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.persist;
037
038
039
040import java.io.File;
041import java.io.FileWriter;
042import java.io.OutputStream;
043import java.io.PrintWriter;
044import java.io.Serializable;
045import java.util.Arrays;
046import java.util.Collection;
047import java.util.Date;
048import java.util.Iterator;
049import java.util.LinkedHashMap;
050import java.util.TreeMap;
051import java.util.TreeSet;
052
053import com.unboundid.ldap.sdk.DN;
054import com.unboundid.ldap.sdk.Entry;
055import com.unboundid.ldap.sdk.Filter;
056import com.unboundid.ldap.sdk.LDAPConnection;
057import com.unboundid.ldap.sdk.LDAPException;
058import com.unboundid.ldap.sdk.LDAPInterface;
059import com.unboundid.ldap.sdk.ReadOnlyEntry;
060import com.unboundid.ldap.sdk.ResultCode;
061import com.unboundid.ldap.sdk.Version;
062import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
063import com.unboundid.ldap.sdk.schema.ObjectClassDefinition;
064import com.unboundid.ldap.sdk.schema.ObjectClassType;
065import com.unboundid.ldap.sdk.schema.Schema;
066import com.unboundid.util.Debug;
067import com.unboundid.util.LDAPCommandLineTool;
068import com.unboundid.util.Mutable;
069import com.unboundid.util.NotNull;
070import com.unboundid.util.Nullable;
071import com.unboundid.util.StaticUtils;
072import com.unboundid.util.ThreadSafety;
073import com.unboundid.util.ThreadSafetyLevel;
074import com.unboundid.util.args.ArgumentException;
075import com.unboundid.util.args.ArgumentParser;
076import com.unboundid.util.args.BooleanArgument;
077import com.unboundid.util.args.DNArgument;
078import com.unboundid.util.args.FileArgument;
079import com.unboundid.util.args.StringArgument;
080
081import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
082
083
084
085/**
086 * This class provides a tool which can be used to generate source code for a
087 * Java class file based on information read from the schema of an LDAP
088 * directory server.
089 */
090@Mutable()
091@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
092public final class GenerateSourceFromSchema
093       extends LDAPCommandLineTool
094       implements Serializable
095{
096  /**
097   * The serial version UID for this serializable class.
098   */
099  private static final long serialVersionUID = 3488976364950590266L;
100
101
102
103  /**
104   * A pre-allocated empty tree set.
105   */
106  @NotNull private static final TreeSet<String> EMPTY_TREE_SET =
107       new TreeSet<>();
108
109
110
111  // Arguments used by this tool.
112  @Nullable private BooleanArgument terseArg;
113  @Nullable private DNArgument      defaultParentDNArg;
114  @Nullable private FileArgument    outputDirectoryArg;
115  @Nullable private StringArgument  auxiliaryClassArg;
116  @Nullable private StringArgument  classNameArg;
117  @Nullable private StringArgument  lazyAttributeArg;
118  @Nullable private StringArgument  operationalAttributeArg;
119  @Nullable private StringArgument  packageNameArg;
120  @Nullable private StringArgument  rdnAttributeArg;
121  @Nullable private StringArgument  structuralClassArg;
122
123  // Indicates whether any multivalued attributes have been identified, and
124  // therefore we need to include java.util.Arrays in the import list.
125  private boolean needArrays;
126
127  // Indicates whether any date attributes have been identified, and therefore
128  // we need to include java.util.Date in the import list.
129  private boolean needDate;
130
131  // Indicates whether any DN-syntax attributes have been identified, and
132  // therefore we need to include com.unboundid.ldap.sdk.DN in the import list.
133  private boolean needDN;
134
135  // Indicates whether
136  // Indicates whether any DN-syntax attributes have been identified, and
137  // therefore we need to include
138  // com.unboundid.ldap.sdk.persist.PersistedObjects in the import list.
139  private boolean needPersistedObjects;
140
141
142
143  /**
144   * Parse the provided command line arguments and perform the appropriate
145   * processing.
146   *
147   * @param  args  The command line arguments provided to this program.
148   */
149  public static void main(@NotNull final String[] args)
150  {
151    final ResultCode resultCode = main(args, System.out, System.err);
152    if (resultCode != ResultCode.SUCCESS)
153    {
154      System.exit(resultCode.intValue());
155    }
156  }
157
158
159
160  /**
161   * Parse the provided command line arguments and perform the appropriate
162   * processing.
163   *
164   * @param  args       The command line arguments provided to this program.
165   * @param  outStream  The output stream to which standard out should be
166   *                    written.  It may be {@code null} if output should be
167   *                    suppressed.
168   * @param  errStream  The output stream to which standard error should be
169   *                    written.  It may be {@code null} if error messages
170   *                    should be suppressed.
171   *
172   * @return  A result code indicating whether the processing was successful.
173   */
174  @NotNull()
175  public static ResultCode main(@NotNull final String[] args,
176                                @Nullable final OutputStream outStream,
177                                @Nullable final OutputStream errStream)
178  {
179    final GenerateSourceFromSchema tool =
180         new GenerateSourceFromSchema(outStream, errStream);
181    return tool.runTool(args);
182  }
183
184
185
186  /**
187   * Creates a new instance of this tool.
188   *
189   * @param  outStream  The output stream to which standard out should be
190   *                    written.  It may be {@code null} if output should be
191   *                    suppressed.
192   * @param  errStream  The output stream to which standard error should be
193   *                    written.  It may be {@code null} if error messages
194   *                    should be suppressed.
195   */
196  public GenerateSourceFromSchema(@Nullable final OutputStream outStream,
197                                  @Nullable final OutputStream errStream)
198  {
199    super(outStream, errStream);
200
201    needArrays           = false;
202    needDate             = false;
203    needDN               = false;
204    needPersistedObjects = false;
205  }
206
207
208
209  /**
210   * {@inheritDoc}
211   */
212  @Override()
213  @NotNull()
214  public String getToolName()
215  {
216    return "generate-source-from-schema";
217  }
218
219
220
221  /**
222   * {@inheritDoc}
223   */
224  @Override()
225  @NotNull()
226  public String getToolDescription()
227  {
228    return INFO_GEN_SOURCE_TOOL_DESCRIPTION.get();
229  }
230
231
232
233  /**
234   * Retrieves the version string for this tool.
235   *
236   * @return  The version string for this tool.
237   */
238  @Override()
239  @NotNull()
240  public String getToolVersion()
241  {
242    return Version.NUMERIC_VERSION_STRING;
243  }
244
245
246
247  /**
248   * Indicates whether this tool should provide support for an interactive mode,
249   * in which the tool offers a mode in which the arguments can be provided in
250   * a text-driven menu rather than requiring them to be given on the command
251   * line.  If interactive mode is supported, it may be invoked using the
252   * "--interactive" argument.  Alternately, if interactive mode is supported
253   * and {@link #defaultsToInteractiveMode()} returns {@code true}, then
254   * interactive mode may be invoked by simply launching the tool without any
255   * arguments.
256   *
257   * @return  {@code true} if this tool supports interactive mode, or
258   *          {@code false} if not.
259   */
260  @Override()
261  public boolean supportsInteractiveMode()
262  {
263    return true;
264  }
265
266
267
268  /**
269   * Indicates whether this tool defaults to launching in interactive mode if
270   * the tool is invoked without any command-line arguments.  This will only be
271   * used if {@link #supportsInteractiveMode()} returns {@code true}.
272   *
273   * @return  {@code true} if this tool defaults to using interactive mode if
274   *          launched without any command-line arguments, or {@code false} if
275   *          not.
276   */
277  @Override()
278  public boolean defaultsToInteractiveMode()
279  {
280    return true;
281  }
282
283
284
285  /**
286   * Indicates whether this tool should provide arguments for redirecting output
287   * to a file.  If this method returns {@code true}, then the tool will offer
288   * an "--outputFile" argument that will specify the path to a file to which
289   * all standard output and standard error content will be written, and it will
290   * also offer a "--teeToStandardOut" argument that can only be used if the
291   * "--outputFile" argument is present and will cause all output to be written
292   * to both the specified output file and to standard output.
293   *
294   * @return  {@code true} if this tool should provide arguments for redirecting
295   *          output to a file, or {@code false} if not.
296   */
297  @Override()
298  protected boolean supportsOutputFile()
299  {
300    return true;
301  }
302
303
304
305  /**
306   * Indicates whether this tool should default to interactively prompting for
307   * the bind password if a password is required but no argument was provided
308   * to indicate how to get the password.
309   *
310   * @return  {@code true} if this tool should default to interactively
311   *          prompting for the bind password, or {@code false} if not.
312   */
313  @Override()
314  protected boolean defaultToPromptForBindPassword()
315  {
316    return true;
317  }
318
319
320
321  /**
322   * Indicates whether this tool supports the use of a properties file for
323   * specifying default values for arguments that aren't specified on the
324   * command line.
325   *
326   * @return  {@code true} if this tool supports the use of a properties file
327   *          for specifying default values for arguments that aren't specified
328   *          on the command line, or {@code false} if not.
329   */
330  @Override()
331  public boolean supportsPropertiesFile()
332  {
333    return true;
334  }
335
336
337
338  /**
339   * {@inheritDoc}
340   */
341  @Override()
342  protected boolean supportsDebugLogging()
343  {
344    return true;
345  }
346
347
348
349  /**
350   * Indicates whether the LDAP-specific arguments should include alternate
351   * versions of all long identifiers that consist of multiple words so that
352   * they are available in both camelCase and dash-separated versions.
353   *
354   * @return  {@code true} if this tool should provide multiple versions of
355   *          long identifiers for LDAP-specific arguments, or {@code false} if
356   *          not.
357   */
358  @Override()
359  protected boolean includeAlternateLongIdentifiers()
360  {
361    return true;
362  }
363
364
365
366  /**
367   * Indicates whether this tool should provide a command-line argument that
368   * allows for low-level SSL debugging.  If this returns {@code true}, then an
369   * "--enableSSLDebugging}" argument will be added that sets the
370   * "javax.net.debug" system property to "all" before attempting any
371   * communication.
372   *
373   * @return  {@code true} if this tool should offer an "--enableSSLDebugging"
374   *          argument, or {@code false} if not.
375   */
376  @Override()
377  protected boolean supportsSSLDebugging()
378  {
379    return true;
380  }
381
382
383
384  /**
385   * {@inheritDoc}
386   */
387  @Override()
388  public void addNonLDAPArguments(@NotNull final ArgumentParser parser)
389         throws ArgumentException
390  {
391    outputDirectoryArg = new FileArgument('d', "outputDirectory", false, 1,
392         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_PATH.get(),
393         INFO_GEN_SOURCE_ARG_DESCRIPTION_OUTPUT_DIRECTORY.get(), true, true,
394         false, true);
395    outputDirectoryArg.addLongIdentifier("output-directory", true);
396    parser.addArgument(outputDirectoryArg);
397
398    structuralClassArg = new StringArgument('s', "structuralClass", true, 1,
399         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
400         INFO_GEN_SOURCE_ARG_DESCRIPTION_STRUCTURAL_CLASS.get());
401    structuralClassArg.addLongIdentifier("structural-class", true);
402    parser.addArgument(structuralClassArg);
403
404    auxiliaryClassArg = new StringArgument('a', "auxiliaryClass", false, 0,
405         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
406         INFO_GEN_SOURCE_ARG_DESCRIPTION_AUXILIARY_CLASS.get());
407    auxiliaryClassArg.addLongIdentifier("auxiliary-class", true);
408    parser.addArgument(auxiliaryClassArg);
409
410    rdnAttributeArg = new StringArgument('r', "rdnAttribute", true, 0,
411         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
412         INFO_GEN_SOURCE_ARG_DESCRIPTION_RDN_ATTRIBUTE.get());
413    rdnAttributeArg.addLongIdentifier("rdn-attribute", true);
414    parser.addArgument(rdnAttributeArg);
415
416    lazyAttributeArg = new StringArgument('l', "lazyAttribute", false, 0,
417         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
418         INFO_GEN_SOURCE_ARG_DESCRIPTION_LAZY_ATTRIBUTE.get());
419    lazyAttributeArg.addLongIdentifier("lazy-attribute", true);
420    parser.addArgument(lazyAttributeArg);
421
422    operationalAttributeArg = new StringArgument('O', "operationalAttribute",
423         false, 0, INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
424         INFO_GEN_SOURCE_ARG_DESCRIPTION_OPERATIONAL_ATTRIBUTE.get());
425    operationalAttributeArg.addLongIdentifier("operational-attribute", true);
426    parser.addArgument(operationalAttributeArg);
427
428    defaultParentDNArg = new DNArgument('b', "defaultParentDN", false, 1,
429         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_DN.get(),
430         INFO_GEN_SOURCE_ARG_DESCRIPTION_DEFAULT_PARENT_DN.get());
431    defaultParentDNArg.addLongIdentifier("default-parent-dn", true);
432    parser.addArgument(defaultParentDNArg);
433
434    packageNameArg = new StringArgument('n', "packageName", false, 1,
435         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
436         INFO_GEN_SOURCE_ARG_DESCRIPTION_PACKAGE_NAME.get());
437    packageNameArg.addLongIdentifier("package-name", true);
438    parser.addArgument(packageNameArg);
439
440    classNameArg = new StringArgument('c', "className", false, 1,
441         INFO_GEN_SOURCE_VALUE_PLACEHOLDER_NAME.get(),
442         INFO_GEN_SOURCE_ARG_DESCRIPTION_CLASS_NAME.get());
443    classNameArg.addLongIdentifier("class-name", true);
444    parser.addArgument(classNameArg);
445
446    terseArg = new BooleanArgument('t', "terse", 1,
447         INFO_GEN_SOURCE_ARG_DESCRIPTION_TERSE.get());
448    parser.addArgument(terseArg);
449  }
450
451
452
453  /**
454   * {@inheritDoc}
455   */
456  @Override()
457  @NotNull()
458  public ResultCode doToolProcessing()
459  {
460    // Establish a connection to the target directory server and retrieve the
461    // schema.
462    final LDAPConnection conn;
463    try
464    {
465      conn = getConnection();
466    }
467    catch (final LDAPException le)
468    {
469      Debug.debugException(le);
470      err(ERR_GEN_SOURCE_CANNOT_CONNECT.get(
471           StaticUtils.getExceptionMessage(le)));
472      return le.getResultCode();
473    }
474
475    final Schema schema;
476    try
477    {
478      schema = conn.getSchema();
479      if (schema == null)
480      {
481        err(ERR_GEN_SOURCE_CANNOT_READ_SCHEMA.get(
482             ERR_GEN_SOURCE_SCHEMA_NOT_RETURNED.get()));
483        return ResultCode.NO_RESULTS_RETURNED;
484      }
485    }
486    catch (final LDAPException le)
487    {
488      Debug.debugException(le);
489      err(ERR_GEN_SOURCE_CANNOT_READ_SCHEMA.get(
490           StaticUtils.getExceptionMessage(le)));
491      return le.getResultCode();
492    }
493    finally
494    {
495      conn.close();
496    }
497
498    return generateSourceFile(schema, terseArg.isPresent());
499  }
500
501
502
503  /**
504   * Generates the source file using the information in the provided schema.
505   *
506   * @param  schema  The schema to use to generate the source file.
507   * @param  terse   Indicates whether to use terse mode when generating the
508   *                 source file.  If this is {@code true}, then all optional
509   *                 elements will be omitted from annotations.
510   *
511   * @return  A result code obtained for the processing.
512   */
513  @NotNull()
514  private ResultCode generateSourceFile(@NotNull final Schema schema,
515                                        final boolean terse)
516  {
517    // Retrieve and process the structural object class.
518    final TreeMap<String,AttributeTypeDefinition> requiredAttrs =
519         new TreeMap<>();
520    final TreeMap<String,AttributeTypeDefinition> optionalAttrs =
521         new TreeMap<>();
522    final TreeMap<String,TreeSet<String>> requiredAttrOCs = new TreeMap<>();
523    final TreeMap<String,TreeSet<String>> optionalAttrOCs = new TreeMap<>();
524    final TreeMap<String,String> types = new TreeMap<>();
525
526    final String structuralClassName = structuralClassArg.getValue();
527    final ObjectClassDefinition structuralOC =
528         schema.getObjectClass(structuralClassName);
529    if (structuralOC == null)
530    {
531      err(ERR_GEN_SOURCE_STRUCTURAL_CLASS_NOT_FOUND.get(structuralClassName));
532      return ResultCode.PARAM_ERROR;
533    }
534
535    if (structuralOC.getObjectClassType(schema) != ObjectClassType.STRUCTURAL)
536    {
537      err(ERR_GEN_SOURCE_STRUCTURAL_CLASS_NOT_STRUCTURAL.get(
538           structuralClassName));
539      return ResultCode.PARAM_ERROR;
540    }
541
542    processObjectClass(structuralOC, schema, requiredAttrs, requiredAttrOCs,
543         optionalAttrs, optionalAttrOCs, types);
544
545
546    // Retrieve and process the auxiliary object classes.
547    final TreeMap<String,ObjectClassDefinition> auxiliaryOCs = new TreeMap<>();
548    if (auxiliaryClassArg.isPresent())
549    {
550      for (final String s : auxiliaryClassArg.getValues())
551      {
552        final ObjectClassDefinition oc = schema.getObjectClass(s);
553        if (oc == null)
554        {
555          err(ERR_GEN_SOURCE_AUXILIARY_CLASS_NOT_FOUND.get(s));
556          return ResultCode.PARAM_ERROR;
557        }
558
559        if  (oc.getObjectClassType(schema) != ObjectClassType.AUXILIARY)
560        {
561          err(ERR_GEN_SOURCE_AUXILIARY_CLASS_NOT_AUXILIARY.get(s));
562          return ResultCode.PARAM_ERROR;
563        }
564
565        auxiliaryOCs.put(StaticUtils.toLowerCase(s), oc);
566
567        processObjectClass(oc, schema, requiredAttrs, requiredAttrOCs,
568             optionalAttrs, optionalAttrOCs, types);
569      }
570    }
571
572
573    // Determine the appropriate set of superior object classes.
574    final TreeMap<String,ObjectClassDefinition> superiorOCs = new TreeMap<>();
575    for (final ObjectClassDefinition s :
576         structuralOC.getSuperiorClasses(schema, true))
577    {
578      superiorOCs.put(StaticUtils.toLowerCase(s.getNameOrOID()), s);
579    }
580
581    for (final ObjectClassDefinition d : auxiliaryOCs.values())
582    {
583      for (final ObjectClassDefinition s : d.getSuperiorClasses(schema, true))
584      {
585        superiorOCs.put(StaticUtils.toLowerCase(s.getNameOrOID()), s);
586      }
587    }
588
589    superiorOCs.remove(StaticUtils.toLowerCase(structuralClassName));
590    for (final String s : auxiliaryOCs.keySet())
591    {
592      superiorOCs.remove(s);
593    }
594
595
596    // Retrieve and process the operational attributes.
597    final TreeMap<String,AttributeTypeDefinition> operationalAttrs =
598         new TreeMap<>();
599    if (operationalAttributeArg.isPresent())
600    {
601      for (final String s : operationalAttributeArg.getValues())
602      {
603        final AttributeTypeDefinition d = schema.getAttributeType(s);
604        if (d == null)
605        {
606          err(ERR_GEN_SOURCE_OPERATIONAL_ATTRIBUTE_NOT_DEFINED.get(s));
607          return ResultCode.PARAM_ERROR;
608        }
609        else if (! d.isOperational())
610        {
611          err(ERR_GEN_SOURCE_OPERATIONAL_ATTRIBUTE_NOT_OPERATIONAL.get(s));
612          return ResultCode.PARAM_ERROR;
613        }
614        else
615        {
616          final String lowerName = StaticUtils.toLowerCase(s);
617          operationalAttrs.put(lowerName, d);
618          types.put(lowerName, getJavaType(schema, d));
619        }
620      }
621    }
622
623
624    // Make sure all of the configured RDN attributes are allowed by at least
625    // one of the associated object classes.
626    final TreeSet<String> rdnAttrs = new TreeSet<>();
627    for (final String s : rdnAttributeArg.getValues())
628    {
629      final AttributeTypeDefinition d = schema.getAttributeType(s);
630      if (d == null)
631      {
632        err(ERR_GEN_SOURCE_RDN_ATTRIBUTE_NOT_DEFINED.get(s));
633        return ResultCode.PARAM_ERROR;
634      }
635
636      final String lowerName = StaticUtils.toLowerCase(d.getNameOrOID());
637      rdnAttrs.add(lowerName);
638      if (requiredAttrs.containsKey(lowerName))
639      {
640        // No action required.
641      }
642      else if (optionalAttrs.containsKey(lowerName))
643      {
644        // Move the attribute to the required set.
645        requiredAttrs.put(lowerName, optionalAttrs.remove(lowerName));
646        requiredAttrOCs.put(lowerName, optionalAttrOCs.remove(lowerName));
647      }
648      else
649      {
650        err(ERR_GEN_SOURCE_RDN_ATTRIBUTE_NOT_DEFINED.get(s));
651        return ResultCode.PARAM_ERROR;
652      }
653    }
654
655
656    // Make sure all of the configured lazily-loaded attributes are allowed by
657    // at least one of the associated object classes or matches a configured
658    // operational attribute.
659    final TreeSet<String> lazyAttrs = new TreeSet<>();
660    for (final String s : lazyAttributeArg.getValues())
661    {
662      final AttributeTypeDefinition d = schema.getAttributeType(s);
663      if (d == null)
664      {
665        err(ERR_GEN_SOURCE_LAZY_ATTRIBUTE_NOT_DEFINED.get(s));
666        return ResultCode.PARAM_ERROR;
667      }
668
669      final String lowerName = StaticUtils.toLowerCase(d.getNameOrOID());
670      lazyAttrs.add(lowerName);
671      if (requiredAttrs.containsKey(lowerName) ||
672          optionalAttrs.containsKey(lowerName) ||
673          operationalAttrs.containsKey(lowerName))
674      {
675        // No action required.
676      }
677      else
678      {
679        err(ERR_GEN_SOURCE_LAZY_ATTRIBUTE_NOT_ALLOWED.get(s));
680        return ResultCode.PARAM_ERROR;
681      }
682    }
683
684
685    final String className;
686    if (classNameArg.isPresent())
687    {
688      className = classNameArg.getValue();
689      final StringBuilder invalidReason = new StringBuilder();
690      if (! PersistUtils.isValidJavaIdentifier(className, invalidReason))
691      {
692        err(ERR_GEN_SOURCE_INVALID_CLASS_NAME.get(className,
693             invalidReason.toString()));
694        return ResultCode.PARAM_ERROR;
695      }
696    }
697    else
698    {
699      className = StaticUtils.capitalize(
700           PersistUtils.toJavaIdentifier(structuralClassName));
701    }
702
703
704    final File sourceFile = new File(outputDirectoryArg.getValue(),
705         className + ".java");
706    final PrintWriter writer;
707    try
708    {
709      writer = new PrintWriter(new FileWriter(sourceFile));
710    }
711    catch (final Exception e)
712    {
713      Debug.debugException(e);
714      err(ERR_GEN_SOURCE_CANNOT_CREATE_WRITER.get(sourceFile.getAbsolutePath(),
715           StaticUtils.getExceptionMessage(e)));
716      return ResultCode.LOCAL_ERROR;
717    }
718
719
720    if (packageNameArg.isPresent())
721    {
722      final String packageName = packageNameArg.getValue();
723      if (! packageName.isEmpty())
724      {
725        writer.println("package " + packageName + ';');
726        writer.println();
727        writer.println();
728        writer.println();
729      }
730    }
731
732    boolean javaImports = false;
733    if (needArrays)
734    {
735      writer.println("import " + Arrays.class.getName() + ';');
736      javaImports = true;
737    }
738
739    if (needDate)
740    {
741      writer.println("import " + Date.class.getName() + ';');
742      javaImports = true;
743    }
744
745    if (javaImports)
746    {
747      writer.println();
748    }
749
750    if (needDN)
751    {
752      writer.println("import " + DN.class.getName() + ';');
753    }
754
755    writer.println("import " + Entry.class.getName() + ';');
756    writer.println("import " + Filter.class.getName() + ';');
757
758    if (needDN)
759    {
760      writer.println("import " + LDAPException.class.getName() + ';');
761      writer.println("import " + LDAPInterface.class.getName() + ';');
762    }
763
764    writer.println("import " + ReadOnlyEntry.class.getName() + ';');
765    writer.println("import " + DefaultObjectEncoder.class.getName() + ';');
766    writer.println("import " + FieldInfo.class.getName() + ';');
767    writer.println("import " + FilterUsage.class.getName() + ';');
768    writer.println("import " + LDAPEntryField.class.getName() + ';');
769    writer.println("import " + LDAPField.class.getName() + ';');
770    writer.println("import " + LDAPObject.class.getName() + ';');
771    writer.println("import " + LDAPObjectHandler.class.getName() + ';');
772    writer.println("import " + LDAPPersister.class.getName() + ';');
773    writer.println("import " + LDAPPersistException.class.getName() + ';');
774
775    if (needPersistedObjects)
776    {
777      writer.println("import " + PersistedObjects.class.getName() + ';');
778    }
779
780    writer.println("import " + PersistFilterType.class.getName() + ';');
781
782    if (needDN)
783    {
784      writer.println("import " + PersistUtils.class.getName() + ';');
785    }
786
787    writer.println();
788    writer.println();
789    writer.println();
790    writer.println("/**");
791    writer.println(" * This class provides an implementation of an object " +
792         "that can be used to");
793    writer.println(" * represent " + structuralClassName +
794         " objects in the directory.");
795    writer.println(" * It was generated by the " + getToolName() +
796         " tool provided with the");
797    writer.println(" * UnboundID LDAP SDK for Java.  It " +
798         "may be customized as desired to better suit");
799    writer.println(" * your needs.");
800    writer.println(" */");
801    writer.println("@LDAPObject(structuralClass=\"" + structuralClassName +
802         "\",");
803
804    switch (auxiliaryOCs.size())
805    {
806      case 0:
807        // No action required.
808        break;
809
810      case 1:
811        writer.println("            auxiliaryClass=\"" +
812             auxiliaryOCs.values().iterator().next().getNameOrOID() + "\",");
813        break;
814
815      default:
816        final Iterator<ObjectClassDefinition> iterator =
817             auxiliaryOCs.values().iterator();
818        writer.println("            auxiliaryClass={ \"" +
819             iterator.next().getNameOrOID() + "\",");
820        while (iterator.hasNext())
821        {
822          final String ocName = iterator.next().getNameOrOID();
823          if (iterator.hasNext())
824          {
825            writer.println("                             \"" + ocName +
826                 "\",");
827          }
828          else
829          {
830            writer.println("                             \"" + ocName +
831                 "\" },");
832          }
833        }
834        break;
835    }
836
837    switch (superiorOCs.size())
838    {
839      case 0:
840        // No action required.
841        break;
842
843      case 1:
844        writer.println("            superiorClass=\"" +
845             superiorOCs.values().iterator().next().getNameOrOID() + "\",");
846        break;
847
848      default:
849        final Iterator<ObjectClassDefinition> iterator =
850             superiorOCs.values().iterator();
851        writer.println("            superiorClass={ \"" +
852             iterator.next().getNameOrOID() + "\",");
853        while (iterator.hasNext())
854        {
855          final String ocName = iterator.next().getNameOrOID();
856          if (iterator.hasNext())
857          {
858            writer.println("                             \"" + ocName +
859                 "\",");
860          }
861          else
862          {
863            writer.println("                             \"" + ocName +
864                 "\" },");
865          }
866        }
867        break;
868    }
869
870    if (defaultParentDNArg.isPresent())
871    {
872      writer.println("            defaultParentDN=\"" +
873           defaultParentDNArg.getValue() + "\",");
874    }
875
876    writer.println("            postDecodeMethod=\"doPostDecode\",");
877    writer.println("            postEncodeMethod=\"doPostEncode\")");
878    writer.println("public class " + className);
879    writer.println("{");
880
881    if (! terse)
882    {
883      writer.println("  /*");
884      writer.println("   * NOTE:  This class includes a number of annotation " +
885           "elements which are not");
886      writer.println("   * required but have been provided to make it easier " +
887           "to edit the resulting");
888      writer.println("   * source code.  If you want to exclude these " +
889           "unnecessary annotation");
890      writer.println("   * elements, use the '--terse' command-line argument.");
891      writer.println("   */");
892      writer.println();
893      writer.println();
894      writer.println();
895    }
896
897    writer.println("  // The field to use to hold a read-only copy of the " +
898         "associated entry.");
899    writer.println("  @LDAPEntryField()");
900    writer.println("  private ReadOnlyEntry ldapEntry;");
901
902
903    // Add all of the fields.  First the fields for the RDN attributes, then
904    // for the rest of the required attributes, then for the optional
905    // attributes, and finally any operational attributes.
906    for (final String lowerName : rdnAttrs)
907    {
908      final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
909      final TreeSet<String> ocNames = requiredAttrOCs.get(lowerName);
910      writeField(writer, d, types.get(lowerName), ocNames, true, true,
911           structuralClassName, false, terse);
912    }
913
914    for (final String lowerName : requiredAttrs.keySet())
915    {
916      if (rdnAttrs.contains(lowerName))
917      {
918        continue;
919      }
920
921      final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
922      final TreeSet<String> ocNames = requiredAttrOCs.get(lowerName);
923      writeField(writer, d, types.get(lowerName), ocNames, false, true,
924           structuralClassName, lazyAttrs.contains(lowerName), terse);
925    }
926
927    for (final String lowerName : optionalAttrs.keySet())
928    {
929      final AttributeTypeDefinition d = optionalAttrs.get(lowerName);
930      final TreeSet<String> ocNames = optionalAttrOCs.get(lowerName);
931      writeField(writer, d, types.get(lowerName), ocNames, false, false,
932           structuralClassName, lazyAttrs.contains(lowerName), terse);
933    }
934
935    for (final String lowerName : operationalAttrs.keySet())
936    {
937      final AttributeTypeDefinition d = operationalAttrs.get(lowerName);
938      final TreeSet<String> ocNames = EMPTY_TREE_SET;
939      writeField(writer, d, types.get(lowerName), ocNames, false, false,
940           structuralClassName, lazyAttrs.contains(lowerName), terse);
941    }
942
943
944    // Add the default constructor.
945    writer.println();
946    writer.println();
947    writer.println();
948    writer.println("  /**");
949    writer.println("   * Creates a new instance of this object.  All fields " +
950         "will be uninitialized,");
951    writer.println("   * so the setter methods should be used to assign " +
952         "values to them.");
953    writer.println("   */");
954    writer.println("  public " + className + "()");
955    writer.println("  {");
956    writer.println("    // No initialization will be performed by default.  " +
957         "Note that if you set");
958    writer.println("    // values for any fields marked with an @LDAPField, " +
959         "@LDAPDNField, or");
960    writer.println("    // @LDAPEntryField annotation, they will be " +
961         "overwritten in the course of");
962    writer.println("    // decoding initializing this object from an LDAP " +
963         "entry.");
964    writer.println("  }");
965
966
967    // Add a static decode method that can create an instance of the object
968    // from a given entry.
969    writer.println();
970    writer.println();
971    writer.println();
972    writer.println("  /**");
973    writer.println("   * Creates a new " + className + " object decoded");
974    writer.println("   * from the provided entry.");
975    writer.println("   *");
976    writer.println("   * @param  entry  The entry to be decoded.");
977    writer.println("   *");
978    writer.println("   * @return  The decoded " + className + " object.");
979    writer.println("   *");
980    writer.println("   * @throws  LDAPPersistException  If a problem occurs " +
981         "while attempting to");
982    writer.println("   *                                decode the provided " +
983         "entry.");
984    writer.println("   */");
985    writer.println("  public static " + className +
986         " decode(final Entry entry)");
987    writer.println("         throws LDAPPersistException");
988    writer.println("  {");
989    writer.println("    return getPersister().decode(entry);");
990    writer.println("  }");
991
992
993    // Add the getPersister method.
994    writer.println("");
995    writer.println("");
996    writer.println("");
997    writer.println("  /**");
998    writer.println("   * Retrieves an {@code LDAPPersister} instance that " +
999         "may be used to interact");
1000    writer.println("   * with objects of this type.");
1001    writer.println("   *");
1002    writer.println("   * @return  An {@code LDAPPersister} instance that may " +
1003         "be used to interact");
1004    writer.println("   *          with objects of this type.");
1005    writer.println("   *");
1006    writer.println("   * @throws  LDAPPersistException  If a problem occurs " +
1007         "while creating the");
1008    writer.println("   *                                " +
1009         "{@code LDAPPersister} instance.");
1010    writer.println("   */");
1011    writer.println("  public static LDAPPersister<" + className +
1012         "> getPersister()");
1013    writer.println("         throws LDAPPersistException");
1014    writer.println("  {");
1015    writer.println("    return LDAPPersister.getInstance(" + className +
1016         ".class);");
1017    writer.println("  }");
1018
1019
1020    // Add the post-decode and post-encode methods.
1021    writer.println();
1022    writer.println();
1023    writer.println();
1024    writer.println("  /**");
1025    writer.println("   * Performs any processing that may be necessary after " +
1026         "initializing this");
1027    writer.println("   * object from an LDAP entry.");
1028    writer.println("   *");
1029    writer.println("   * @throws  LDAPPersistException  If there is a " +
1030         "problem with the object after");
1031    writer.println("   *                                it has been decoded " +
1032         "from an LDAP entry.");
1033    writer.println("   */");
1034    writer.println("  private void doPostDecode()");
1035    writer.println("          throws LDAPPersistException");
1036    writer.println("  {");
1037    writer.println("    // No processing is needed by default.  You may " +
1038         "provide an implementation");
1039    writer.println("    // for this method if custom post-decode processing " +
1040         "is needed.");
1041    writer.println("  }");
1042    writer.println();
1043    writer.println();
1044    writer.println();
1045    writer.println("  /**");
1046    writer.println("   * Performs any processing that may be necessary after " +
1047         "encoding this object");
1048    writer.println("   * to an LDAP entry.");
1049    writer.println("   *");
1050    writer.println("   * @param  entry  The entry that has been generated.  " +
1051         "It may be altered if");
1052    writer.println("   *                desired.");
1053    writer.println("   *");
1054    writer.println("   * @throws  LDAPPersistException  If the generated " +
1055         "entry should not be used.");
1056    writer.println("   */");
1057    writer.println("  private void doPostEncode(final Entry entry)");
1058    writer.println("          throws LDAPPersistException");
1059    writer.println("  {");
1060    writer.println("    // No processing is needed by default.  You may " +
1061         "provide an implementation");
1062    writer.println("    // for this method if custom post-encode processing " +
1063         "is needed.");
1064    writer.println("  }");
1065
1066
1067    // Add a method for getting a read-only copy of the associated entry.
1068    writer.println();
1069    writer.println();
1070    writer.println();
1071    writer.println("  /**");
1072    writer.println("   * Retrieves a read-only copy of the entry with which " +
1073         "this object is");
1074    writer.println("   * associated, if it is available.  It will only be " +
1075         "available if this object");
1076    writer.println("   * was decoded from or encoded to an LDAP entry.");
1077    writer.println("   *");
1078    writer.println("   * @return  A read-only copy of the entry with which " +
1079         "this object is");
1080    writer.println("   *          associated, or {@code null} if it is not " +
1081         "available.");
1082    writer.println("   */");
1083    writer.println("  public ReadOnlyEntry getLDAPEntry()");
1084    writer.println("  {");
1085    writer.println("    return ldapEntry;");
1086    writer.println("  }");
1087
1088
1089    // Add a method for getting the DN of the associated entry.
1090    writer.println();
1091    writer.println();
1092    writer.println();
1093    writer.println("  /**");
1094    writer.println("   * Retrieves the DN of the entry with which this " +
1095         "object is associated, if it");
1096    writer.println("   * is available.  It will only be available if this " +
1097         "object was decoded from or");
1098    writer.println("   * encoded to an LDAP entry.");
1099    writer.println("   *");
1100    writer.println("   * @return  The DN of the entry with which this object " +
1101         "is associated, or");
1102    writer.println("   *          {@code null} if it is not available.");
1103    writer.println("   */");
1104    writer.println("  public String getLDAPEntryDN()");
1105    writer.println("  {");
1106    writer.println("    if (ldapEntry == null)");
1107    writer.println("    {");
1108    writer.println("      return null;");
1109    writer.println("    }");
1110    writer.println("    else");
1111    writer.println("    {");
1112    writer.println("      return ldapEntry.getDN();");
1113    writer.println("    }");
1114    writer.println("  }");
1115
1116
1117    // Add getter, setter, and filter generation methods for all of the fields
1118    // associated with LDAP attributes.  First the fields for the RDN
1119    // attributes, then for the rest of the required attributes, and then for
1120    // the optional attributes.
1121    for (final String lowerName : rdnAttrs)
1122    {
1123      final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
1124      writeFieldMethods(writer, d, types.get(lowerName), true);
1125    }
1126
1127    for (final String lowerName : requiredAttrs.keySet())
1128    {
1129      if (rdnAttrs.contains(lowerName))
1130      {
1131        continue;
1132      }
1133
1134      final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
1135      writeFieldMethods(writer, d, types.get(lowerName), true);
1136    }
1137
1138    for (final String lowerName : optionalAttrs.keySet())
1139    {
1140      final AttributeTypeDefinition d = optionalAttrs.get(lowerName);
1141      writeFieldMethods(writer, d, types.get(lowerName), true);
1142    }
1143
1144    for (final String lowerName : operationalAttrs.keySet())
1145    {
1146      final AttributeTypeDefinition d = operationalAttrs.get(lowerName);
1147      writeFieldMethods(writer, d, types.get(lowerName), false);
1148    }
1149
1150    writeToString(writer, className, requiredAttrs.values(),
1151         optionalAttrs.values(), operationalAttrs.values());
1152
1153    writer.println("}");
1154    writer.println();
1155    writer.close();
1156
1157    return ResultCode.SUCCESS;
1158  }
1159
1160
1161
1162  /**
1163   * Performs an appropriate set of processing for the provided object class to
1164   * ensure that all of the required and optional attributes are classified
1165   * properly.
1166   *
1167   * @param  oc   The object class to process.
1168   * @param  s    The server schema.
1169   * @param  ra   The set of required attributes identified so far.
1170   * @param  rac  The object classes referenced by the required attributes.
1171   * @param  oa   The set of optional attributes identified so far.
1172   * @param  oac  The object classes referenced by the optional attributes.
1173   * @param  t    A map of attribute type names to Java types.
1174   */
1175  private void processObjectClass(@NotNull final ObjectClassDefinition oc,
1176                   @NotNull final Schema s,
1177                   @NotNull final TreeMap<String,AttributeTypeDefinition> ra,
1178                   @NotNull final TreeMap<String,TreeSet<String>> rac,
1179                   @NotNull final TreeMap<String,AttributeTypeDefinition> oa,
1180                   @NotNull final TreeMap<String,TreeSet<String>> oac,
1181                   @NotNull final TreeMap<String,String> t)
1182  {
1183    for (final AttributeTypeDefinition d : oc.getRequiredAttributes(s, true))
1184    {
1185      if (d.hasNameOrOID("objectClass"))
1186      {
1187        continue;
1188      }
1189
1190      final String lowerName = StaticUtils.toLowerCase(d.getNameOrOID());
1191      if (ra.containsKey(lowerName))
1192      {
1193        rac.get(lowerName).add(oc.getNameOrOID());
1194      }
1195      else if (oa.containsKey(lowerName))
1196      {
1197        oa.remove(lowerName);
1198        ra.put(lowerName, d);
1199
1200        final TreeSet<String> ocSet = oac.remove(lowerName);
1201        ocSet.add(oc.getNameOrOID());
1202        rac.put(lowerName, ocSet);
1203      }
1204      else
1205      {
1206        final TreeSet<String> ocSet = new TreeSet<>();
1207        ocSet.add(oc.getNameOrOID());
1208        ra.put(lowerName, d);
1209        rac.put(lowerName, ocSet);
1210        t.put(lowerName, getJavaType(s, d));
1211      }
1212    }
1213
1214    for (final AttributeTypeDefinition d : oc.getOptionalAttributes(s, true))
1215    {
1216      if (d.hasNameOrOID("objectClass"))
1217      {
1218        continue;
1219      }
1220
1221      final String lowerName = StaticUtils.toLowerCase(d.getNameOrOID());
1222      if (ra.containsKey(lowerName))
1223      {
1224        rac.get(lowerName).add(oc.getNameOrOID());
1225      }
1226      else if (oa.containsKey(lowerName))
1227      {
1228        oac.get(lowerName).add(oc.getNameOrOID());
1229      }
1230      else
1231      {
1232        final TreeSet<String> ocSet = new TreeSet<>();
1233        ocSet.add(oc.getNameOrOID());
1234        oa.put(lowerName, d);
1235        oac.put(lowerName, ocSet);
1236        t.put(lowerName, getJavaType(s, d));
1237      }
1238    }
1239  }
1240
1241
1242
1243  /**
1244   * Writes information about a field to the Java class file.
1245   *
1246   * @param  writer    The writer to which the field information should be
1247   *                   written.
1248   * @param  d         The attribute type definition.
1249   * @param  type      The name of the Java type to use for the field.
1250   * @param  ocNames   The names of the object classes for the attribute type.
1251   * @param  inRDN     Indicates whether the attribute should be included in
1252   *                   generated entry RDNs.
1253   * @param  required  Indicates whether the attribute should be considered
1254   *                   required.
1255   * @param  sc        The name of the structural object class for the object.
1256   * @param  lazy      Indicates whether the field should be marked for lazy
1257   *                   loading.
1258   * @param  terse     Indicates whether to use terse mode.
1259   */
1260  private static void writeField(@NotNull final PrintWriter writer,
1261                           @NotNull final AttributeTypeDefinition d,
1262                           @NotNull final String type,
1263                           @NotNull final TreeSet<String> ocNames,
1264                           final boolean inRDN, final boolean required,
1265                           @NotNull final String sc, final boolean lazy,
1266                           final boolean terse)
1267  {
1268    final String attrName  = d.getNameOrOID();
1269    final String fieldName = PersistUtils.toJavaIdentifier(attrName);
1270
1271    writer.println();
1272
1273    if (inRDN)
1274    {
1275      writer.println("  // The field used for RDN attribute " + attrName + '.');
1276    }
1277    else if (required)
1278    {
1279      writer.println("  // The field used for required attribute " + attrName +
1280           '.');
1281    }
1282    else if (d.isOperational())
1283    {
1284      writer.println("  // The field used for operational attribute " +
1285           attrName + '.');
1286    }
1287    else
1288    {
1289      writer.println("  // The field used for optional attribute " + attrName +
1290           '.');
1291    }
1292
1293    boolean added = false;
1294    if (terse && attrName.equalsIgnoreCase(fieldName))
1295    {
1296      writer.print("  @LDAPField(");
1297    }
1298    else
1299    {
1300      writer.print("  @LDAPField(attribute=\"" + attrName + '"');
1301      added = true;
1302    }
1303
1304    if (ocNames.isEmpty())
1305    {
1306      // Don't need to do anything.  This should only be the case for
1307      // operational attributes.
1308    }
1309    else if (ocNames.size() == 1)
1310    {
1311      if ((! terse) || (! ocNames.iterator().next().equalsIgnoreCase(sc)))
1312      {
1313        if (added)
1314        {
1315          writer.println(",");
1316          writer.print("             objectClass=\"" +
1317               ocNames.iterator().next() + '"');
1318        }
1319        else
1320        {
1321          writer.println("objectClass=\"" +
1322               ocNames.iterator().next() + '"');
1323          added = true;
1324        }
1325      }
1326    }
1327    else
1328    {
1329      final Iterator<String> iterator = ocNames.iterator();
1330      if (added)
1331      {
1332        writer.println(",");
1333        writer.println("             objectClass={ \"" +
1334             iterator.next() + "\",");
1335      }
1336      else
1337      {
1338        writer.println("objectClass={ \"" +
1339             iterator.next() + "\",");
1340        added = true;
1341      }
1342
1343      while (iterator.hasNext())
1344      {
1345        final String name = iterator.next();
1346        if (iterator.hasNext())
1347        {
1348          writer.println("                           \"" + name + "\",");
1349        }
1350        else
1351        {
1352          writer.print("                           \"" + name + "\" }");
1353        }
1354      }
1355    }
1356
1357    if (inRDN)
1358    {
1359      if (added)
1360      {
1361        writer.println(",");
1362        writer.println("             inRDN=true,");
1363      }
1364      else
1365      {
1366        writer.println("inRDN=true,");
1367        added = true;
1368      }
1369      writer.print("             filterUsage=FilterUsage.ALWAYS_ALLOWED");
1370    }
1371    else
1372    {
1373      if (! terse)
1374      {
1375        if (added)
1376        {
1377          writer.println(",");
1378          writer.print("             " +
1379               "filterUsage=FilterUsage.CONDITIONALLY_ALLOWED");
1380        }
1381        else
1382        {
1383          writer.print("filterUsage=FilterUsage.CONDITIONALLY_ALLOWED");
1384          added = true;
1385        }
1386      }
1387    }
1388
1389    if (required)
1390    {
1391      if (added)
1392      {
1393        writer.println(",");
1394        writer.print("             requiredForEncode=true");
1395      }
1396      else
1397      {
1398        writer.print("requiredForEncode=true");
1399        added = true;
1400      }
1401    }
1402
1403    if (d.isOperational())
1404    {
1405      if (added)
1406      {
1407        writer.println(",");
1408        writer.println("             inAdd=false,");
1409      }
1410      else
1411      {
1412        writer.println("inAdd=false,");
1413        added = true;
1414      }
1415
1416      writer.print("             inModify=false");
1417    }
1418
1419    if (lazy)
1420    {
1421      if (added)
1422      {
1423        writer.println(",");
1424        writer.print("             lazilyLoad=true");
1425      }
1426      else
1427      {
1428        writer.print("lazilyLoad=true");
1429        added = true;
1430      }
1431    }
1432
1433    writer.println(")");
1434    if (d.isSingleValued())
1435    {
1436      writer.println("  private " + type + ' ' + fieldName + ';');
1437    }
1438    else
1439    {
1440      writer.println("  private " + type + "[] " + fieldName + ';');
1441    }
1442  }
1443
1444
1445
1446  /**
1447   * Writes getter, setter, and filter creation methods for the specified
1448   * attribute.
1449   *
1450   * @param  writer     The writer to use to write the methods.
1451   * @param  d          The attribute type definition to be written.
1452   * @param  type       The name of the Java type to use for the attribute.
1453   * @param  addSetter  Indicates whether to write a setter method.
1454   */
1455  private static void writeFieldMethods(@NotNull final PrintWriter writer,
1456                           @NotNull final AttributeTypeDefinition d,
1457                           @NotNull final String type,
1458                           final boolean addSetter)
1459  {
1460    writer.println();
1461    writer.println();
1462    writer.println();
1463
1464    final String attrName  = d.getNameOrOID();
1465    final String fieldName = PersistUtils.toJavaIdentifier(attrName);
1466    final String capFieldName = StaticUtils.capitalize(fieldName);
1467
1468    if (d.isSingleValued())
1469    {
1470      if (type.equals("DN"))
1471      {
1472        writer.println("  /**");
1473        writer.println("   * Retrieves the first value for the field " +
1474             "associated with the");
1475        writer.println("   * " + attrName + " attribute as a DN, if present.");
1476        writer.println("   *");
1477        writer.println("   * @return  The first value for the field " +
1478             "associated with the");
1479        writer.println("   *          " + attrName + " attribute, or");
1480        writer.println("   *          {@code null} if the field does not " +
1481             "have a value.");
1482        writer.println("   */");
1483        writer.println("  public DN get" + capFieldName + "DN()");
1484        writer.println("  {");
1485        writer.println("    return " + fieldName + ';');
1486        writer.println("  }");
1487
1488        writer.println();
1489        writer.println();
1490        writer.println();
1491
1492        writer.println("  /**");
1493        writer.println("   * Retrieves the object referenced by the DN held " +
1494             "in the");
1495        writer.println("   * " + attrName + " attribute, if present.");
1496        writer.println("   *");
1497        writer.println("   * @param  <T>  The type of object to return.");
1498        writer.println("   *");
1499        writer.println("   * @param  connection  The connection to use to " +
1500             "retrieve the entry.  It must");
1501        writer.println("   *                     not be {@code null}.");
1502        writer.println("   * @param  type        The type of object as which " +
1503             "to decode the entry.  It");
1504        writer.println("   *                     must not be {@code null}, " +
1505             "and the class must be marked");
1506        writer.println("   *                     with the {@code LDAPObject} " +
1507             "annotation type.");
1508        writer.println("   *");
1509        writer.println("   * @return  The object decoded from the entry with " +
1510             "the associated DN, or");
1511        writer.println("   *          {@code null} if the field does not " +
1512             "have a value or the referenced");
1513        writer.println("   *          entry does not exist.");
1514        writer.println("   *");
1515        writer.println("   * @throws  LDAPException  If a problem occurs " +
1516             "while attempting to retrieve");
1517        writer.println("   *                         the entry or decode it " +
1518             "as an object of the");
1519        writer.println("   *                         specified type.");
1520        writer.println("   */");
1521        writer.println("  public <T> T get" + capFieldName + "Object(");
1522        writer.println("                    final LDAPInterface connection,");
1523        writer.println("                    final Class<T> type)");
1524        writer.println("         throws LDAPException");
1525        writer.println("  {");
1526        writer.println("    return PersistUtils.getEntryAsObject(" + fieldName +
1527             ',');
1528        writer.println("         type, connection);");
1529        writer.println("  }");
1530
1531        if (addSetter)
1532        {
1533          writer.println();
1534          writer.println();
1535          writer.println();
1536
1537          writer.println("  /**");
1538          writer.println("   * Sets the value for the field associated with " +
1539               "the");
1540          writer.println("   * " + attrName + " attribute.");
1541          writer.println("   *");
1542          writer.println("   * @param  v  The value for the field associated " +
1543               "with the");
1544          writer.println("   *            " + attrName + " attribute.");
1545          writer.println("   */");
1546          writer.println("  public void set" + capFieldName + "(final DN v)");
1547          writer.println("  {");
1548          writer.println("    this." + fieldName + " = v;");
1549          writer.println("  }");
1550
1551          writer.println();
1552          writer.println();
1553          writer.println();
1554
1555          writer.println("  /**");
1556          writer.println("   * Sets the value for the field associated with " +
1557               "the");
1558          writer.println("   * " + attrName + " attribute.");
1559          writer.println("   *");
1560          writer.println("   * @param  v  The string representation of the " +
1561               "value for the field associated");
1562          writer.println("   *            with the " + attrName +
1563               " attribute.");
1564          writer.println("   *");
1565          writer.println("   * @throws  LDAPException  If the provided " +
1566               "string cannot be parsed as a DN.");
1567          writer.println("   */");
1568          writer.println("  public void set" + capFieldName +
1569               "(final String v)");
1570          writer.println("         throws LDAPException");
1571          writer.println("  {");
1572          writer.println("    if (v == null)");
1573          writer.println("    {");
1574          writer.println("      this." + fieldName + " = null;");
1575          writer.println("    }");
1576          writer.println("    else");
1577          writer.println("    {");
1578          writer.println("      this." + fieldName + " = new DN(v);");
1579          writer.println("    }");
1580          writer.println("  }");
1581        }
1582      }
1583      else
1584      {
1585        writer.println("  /**");
1586        writer.println("   * Retrieves the value for the field associated " +
1587             "with the");
1588        writer.println("   * " + attrName + " attribute, if present.");
1589        writer.println("   *");
1590        writer.println("   * @return  The value for the field associated " +
1591             "with the");
1592        writer.println("   *          " + attrName + " attribute, or");
1593        writer.println("   *          {@code null} if the field does not " +
1594             "have a value.");
1595        writer.println("   */");
1596        writer.println("  public " + type + " get" + capFieldName + "()");
1597        writer.println("  {");
1598        writer.println("    return " + fieldName + ';');
1599        writer.println("  }");
1600
1601        if (addSetter)
1602        {
1603          writer.println();
1604          writer.println();
1605          writer.println();
1606
1607          writer.println("  /**");
1608          writer.println("   * Sets the value for the field associated with " +
1609               "the");
1610          writer.println("   * " + attrName + " attribute.");
1611          writer.println("   *");
1612          writer.println("   * @param  v  The value for the field associated " +
1613               "with the");
1614          writer.println("   *            " + attrName + " attribute.");
1615          writer.println("   */");
1616          writer.println("  public void set" + capFieldName + "(final " + type +
1617               " v)");
1618          writer.println("  {");
1619          writer.println("    this." + fieldName + " = v;");
1620          writer.println("  }");
1621        }
1622      }
1623    }
1624    else
1625    {
1626      if (type.equals("DN"))
1627      {
1628        writer.println("  /**");
1629        writer.println("   * Retrieves the first value for the field " +
1630             "associated with the");
1631        writer.println("   * " + attrName + " attribute as a DN, if present.");
1632        writer.println("   *");
1633        writer.println("   * @return  The first value for the field " +
1634             "associated with the");
1635        writer.println("   *          " + attrName + " attribute, or");
1636        writer.println("   *          {@code null} if that attribute was not " +
1637             "present in the entry or");
1638        writer.println("   *          does not have any values.");
1639        writer.println("   */");
1640        writer.println("  public DN getFirst" + capFieldName + "DN()");
1641        writer.println("  {");
1642        writer.println("    if ((" + fieldName + " == null) ||");
1643        writer.println("        (" + fieldName + ".length == 0))");
1644        writer.println("    {");
1645        writer.println("      return null;");
1646        writer.println("    }");
1647        writer.println("    else");
1648        writer.println("    {");
1649        writer.println("      return " + fieldName + "[0];");
1650        writer.println("    }");
1651        writer.println("  }");
1652
1653        writer.println();
1654        writer.println();
1655        writer.println();
1656
1657        writer.println("  /**");
1658        writer.println("   * Retrieves the values for the field associated " +
1659             "with the");
1660        writer.println("   * " + attrName + " attribute as DNs, if present.");
1661        writer.println("   *");
1662        writer.println("   * @return  The values for the field associated " +
1663             "with the");
1664        writer.println("   *          " + attrName + " attribute, or");
1665        writer.println("   *          {@code null} if that attribute was not " +
1666             "present in the entry.");
1667        writer.println("   */");
1668        writer.println("  public DN[] get" + capFieldName + "DNs()");
1669        writer.println("  {");
1670        writer.println("    return " + fieldName + ';');
1671        writer.println("  }");
1672
1673        writer.println();
1674        writer.println();
1675        writer.println();
1676
1677        writer.println("  /**");
1678        writer.println("   * Retrieves the values for the field associated " +
1679             "with the");
1680        writer.println("   * " + attrName + " attribute as objects of the " +
1681             "specified type,");
1682        writer.println("   * if present.");
1683        writer.println("   *");
1684        writer.println("   * @param  <T>  The type of object to return.");
1685        writer.println("   *");
1686        writer.println("   * @param  connection  The connection to use to " +
1687             "retrieve the entries.  It");
1688        writer.println("   *                     must not be {@code null}.");
1689        writer.println("   * @param  type        The type of object as which " +
1690             "the entries should be");
1691        writer.println("   *                     decoded.  It must not be " +
1692             "{@code null}, and the class");
1693        writer.println("   *                     must be marked with the " +
1694             "{@code LDAPObject} annotation");
1695        writer.println("   *                     type.");
1696        writer.println("   *");
1697        writer.println("   * @return  A {@code PersistedObjects} object that " +
1698             "may be used to iterate");
1699        writer.println("   *          across the resulting objects.");
1700        writer.println("   *");
1701        writer.println("   * @throws  LDAPException  If the requested type " +
1702             "cannot be used with the LDAP");
1703        writer.println("   *                         SDK persistence " +
1704             "framework.");
1705        writer.println("   */");
1706        writer.println("  public <T> PersistedObjects<T> get" + capFieldName +
1707             "Objects(");
1708        writer.println("                                      final " +
1709             "LDAPInterface connection,");
1710        writer.println("                                      final Class<T> " +
1711             "type)");
1712        writer.println("         throws LDAPException");
1713        writer.println("  {");
1714        writer.println("    return PersistUtils.getEntriesAsObjects(" +
1715             fieldName + ',');
1716        writer.println("         type, connection);");
1717        writer.println("  }");
1718
1719        if (addSetter)
1720        {
1721          writer.println();
1722          writer.println();
1723          writer.println();
1724
1725          writer.println("  /**");
1726          writer.println("   * Sets the values for the field associated with " +
1727               "the");
1728          writer.println("   * " + attrName + " attribute.");
1729          writer.println("   *");
1730          writer.println("   * @param  v  The values for the field " +
1731               "associated with the");
1732          writer.println("   *            " + attrName + " attribute.");
1733          writer.println("   */");
1734          writer.println("  public void set" + capFieldName +
1735               "(final DN... v)");
1736          writer.println("  {");
1737          writer.println("    this." + fieldName + " = v;");
1738          writer.println("  }");
1739
1740          writer.println();
1741          writer.println();
1742          writer.println();
1743
1744          writer.println("  /**");
1745          writer.println("   * Sets the values for the field associated with " +
1746               "the");
1747          writer.println("   * " + attrName + " attribute.");
1748          writer.println("   *");
1749          writer.println("   * @param  v  The string representations of the " +
1750               "values for the field");
1751          writer.println("   *            associated with the " + attrName +
1752               " attribute.");
1753          writer.println("   *");
1754          writer.println("   * @throws  LDAPException  If any of the " +
1755               "provided strings cannot be parsed as");
1756          writer.println("   *                         a DN.");
1757          writer.println("   */");
1758          writer.println("  public void set" + capFieldName +
1759               "(final String... v)");
1760          writer.println("         throws LDAPException");
1761          writer.println("  {");
1762          writer.println("    if (v == null)");
1763          writer.println("    {");
1764          writer.println("      this." + fieldName + " = null;");
1765          writer.println("    }");
1766          writer.println("    else");
1767          writer.println("    {");
1768          writer.println("      this." + fieldName + " = new DN[v.length];");
1769          writer.println("      for (int i=0; i < v.length; i++)");
1770          writer.println("      {");
1771          writer.println("        this." + fieldName + "[i] = new DN(v[i]);");
1772          writer.println("      }");
1773          writer.println("    }");
1774          writer.println("  }");
1775        }
1776      }
1777      else
1778      {
1779        writer.println("  /**");
1780        writer.println("   * Retrieves the first value for the field " +
1781             "associated with the");
1782        writer.println("   * " + attrName + " attribute, if present.");
1783        writer.println("   *");
1784        writer.println("   * @return  The first value for the field " +
1785             "associated with the");
1786        writer.println("   *          " + attrName + " attribute, or");
1787        writer.println("   *          {@code null} if that attribute was not " +
1788             "present in the entry or");
1789        writer.println("   *          does not have any values.");
1790        writer.println("   */");
1791        writer.println("  public " + type + " getFirst" + capFieldName + "()");
1792        writer.println("  {");
1793        writer.println("    if ((" + fieldName + " == null) ||");
1794        writer.println("        (" + fieldName + ".length == 0))");
1795        writer.println("    {");
1796        writer.println("      return null;");
1797        writer.println("    }");
1798        writer.println("    else");
1799        writer.println("    {");
1800        writer.println("      return " + fieldName + "[0];");
1801        writer.println("    }");
1802        writer.println("  }");
1803
1804        writer.println();
1805        writer.println();
1806        writer.println();
1807
1808        writer.println("  /**");
1809        writer.println("   * Retrieves the values for the field associated " +
1810             "with the");
1811        writer.println("   * " + attrName + " attribute, if present.");
1812        writer.println("   *");
1813        writer.println("   * @return  The values for the field associated " +
1814             "with the");
1815        writer.println("   *          " + attrName + " attribute, or");
1816        writer.println("   *          {@code null} if that attribute was not " +
1817             "present in the entry.");
1818        writer.println("   */");
1819        writer.println("  public " + type + "[] get" + capFieldName + "()");
1820        writer.println("  {");
1821        writer.println("    return " + fieldName + ';');
1822        writer.println("  }");
1823
1824        if (addSetter)
1825        {
1826          writer.println();
1827          writer.println();
1828          writer.println();
1829
1830          writer.println("  /**");
1831          writer.println("   * Sets the values for the field associated with " +
1832               "the");
1833          writer.println("   * " + attrName + " attribute.");
1834          writer.println("   *");
1835          writer.println("   * @param  v  The values for the field " +
1836               "associated with the");
1837          writer.println("   *            " + attrName + " attribute.");
1838          writer.println("   */");
1839          writer.println("  public void set" + capFieldName + "(final " + type +
1840               "... v)");
1841          writer.println("  {");
1842          writer.println("    this." + fieldName + " = v;");
1843          writer.println("  }");
1844        }
1845      }
1846    }
1847
1848
1849    writer.println();
1850    writer.println();
1851    writer.println();
1852
1853    writer.println("  /**");
1854    writer.println("   * Generates a filter that may be used to search for " +
1855         "objects of this type");
1856    writer.println("   * using the " + attrName + " attribute.");
1857    writer.println("   * The resulting filter may be combined with other " +
1858         "filter elements to create a");
1859    writer.println("   * more complex filter.");
1860    writer.println("   *");
1861    writer.println("   * @param  filterType  The type of filter to generate.");
1862    writer.println("   * @param  value       The value to use to use for the " +
1863         "filter.  It may be");
1864    writer.println("   *                     {@code null} only for a filter " +
1865         "type of");
1866    writer.println("   *                     {@code PRESENCE}.");
1867    writer.println("   *");
1868    writer.println("   * @return  The generated search filter.");
1869    writer.println("   *");
1870    writer.println("   * @throws  LDAPPersistException  If a problem is " +
1871         "encountered while attempting");
1872    writer.println("   *                                to generate the " +
1873         "filter.");
1874    writer.println("   */");
1875    writer.println("  public static Filter generate" + capFieldName +
1876         "Filter(");
1877    writer.println("                            final PersistFilterType " +
1878         "filterType,");
1879    writer.println("                            final " + type + " value)");
1880    writer.println("         throws LDAPPersistException");
1881    writer.println("  {");
1882    writer.println("    final byte[] valueBytes;");
1883    writer.println("    if (filterType == PersistFilterType.PRESENCE)");
1884    writer.println("    {");
1885    writer.println("      valueBytes = null;");
1886    writer.println("    }");
1887    writer.println("    else");
1888    writer.println("    {");
1889    writer.println("      if (value == null)");
1890    writer.println("      {");
1891    writer.println("        throw new LDAPPersistException(\"Unable to " +
1892         "generate a filter of type \" +");
1893    writer.println("             filterType.name() + \" with a null value " +
1894         "for attribute \" +");
1895    writer.println("             \"" + attrName + "\");");
1896    writer.println("      }");
1897    writer.println();
1898    writer.println("      final LDAPObjectHandler<?> objectHandler =");
1899    writer.println("           getPersister().getObjectHandler();");
1900    writer.println("      final FieldInfo fieldInfo = " +
1901         "objectHandler.getFields().get(");
1902    writer.println("           \"" + StaticUtils.toLowerCase(attrName) +
1903         "\");");
1904    writer.println();
1905    writer.println("      final DefaultObjectEncoder objectEncoder = new " +
1906         "DefaultObjectEncoder();");
1907    writer.println("      valueBytes = " +
1908         "objectEncoder.encodeFieldValue(fieldInfo.getField(),");
1909
1910    if (d.isSingleValued())
1911    {
1912      writer.println("           value,");
1913    }
1914    else
1915    {
1916      writer.println("           new " + type + "[] { value },");
1917    }
1918
1919    writer.println("           \"" + attrName + "\").getValueByteArray();");
1920    writer.println("    }");
1921    writer.println();
1922    writer.println("    switch (filterType)");
1923    writer.println("    {");
1924    writer.println("      case PRESENCE:");
1925    writer.println("        return Filter.createPresenceFilter(");
1926    writer.println("             \"" + attrName + "\");");
1927    writer.println("      case EQUALITY:");
1928    writer.println("        return Filter.createEqualityFilter(");
1929    writer.println("             \"" + attrName + "\",");
1930    writer.println("             valueBytes);");
1931    writer.println("      case STARTS_WITH:");
1932    writer.println("        return Filter.createSubstringFilter(");
1933    writer.println("             \"" + attrName + "\",");
1934    writer.println("             valueBytes, null, null);");
1935    writer.println("      case ENDS_WITH:");
1936    writer.println("        return Filter.createSubstringFilter(");
1937    writer.println("             \"" + attrName + "\",");
1938    writer.println("             null, null, valueBytes);");
1939    writer.println("      case CONTAINS:");
1940    writer.println("        return Filter.createSubstringFilter(");
1941    writer.println("             \"" + attrName + "\",");
1942    writer.println("             null, new byte[][] { valueBytes }, null);");
1943    writer.println("      case GREATER_OR_EQUAL:");
1944    writer.println("        return Filter.createGreaterOrEqualFilter(");
1945    writer.println("             \"" + attrName + "\",");
1946    writer.println("             valueBytes);");
1947    writer.println("      case LESS_OR_EQUAL:");
1948    writer.println("        return Filter.createLessOrEqualFilter(");
1949    writer.println("             \"" + attrName + "\",");
1950    writer.println("             valueBytes);");
1951    writer.println("      case APPROXIMATELY_EQUAL_TO:");
1952    writer.println("        return Filter.createApproximateMatchFilter(");
1953    writer.println("             \"" + attrName + "\",");
1954    writer.println("             valueBytes);");
1955    writer.println("      default:");
1956    writer.println("        // This should never happen.");
1957    writer.println("        throw new LDAPPersistException(\"Unrecognized " +
1958         "filter type \" +");
1959    writer.println("             filterType.name());");
1960    writer.println("    }");
1961    writer.println("  }");
1962  }
1963
1964
1965
1966  /**
1967   * Writes a {@code toString} method for the generated class.
1968   *
1969   * @param  writer            The writer to use to write the methods.
1970   * @param  className         The base name (without package information) for
1971   *                           the generated class.
1972   * @param  requiredAttrs     The set of required attributes for the generated
1973   *                           class.
1974   * @param  optionalAttrs     The set of optional attributes for the generated
1975   *                           class.
1976   * @param  operationalAttrs  The set of operational attributes for the
1977   *                           generated class.
1978   */
1979  private static void writeToString(@NotNull final PrintWriter writer,
1980       @NotNull final String className,
1981       @NotNull final Collection<AttributeTypeDefinition> requiredAttrs,
1982       @NotNull final Collection<AttributeTypeDefinition> optionalAttrs,
1983       @NotNull final Collection<AttributeTypeDefinition> operationalAttrs)
1984  {
1985    writer.println();
1986    writer.println();
1987    writer.println();
1988    writer.println("  /**");
1989    writer.println("   * Retrieves a string representation of this");
1990    writer.println("   * {@code " + className + "} object.");
1991    writer.println("   *");
1992    writer.println("   * @return  A string representation of this");
1993    writer.println("   *          {@code " + className + "} object.");
1994    writer.println("   */");
1995    writer.println("  @Override()");
1996    writer.println("  public String toString()");
1997    writer.println("  {");
1998    writer.println("    final StringBuilder buffer = new StringBuilder();");
1999    writer.println("    toString(buffer);");
2000    writer.println("    return buffer.toString();");
2001    writer.println("  }");
2002
2003    writer.println();
2004    writer.println();
2005    writer.println();
2006    writer.println("  /**");
2007    writer.println("   * Appends a string representation of this");
2008    writer.println("   * {@code " + className + "} object");
2009    writer.println("   * to the provided buffer.");
2010    writer.println("   *");
2011    writer.println("   * @param  buffer  The buffer to which the string " +
2012         "representation should be");
2013    writer.println("   *                 appended.");
2014    writer.println("   */");
2015    writer.println("  public void toString(final StringBuilder buffer)");
2016    writer.println("  {");
2017    writer.println("    buffer.append(\"" + className + "(\");");
2018    writer.println();
2019    writer.println("    boolean appended = false;");
2020    writer.println("    if (ldapEntry != null)");
2021    writer.println("    {");
2022    writer.println("      appended = true;");
2023    writer.println("      buffer.append(\"entryDN='\");");
2024    writer.println("      buffer.append(ldapEntry.getDN());");
2025    writer.println("      buffer.append('\\'');");
2026    writer.println("    }");
2027
2028    for (final AttributeTypeDefinition d : requiredAttrs)
2029    {
2030      writeToStringField(writer, d);
2031    }
2032
2033    for (final AttributeTypeDefinition d : optionalAttrs)
2034    {
2035      writeToStringField(writer, d);
2036    }
2037
2038    for (final AttributeTypeDefinition d : operationalAttrs)
2039    {
2040      writeToStringField(writer, d);
2041    }
2042
2043    writer.println();
2044    writer.println("    buffer.append(')');");
2045    writer.println("  }");
2046  }
2047
2048
2049
2050  /**
2051   * Writes information about the provided field for use in the {@code toString}
2052   * method.
2053   *
2054   * @param  w  The writer to use to write the {@code toString} content.
2055   * @param  d  The attribute type definition for the field to write.
2056   */
2057  private static void writeToStringField(@NotNull final PrintWriter w,
2058                           @NotNull final AttributeTypeDefinition d)
2059  {
2060    final String fieldName = PersistUtils.toJavaIdentifier(d.getNameOrOID());
2061    w.println();
2062    w.println("    if (" +  fieldName + " != null)");
2063    w.println("    {");
2064    w.println("      if (appended)");
2065    w.println("      {");
2066    w.println("        buffer.append(\", \");");
2067    w.println("      }");
2068    w.println("      appended = true;");
2069    w.println("      buffer.append(\"" + fieldName + "=\");");
2070    if (d.isSingleValued())
2071    {
2072      w.println("      buffer.append(" + fieldName + ");");
2073    }
2074    else
2075    {
2076      w.println("      buffer.append(Arrays.toString(" + fieldName + "));");
2077    }
2078    w.println("    }");
2079  }
2080
2081
2082
2083  /**
2084   * Retrieves the Java type to use for the provided attribute type definition.
2085   * For multi-valued attributes, the value returned will be the base type
2086   * without square brackets to indicate an array.
2087   *
2088   * @param  schema  The schema to use to determine the syntax for the
2089   *                 attribute.
2090   * @param  d       The attribute type definition for which to get the Java
2091   *                 type.
2092   *
2093   * @return  The Java type to use for the provided attribute type definition.
2094   */
2095  @NotNull()
2096  private String getJavaType(@NotNull final Schema schema,
2097                             @NotNull final AttributeTypeDefinition d)
2098  {
2099    if (! d.isSingleValued())
2100    {
2101      needArrays = true;
2102    }
2103
2104    final String syntaxOID = d.getSyntaxOID(schema);
2105    if (syntaxOID == null)
2106    {
2107      return "String";
2108    }
2109
2110    final String oid;
2111    final int bracePos = syntaxOID.indexOf('{');
2112    if (bracePos > 0)
2113    {
2114      oid = syntaxOID.substring(0, bracePos);
2115    }
2116    else
2117    {
2118      oid = syntaxOID;
2119    }
2120
2121    if (oid.equals("1.3.6.1.4.1.1466.115.121.1.7"))
2122    {
2123      // Boolean
2124      return "Boolean";
2125    }
2126    else if (oid.equals("1.3.6.1.4.1.4203.1.1.2") ||
2127             oid.equals("1.3.6.1.4.1.1466.115.121.1.5") ||
2128             oid.equals("1.3.6.1.4.1.1466.115.121.1.8") ||
2129             oid.equals("1.3.6.1.4.1.1466.115.121.1.9") ||
2130             oid.equals("1.3.6.1.4.1.1466.115.121.1.10") ||
2131             oid.equals("1.3.6.1.4.1.1466.115.121.1.28") ||
2132             oid.equals("1.3.6.1.4.1.1466.115.121.1.40"))
2133    {
2134      // auth password
2135      // binary
2136      // certificate
2137      // certificate list
2138      // certificate pair
2139      // JPEG
2140      // octet string
2141      return "byte[]";
2142    }
2143    else if (oid.equals("1.3.6.1.4.1.1466.115.121.1.24"))
2144    {
2145      // generalized time.
2146      needDate = true;
2147      return "Date";
2148    }
2149    else if (oid.equals("1.3.6.1.4.1.1466.115.121.1.27"))
2150    {
2151      // integer
2152      return "Long";
2153    }
2154    else if (oid.equals("1.3.6.1.4.1.1466.115.121.1.12") ||
2155             oid.equals("1.3.6.1.4.1.1466.115.121.1.34"))
2156    {
2157      // DN
2158      // name and optional UID
2159      needDN = true;
2160      if (! d.isSingleValued())
2161      {
2162        needPersistedObjects = true;
2163      }
2164      return "DN";
2165    }
2166    else
2167    {
2168      return "String";
2169    }
2170  }
2171
2172
2173
2174  /**
2175   * {@inheritDoc}
2176   */
2177  @Override()
2178  @NotNull()
2179  public LinkedHashMap<String[],String> getExampleUsages()
2180  {
2181    final LinkedHashMap<String[],String> examples =
2182         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
2183
2184    final String[] args =
2185    {
2186      "--hostname", "server.example.com",
2187      "--port", "389",
2188      "--bindDN", "uid=admin,dc=example,dc=com",
2189      "--bindPassword", "password",
2190      "--outputDirectory", "src/com/example",
2191      "--structuralClass", "myStructuralClass",
2192      "--auxiliaryClass", "auxClass1",
2193      "--auxiliaryClass", "auxClass2",
2194      "--rdnAttribute", "cn",
2195      "--defaultParentDN", "dc=example,dc=com",
2196      "--packageName", "com.example",
2197      "--className", "MyObject"
2198    };
2199    examples.put(args, INFO_GEN_SOURCE_EXAMPLE_1.get());
2200
2201    return examples;
2202  }
2203}