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