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