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