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