001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 UnboundID Corp.
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     */
021    package com.unboundid.ldap.sdk.persist;
022    
023    
024    
025    import java.io.File;
026    import java.io.FileWriter;
027    import java.io.OutputStream;
028    import java.io.PrintWriter;
029    import java.io.Serializable;
030    import java.util.Arrays;
031    import java.util.Collection;
032    import java.util.Date;
033    import java.util.Iterator;
034    import java.util.LinkedHashMap;
035    import java.util.TreeMap;
036    import java.util.TreeSet;
037    
038    import com.unboundid.ldap.sdk.DN;
039    import com.unboundid.ldap.sdk.Entry;
040    import com.unboundid.ldap.sdk.Filter;
041    import com.unboundid.ldap.sdk.LDAPConnection;
042    import com.unboundid.ldap.sdk.LDAPException;
043    import com.unboundid.ldap.sdk.LDAPInterface;
044    import com.unboundid.ldap.sdk.ReadOnlyEntry;
045    import com.unboundid.ldap.sdk.ResultCode;
046    import com.unboundid.ldap.sdk.Version;
047    import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
048    import com.unboundid.ldap.sdk.schema.ObjectClassDefinition;
049    import com.unboundid.ldap.sdk.schema.ObjectClassType;
050    import com.unboundid.ldap.sdk.schema.Schema;
051    import com.unboundid.util.LDAPCommandLineTool;
052    import com.unboundid.util.Mutable;
053    import com.unboundid.util.ThreadSafety;
054    import com.unboundid.util.ThreadSafetyLevel;
055    import com.unboundid.util.args.ArgumentException;
056    import com.unboundid.util.args.ArgumentParser;
057    import com.unboundid.util.args.BooleanArgument;
058    import com.unboundid.util.args.DNArgument;
059    import com.unboundid.util.args.FileArgument;
060    import com.unboundid.util.args.StringArgument;
061    
062    import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
063    import static com.unboundid.util.Debug.*;
064    import static com.unboundid.util.StaticUtils.*;
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)
075    public 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<String>();
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");
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");
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");
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");
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");
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");
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");
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");
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");
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 (LDAPException le)
416        {
417          debugException(le);
418          err(ERR_GEN_SOURCE_CANNOT_CONNECT.get(getExceptionMessage(le)));
419          return le.getResultCode();
420        }
421    
422        final Schema schema;
423        try
424        {
425          schema = conn.getSchema();
426          if (schema == null)
427          {
428            err(ERR_GEN_SOURCE_CANNOT_READ_SCHEMA.get(
429                 ERR_GEN_SOURCE_SCHEMA_NOT_RETURNED.get()));
430            return ResultCode.NO_RESULTS_RETURNED;
431          }
432        }
433        catch (LDAPException le)
434        {
435          debugException(le);
436          err(ERR_GEN_SOURCE_CANNOT_READ_SCHEMA.get(getExceptionMessage(le)));
437          return le.getResultCode();
438        }
439        finally
440        {
441          conn.close();
442        }
443    
444        return generateSourceFile(schema, terseArg.isPresent());
445      }
446    
447    
448    
449      /**
450       * Generates the source file using the information in the provided schema.
451       *
452       * @param  schema  The schema to use to generate the source file.
453       * @param  terse   Indicates whether to use terse mode when generating the
454       *                 source file.  If this is {@code true}, then all optional
455       *                 elements will be omitted from annotations.
456       *
457       * @return  A result code obtained for the processing.
458       */
459      private ResultCode generateSourceFile(final Schema schema,
460                                            final boolean terse)
461      {
462        // Retrieve and process the structural object class.
463        final TreeMap<String,AttributeTypeDefinition> requiredAttrs =
464             new TreeMap<String,AttributeTypeDefinition>();
465        final TreeMap<String,AttributeTypeDefinition> optionalAttrs =
466             new TreeMap<String,AttributeTypeDefinition>();
467        final TreeMap<String,TreeSet<String>> requiredAttrOCs =
468             new TreeMap<String,TreeSet<String>>();
469        final TreeMap<String,TreeSet<String>> optionalAttrOCs =
470             new TreeMap<String,TreeSet<String>>();
471        final TreeMap<String,String> types = new TreeMap<String,String>();
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 =
495             new TreeMap<String,ObjectClassDefinition>();
496        if (auxiliaryClassArg.isPresent())
497        {
498          for (final String s : auxiliaryClassArg.getValues())
499          {
500            final ObjectClassDefinition oc = schema.getObjectClass(s);
501            if (oc == null)
502            {
503              err(ERR_GEN_SOURCE_AUXILIARY_CLASS_NOT_FOUND.get(s));
504              return ResultCode.PARAM_ERROR;
505            }
506    
507            if  (oc.getObjectClassType(schema) != ObjectClassType.AUXILIARY)
508            {
509              err(ERR_GEN_SOURCE_AUXILIARY_CLASS_NOT_AUXILIARY.get(s));
510              return ResultCode.PARAM_ERROR;
511            }
512    
513            auxiliaryOCs.put(toLowerCase(s), oc);
514    
515            processObjectClass(oc, schema, requiredAttrs, requiredAttrOCs,
516                 optionalAttrs, optionalAttrOCs, types);
517          }
518        }
519    
520    
521        // Determine the appropriate set of superior object classes.
522        final TreeMap<String,ObjectClassDefinition> superiorOCs =
523             new TreeMap<String,ObjectClassDefinition>();
524        for (final ObjectClassDefinition s :
525             structuralOC.getSuperiorClasses(schema, true))
526        {
527          superiorOCs.put(toLowerCase(s.getNameOrOID()), s);
528        }
529    
530        for (final ObjectClassDefinition d : auxiliaryOCs.values())
531        {
532          for (final ObjectClassDefinition s : d.getSuperiorClasses(schema, true))
533          {
534            superiorOCs.put(toLowerCase(s.getNameOrOID()), s);
535          }
536        }
537    
538        superiorOCs.remove(toLowerCase(structuralClassName));
539        for (final String s : auxiliaryOCs.keySet())
540        {
541          superiorOCs.remove(s);
542        }
543    
544    
545        // Retrieve and process the operational attributes.
546        final TreeMap<String,AttributeTypeDefinition> operationalAttrs =
547             new TreeMap<String,AttributeTypeDefinition>();
548        if (operationalAttributeArg.isPresent())
549        {
550          for (final String s : operationalAttributeArg.getValues())
551          {
552            final AttributeTypeDefinition d = schema.getAttributeType(s);
553            if (d == null)
554            {
555              err(ERR_GEN_SOURCE_OPERATIONAL_ATTRIBUTE_NOT_DEFINED.get(s));
556              return ResultCode.PARAM_ERROR;
557            }
558            else if (! d.isOperational())
559            {
560              err(ERR_GEN_SOURCE_OPERATIONAL_ATTRIBUTE_NOT_OPERATIONAL.get(s));
561              return ResultCode.PARAM_ERROR;
562            }
563            else
564            {
565              final String lowerName = toLowerCase(s);
566              operationalAttrs.put(lowerName, d);
567              types.put(lowerName, getJavaType(schema, d));
568            }
569          }
570        }
571    
572    
573        // Make sure all of the configured RDN attributes are allowed by at least
574        // one of the associated object classes.
575        final TreeSet<String> rdnAttrs = new TreeSet<String>();
576        for (final String s : rdnAttributeArg.getValues())
577        {
578          final AttributeTypeDefinition d = schema.getAttributeType(s);
579          if (d == null)
580          {
581            err(ERR_GEN_SOURCE_RDN_ATTRIBUTE_NOT_DEFINED.get(s));
582            return ResultCode.PARAM_ERROR;
583          }
584    
585          final String lowerName = toLowerCase(d.getNameOrOID());
586          rdnAttrs.add(lowerName);
587          if (requiredAttrs.containsKey(lowerName))
588          {
589            // No action required.
590          }
591          else if (optionalAttrs.containsKey(lowerName))
592          {
593            // Move the attribute to the required set.
594            requiredAttrs.put(lowerName, optionalAttrs.remove(lowerName));
595            requiredAttrOCs.put(lowerName, optionalAttrOCs.remove(lowerName));
596          }
597          else
598          {
599            err(ERR_GEN_SOURCE_RDN_ATTRIBUTE_NOT_DEFINED.get(s));
600            return ResultCode.PARAM_ERROR;
601          }
602        }
603    
604    
605        // Make sure all of the configured lazily-loaded attributes are allowed by
606        // at least one of the associated object classes or matches a configured
607        // operational attribute.
608        final TreeSet<String> lazyAttrs = new TreeSet<String>();
609        for (final String s : lazyAttributeArg.getValues())
610        {
611          final AttributeTypeDefinition d = schema.getAttributeType(s);
612          if (d == null)
613          {
614            err(ERR_GEN_SOURCE_LAZY_ATTRIBUTE_NOT_DEFINED.get(s));
615            return ResultCode.PARAM_ERROR;
616          }
617    
618          final String lowerName = toLowerCase(d.getNameOrOID());
619          lazyAttrs.add(lowerName);
620          if (requiredAttrs.containsKey(lowerName) ||
621              optionalAttrs.containsKey(lowerName) ||
622              operationalAttrs.containsKey(lowerName))
623          {
624            // No action required.
625          }
626          else
627          {
628            err(ERR_GEN_SOURCE_LAZY_ATTRIBUTE_NOT_ALLOWED.get(s));
629            return ResultCode.PARAM_ERROR;
630          }
631        }
632    
633    
634        final String className;
635        if (classNameArg.isPresent())
636        {
637          className = classNameArg.getValue();
638          final StringBuilder invalidReason = new StringBuilder();
639          if (! PersistUtils.isValidJavaIdentifier(className, invalidReason))
640          {
641            err(ERR_GEN_SOURCE_INVALID_CLASS_NAME.get(className,
642                 invalidReason.toString()));
643            return ResultCode.PARAM_ERROR;
644          }
645        }
646        else
647        {
648          className =
649               capitalize(PersistUtils.toJavaIdentifier(structuralClassName));
650        }
651    
652    
653        final File sourceFile = new File(outputDirectoryArg.getValue(),
654             className + ".java");
655        final PrintWriter writer;
656        try
657        {
658          writer = new PrintWriter(new FileWriter(sourceFile));
659        }
660        catch (Exception e)
661        {
662          debugException(e);
663          err(ERR_GEN_SOURCE_CANNOT_CREATE_WRITER.get(sourceFile.getAbsolutePath(),
664               getExceptionMessage(e)));
665          return ResultCode.LOCAL_ERROR;
666        }
667    
668    
669        if (packageNameArg.isPresent())
670        {
671          final String packageName = packageNameArg.getValue();
672          if (packageName.length() > 0)
673          {
674            writer.println("package " + packageName + ';');
675            writer.println();
676            writer.println();
677            writer.println();
678          }
679        }
680    
681        boolean javaImports = false;
682        if (needArrays)
683        {
684          writer.println("import " + Arrays.class.getName() + ';');
685          javaImports = true;
686        }
687    
688        if (needDate)
689        {
690          writer.println("import " + Date.class.getName() + ';');
691          javaImports = true;
692        }
693    
694        if (javaImports)
695        {
696          writer.println();
697        }
698    
699        if (needDN)
700        {
701          writer.println("import " + DN.class.getName() + ';');
702        }
703    
704        writer.println("import " + Entry.class.getName() + ';');
705        writer.println("import " + Filter.class.getName() + ';');
706    
707        if (needDN)
708        {
709          writer.println("import " + LDAPException.class.getName() + ';');
710          writer.println("import " + LDAPInterface.class.getName() + ';');
711        }
712    
713        writer.println("import " + ReadOnlyEntry.class.getName() + ';');
714        writer.println("import " + DefaultObjectEncoder.class.getName() + ';');
715        writer.println("import " + FieldInfo.class.getName() + ';');
716        writer.println("import " + FilterUsage.class.getName() + ';');
717        writer.println("import " + LDAPEntryField.class.getName() + ';');
718        writer.println("import " + LDAPField.class.getName() + ';');
719        writer.println("import " + LDAPObject.class.getName() + ';');
720        writer.println("import " + LDAPObjectHandler.class.getName() + ';');
721        writer.println("import " + LDAPPersister.class.getName() + ';');
722        writer.println("import " + LDAPPersistException.class.getName() + ';');
723    
724        if (needPersistedObjects)
725        {
726          writer.println("import " + PersistedObjects.class.getName() + ';');
727        }
728    
729        writer.println("import " + PersistFilterType.class.getName() + ';');
730    
731        if (needDN)
732        {
733          writer.println("import " + PersistUtils.class.getName() + ';');
734        }
735    
736        writer.println();
737        writer.println();
738        writer.println();
739        writer.println("/**");
740        writer.println(" * This class provides an implementation of an object " +
741             "that can be used to");
742        writer.println(" * represent " + structuralClassName +
743             " objects in the directory.");
744        writer.println(" * It was generated by the " + getToolName() +
745             " tool provided with the");
746        writer.println(" * UnboundID LDAP SDK for Java.  It " +
747             "may be customized as desired to better suit");
748        writer.println(" * your needs.");
749        writer.println(" */");
750        writer.println("@LDAPObject(structuralClass=\"" + structuralClassName +
751             "\",");
752    
753        switch (auxiliaryOCs.size())
754        {
755          case 0:
756            // No action required.
757            break;
758    
759          case 1:
760            writer.println("            auxiliaryClass=\"" +
761                 auxiliaryOCs.values().iterator().next().getNameOrOID() + "\",");
762            break;
763    
764          default:
765            final Iterator<ObjectClassDefinition> iterator =
766                 auxiliaryOCs.values().iterator();
767            writer.println("            auxiliaryClass={ \"" +
768                 iterator.next().getNameOrOID() + "\",");
769            while (iterator.hasNext())
770            {
771              final String ocName = iterator.next().getNameOrOID();
772              if (iterator.hasNext())
773              {
774                writer.println("                             \"" + ocName +
775                     "\",");
776              }
777              else
778              {
779                writer.println("                             \"" + ocName +
780                     "\" },");
781              }
782            }
783            break;
784        }
785    
786        switch (superiorOCs.size())
787        {
788          case 0:
789            // No action required.
790            break;
791    
792          case 1:
793            writer.println("            superiorClass=\"" +
794                 superiorOCs.values().iterator().next().getNameOrOID() + "\",");
795            break;
796    
797          default:
798            final Iterator<ObjectClassDefinition> iterator =
799                 superiorOCs.values().iterator();
800            writer.println("            superiorClass={ \"" +
801                 iterator.next().getNameOrOID() + "\",");
802            while (iterator.hasNext())
803            {
804              final String ocName = iterator.next().getNameOrOID();
805              if (iterator.hasNext())
806              {
807                writer.println("                             \"" + ocName +
808                     "\",");
809              }
810              else
811              {
812                writer.println("                             \"" + ocName +
813                     "\" },");
814              }
815            }
816            break;
817        }
818    
819        if (defaultParentDNArg.isPresent())
820        {
821          writer.println("            defaultParentDN=\"" +
822               defaultParentDNArg.getValue() + "\",");
823        }
824    
825        writer.println("            postDecodeMethod=\"doPostDecode\",");
826        writer.println("            postEncodeMethod=\"doPostEncode\")");
827        writer.println("public class " + className);
828        writer.println("{");
829    
830        if (! terse)
831        {
832          writer.println("  /*");
833          writer.println("   * NOTE:  This class includes a number of annotation " +
834               "elements which are not");
835          writer.println("   * required but have been provided to make it easier " +
836               "to edit the resulting");
837          writer.println("   * source code.  If you want to exclude these " +
838               "unnecessary annotation");
839          writer.println("   * elements, use the '--terse' command-line argument.");
840          writer.println("   */");
841          writer.println();
842          writer.println();
843          writer.println();
844        }
845    
846        writer.println("  // The field to use to hold a read-only copy of the " +
847             "associated entry.");
848        writer.println("  @LDAPEntryField()");
849        writer.println("  private ReadOnlyEntry ldapEntry;");
850    
851    
852        // Add all of the fields.  First the fields for the RDN attributes, then
853        // for the rest of the required attributes, then for the optional
854        // attributes, and finally any operational attributes.
855        for (final String lowerName : rdnAttrs)
856        {
857          final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
858          final TreeSet<String> ocNames = requiredAttrOCs.get(lowerName);
859          writeField(writer, d, types.get(lowerName), ocNames, true, true,
860               structuralClassName, false, terse);
861        }
862    
863        for (final String lowerName : requiredAttrs.keySet())
864        {
865          if (rdnAttrs.contains(lowerName))
866          {
867            continue;
868          }
869    
870          final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
871          final TreeSet<String> ocNames = requiredAttrOCs.get(lowerName);
872          writeField(writer, d, types.get(lowerName), ocNames, false, true,
873               structuralClassName, lazyAttrs.contains(lowerName), terse);
874        }
875    
876        for (final String lowerName : optionalAttrs.keySet())
877        {
878          final AttributeTypeDefinition d = optionalAttrs.get(lowerName);
879          final TreeSet<String> ocNames = optionalAttrOCs.get(lowerName);
880          writeField(writer, d, types.get(lowerName), ocNames, false, false,
881               structuralClassName, lazyAttrs.contains(lowerName), terse);
882        }
883    
884        for (final String lowerName : operationalAttrs.keySet())
885        {
886          final AttributeTypeDefinition d = operationalAttrs.get(lowerName);
887          final TreeSet<String> ocNames = EMPTY_TREE_SET;
888          writeField(writer, d, types.get(lowerName), ocNames, false, false,
889               structuralClassName, lazyAttrs.contains(lowerName), terse);
890        }
891    
892    
893        // Add the default constructor.
894        writer.println();
895        writer.println();
896        writer.println();
897        writer.println("  /**");
898        writer.println("   * Creates a new instance of this object.  All fields " +
899             "will be uninitialized,");
900        writer.println("   * so the setter methods should be used to assign " +
901             "values to them.");
902        writer.println("   */");
903        writer.println("  public " + className + "()");
904        writer.println("  {");
905        writer.println("    // No initialization will be performed by default.  " +
906             "Note that if you set");
907        writer.println("    // values for any fields marked with an @LDAPField, " +
908             "@LDAPDNField, or");
909        writer.println("    // @LDAPEntryField annotation, they will be " +
910             "overwritten in the course of");
911        writer.println("    // decoding initializing this object from an LDAP " +
912             "entry.");
913        writer.println("  }");
914    
915    
916        // Add a static decode method that can create an instance of the object
917        // from a given entry.
918        writer.println();
919        writer.println();
920        writer.println();
921        writer.println("  /**");
922        writer.println("   * Creates a new " + className + " object decoded");
923        writer.println("   * from the provided entry.");
924        writer.println("   *");
925        writer.println("   * @param  entry  The entry to be decoded.");
926        writer.println("   *");
927        writer.println("   * @return  The decoded " + className + " object.");
928        writer.println("   *");
929        writer.println("   * @throws  LDAPPersistException  If a problem occurs " +
930             "while attempting to");
931        writer.println("   *                                decode the provided " +
932             "entry.");
933        writer.println("   */");
934        writer.println("  public static " + className +
935             " decode(final Entry entry)");
936        writer.println("         throws LDAPPersistException");
937        writer.println("  {");
938        writer.println("    return getPersister().decode(entry);");
939        writer.println("  }");
940    
941    
942        // Add the getPersister method.
943        writer.println("");
944        writer.println("");
945        writer.println("");
946        writer.println("  /**");
947        writer.println("   * Retrieves an {@code LDAPPersister} instance that " +
948             "may be used to interact");
949        writer.println("   * with objects of this type.");
950        writer.println("   *");
951        writer.println("   * @return  An {@code LDAPPersister} instance that may " +
952             "be used to interact");
953        writer.println("   *          with objects of this type.");
954        writer.println("   *");
955        writer.println("   * @throws  LDAPPersistException  If a problem occurs " +
956             "while creating the");
957        writer.println("   *                                " +
958             "{@code LDAPPersister} instance.");
959        writer.println("   */");
960        writer.println("  public static LDAPPersister<" + className +
961             "> getPersister()");
962        writer.println("         throws LDAPPersistException");
963        writer.println("  {");
964        writer.println("    return LDAPPersister.getInstance(" + className +
965             ".class);");
966        writer.println("  }");
967    
968    
969        // Add the post-decode and post-encode methods.
970        writer.println();
971        writer.println();
972        writer.println();
973        writer.println("  /**");
974        writer.println("   * Performs any processing that may be necessary after " +
975             "initializing this");
976        writer.println("   * object from an LDAP entry.");
977        writer.println("   *");
978        writer.println("   * @throws  LDAPPersistException  If there is a " +
979             "problem with the object after");
980        writer.println("   *                                it has been decoded " +
981             "from an LDAP entry.");
982        writer.println("   */");
983        writer.println("  private void doPostDecode()");
984        writer.println("          throws LDAPPersistException");
985        writer.println("  {");
986        writer.println("    // No processing is needed by default.  You may " +
987             "provide an implementation");
988        writer.println("    // for this method if custom post-decode processing " +
989             "is needed.");
990        writer.println("  }");
991        writer.println();
992        writer.println();
993        writer.println();
994        writer.println("  /**");
995        writer.println("   * Performs any processing that may be necessary after " +
996             "encoding this object");
997        writer.println("   * to an LDAP entry.");
998        writer.println("   *");
999        writer.println("   * @param  entry  The entry that has been generated.  " +
1000             "It may be altered if");
1001        writer.println("   *                desired.");
1002        writer.println("   *");
1003        writer.println("   * @throws  LDAPPersistException  If the generated " +
1004             "entry should not be used.");
1005        writer.println("   */");
1006        writer.println("  private void doPostEncode(final Entry entry)");
1007        writer.println("          throws LDAPPersistException");
1008        writer.println("  {");
1009        writer.println("    // No processing is needed by default.  You may " +
1010             "provide an implementation");
1011        writer.println("    // for this method if custom post-encode processing " +
1012             "is needed.");
1013        writer.println("  }");
1014    
1015    
1016        // Add a method for getting a read-only copy of the associated entry.
1017        writer.println();
1018        writer.println();
1019        writer.println();
1020        writer.println("  /**");
1021        writer.println("   * Retrieves a read-only copy of the entry with which " +
1022             "this object is");
1023        writer.println("   * associated, if it is available.  It will only be " +
1024             "available if this object");
1025        writer.println("   * was decoded from or encoded to an LDAP entry.");
1026        writer.println("   *");
1027        writer.println("   * @return  A read-only copy of the entry with which " +
1028             "this object is");
1029        writer.println("   *          associated, or {@code null} if it is not " +
1030             "available.");
1031        writer.println("   */");
1032        writer.println("  public ReadOnlyEntry getLDAPEntry()");
1033        writer.println("  {");
1034        writer.println("    return ldapEntry;");
1035        writer.println("  }");
1036    
1037    
1038        // Add a method for getting the DN of the associated entry.
1039        writer.println();
1040        writer.println();
1041        writer.println();
1042        writer.println("  /**");
1043        writer.println("   * Retrieves the DN of the entry with which this " +
1044             "object is associated, if it");
1045        writer.println("   * is available.  It will only be available if this " +
1046             "object was decoded from or");
1047        writer.println("   * encoded to an LDAP entry.");
1048        writer.println("   *");
1049        writer.println("   * @return  The DN of the entry with which this object " +
1050             "is associated, or");
1051        writer.println("   *          {@code null} if it is not available.");
1052        writer.println("   */");
1053        writer.println("  public String getLDAPEntryDN()");
1054        writer.println("  {");
1055        writer.println("    if (ldapEntry == null)");
1056        writer.println("    {");
1057        writer.println("      return null;");
1058        writer.println("    }");
1059        writer.println("    else");
1060        writer.println("    {");
1061        writer.println("      return ldapEntry.getDN();");
1062        writer.println("    }");
1063        writer.println("  }");
1064    
1065    
1066        // Add getter, setter, and filter generation methods for all of the fields
1067        // associated with LDAP attributes.  First the fields for the RDN
1068        // attributes, then for the rest of the required attributes, and then for
1069        // the optional attributes.
1070        for (final String lowerName : rdnAttrs)
1071        {
1072          final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
1073          writeFieldMethods(writer, d, types.get(lowerName), true);
1074        }
1075    
1076        for (final String lowerName : requiredAttrs.keySet())
1077        {
1078          if (rdnAttrs.contains(lowerName))
1079          {
1080            continue;
1081          }
1082    
1083          final AttributeTypeDefinition d = requiredAttrs.get(lowerName);
1084          writeFieldMethods(writer, d, types.get(lowerName), true);
1085        }
1086    
1087        for (final String lowerName : optionalAttrs.keySet())
1088        {
1089          final AttributeTypeDefinition d = optionalAttrs.get(lowerName);
1090          writeFieldMethods(writer, d, types.get(lowerName), true);
1091        }
1092    
1093        for (final String lowerName : operationalAttrs.keySet())
1094        {
1095          final AttributeTypeDefinition d = operationalAttrs.get(lowerName);
1096          writeFieldMethods(writer, d, types.get(lowerName), false);
1097        }
1098    
1099        writeToString(writer, className, requiredAttrs.values(),
1100             optionalAttrs.values(), operationalAttrs.values());
1101    
1102        writer.println("}");
1103        writer.println();
1104        writer.close();
1105    
1106        return ResultCode.SUCCESS;
1107      }
1108    
1109    
1110    
1111    
1112    
1113      /**
1114       * Performs an appropriate set of processing for the provided object class to
1115       * ensure that all of the required and optional attributes are classified
1116       * properly.
1117       *
1118       * @param  oc   The object class to process.
1119       * @param  s    The server schema.
1120       * @param  ra   The set of required attributes identified so far.
1121       * @param  rac  The object classes referenced by the required attributes.
1122       * @param  oa   The set of optional attributes identified so far.
1123       * @param  oac  The object classes referenced by the optional attributes.
1124       * @param  t    A map of attribute type names to Java types.
1125       */
1126      void processObjectClass(final ObjectClassDefinition oc, final Schema s,
1127                final TreeMap<String,AttributeTypeDefinition> ra,
1128                final TreeMap<String,TreeSet<String>> rac,
1129                final TreeMap<String,AttributeTypeDefinition> oa,
1130                final TreeMap<String,TreeSet<String>> oac,
1131                final TreeMap<String,String> t)
1132      {
1133        for (final AttributeTypeDefinition d : oc.getRequiredAttributes(s, true))
1134        {
1135          if (d.hasNameOrOID("objectClass"))
1136          {
1137            continue;
1138          }
1139    
1140          final String lowerName = toLowerCase(d.getNameOrOID());
1141          if (ra.containsKey(lowerName))
1142          {
1143            rac.get(lowerName).add(oc.getNameOrOID());
1144          }
1145          else if (oa.containsKey(lowerName))
1146          {
1147            oa.remove(lowerName);
1148            ra.put(lowerName, d);
1149    
1150            final TreeSet<String> ocSet = oac.remove(lowerName);
1151            ocSet.add(oc.getNameOrOID());
1152            rac.put(lowerName, ocSet);
1153          }
1154          else
1155          {
1156            final TreeSet<String> ocSet = new TreeSet<String>();
1157            ocSet.add(oc.getNameOrOID());
1158            ra.put(lowerName, d);
1159            rac.put(lowerName, ocSet);
1160            t.put(lowerName, getJavaType(s, d));
1161          }
1162        }
1163    
1164        for (final AttributeTypeDefinition d : oc.getOptionalAttributes(s, true))
1165        {
1166          if (d.hasNameOrOID("objectClass"))
1167          {
1168            continue;
1169          }
1170    
1171          final String lowerName = toLowerCase(d.getNameOrOID());
1172          if (ra.containsKey(lowerName))
1173          {
1174            rac.get(lowerName).add(oc.getNameOrOID());
1175          }
1176          else if (oa.containsKey(lowerName))
1177          {
1178            oac.get(lowerName).add(oc.getNameOrOID());
1179          }
1180          else
1181          {
1182            final TreeSet<String> ocSet = new TreeSet<String>();
1183            ocSet.add(oc.getNameOrOID());
1184            oa.put(lowerName, d);
1185            oac.put(lowerName, ocSet);
1186            t.put(lowerName, getJavaType(s, d));
1187          }
1188        }
1189      }
1190    
1191    
1192    
1193      /**
1194       * Writes information about a field to the Java class file.
1195       *
1196       * @param  writer    The writer to which the field information should be
1197       *                   written.
1198       * @param  d         The attribute type definition.
1199       * @param  type      The name of the Java type to use for the field.
1200       * @param  ocNames   The names of the object classes for the attribute type.
1201       * @param  inRDN     Indicates whether the attribute should be included in
1202       *                   generated entry RDNs.
1203       * @param  required  Indicates whether the attribute should be considered
1204       *                   required.
1205       * @param  sc        The name of the structural object class for the object.
1206       * @param  lazy      Indicates whether the field should be marked for lazy
1207       *                   loading.
1208       * @param  terse     Indicates whether to use terse mode.
1209       */
1210      static void writeField(final PrintWriter writer,
1211                             final AttributeTypeDefinition d, final String type,
1212                             final TreeSet<String> ocNames,
1213                             final boolean inRDN, final boolean required,
1214                             final String sc, final boolean lazy,
1215                             final boolean terse)
1216      {
1217        final String attrName  = d.getNameOrOID();
1218        final String fieldName = PersistUtils.toJavaIdentifier(attrName);
1219    
1220        writer.println();
1221    
1222        if (inRDN)
1223        {
1224          writer.println("  // The field used for RDN attribute " + attrName + '.');
1225        }
1226        else if (required)
1227        {
1228          writer.println("  // The field used for required attribute " + attrName +
1229               '.');
1230        }
1231        else if (d.isOperational())
1232        {
1233          writer.println("  // The field used for operational attribute " +
1234               attrName + '.');
1235        }
1236        else
1237        {
1238          writer.println("  // The field used for optional attribute " + attrName +
1239               '.');
1240        }
1241    
1242        boolean added = false;
1243        if (terse && attrName.equalsIgnoreCase(fieldName))
1244        {
1245          writer.print("  @LDAPField(");
1246        }
1247        else
1248        {
1249          writer.print("  @LDAPField(attribute=\"" + attrName + '"');
1250          added = true;
1251        }
1252    
1253        if (ocNames.isEmpty())
1254        {
1255          // Don't need to do anything.  This should only be the case for
1256          // operational attributes.
1257        }
1258        else if (ocNames.size() == 1)
1259        {
1260          if ((! terse) || (! ocNames.iterator().next().equalsIgnoreCase(sc)))
1261          {
1262            if (added)
1263            {
1264              writer.println(",");
1265              writer.print("             objectClass=\"" +
1266                   ocNames.iterator().next() + '"');
1267            }
1268            else
1269            {
1270              writer.println("objectClass=\"" +
1271                   ocNames.iterator().next() + '"');
1272              added = true;
1273            }
1274          }
1275        }
1276        else
1277        {
1278          final Iterator<String> iterator = ocNames.iterator();
1279          if (added)
1280          {
1281            writer.println(",");
1282            writer.println("             objectClass={ \"" +
1283                 iterator.next() + "\",");
1284          }
1285          else
1286          {
1287            writer.println("objectClass={ \"" +
1288                 iterator.next() + "\",");
1289            added = true;
1290          }
1291    
1292          while (iterator.hasNext())
1293          {
1294            final String name = iterator.next();
1295            if (iterator.hasNext())
1296            {
1297              writer.println("                           \"" + name + "\",");
1298            }
1299            else
1300            {
1301              writer.print("                           \"" + name + "\" }");
1302            }
1303          }
1304        }
1305    
1306        if (inRDN)
1307        {
1308          if (added)
1309          {
1310            writer.println(",");
1311            writer.println("             inRDN=true,");
1312          }
1313          else
1314          {
1315            writer.println("inRDN=true,");
1316            added = true;
1317          }
1318          writer.print("             filterUsage=FilterUsage.ALWAYS_ALLOWED");
1319        }
1320        else
1321        {
1322          if (! terse)
1323          {
1324            if (added)
1325            {
1326              writer.println(",");
1327              writer.print("             " +
1328                   "filterUsage=FilterUsage.CONDITIONALLY_ALLOWED");
1329            }
1330            else
1331            {
1332              writer.print("filterUsage=FilterUsage.CONDITIONALLY_ALLOWED");
1333              added = true;
1334            }
1335          }
1336        }
1337    
1338        if (required)
1339        {
1340          if (added)
1341          {
1342            writer.println(",");
1343            writer.print("             requiredForEncode=true");
1344          }
1345          else
1346          {
1347            writer.print("requiredForEncode=true");
1348            added = true;
1349          }
1350        }
1351    
1352        if (d.isOperational())
1353        {
1354          if (added)
1355          {
1356            writer.println(",");
1357            writer.println("             inAdd=false,");
1358          }
1359          else
1360          {
1361            writer.println("inAdd=false,");
1362            added = true;
1363          }
1364    
1365          writer.print("             inModify=false");
1366        }
1367    
1368        if (lazy)
1369        {
1370          if (added)
1371          {
1372            writer.println(",");
1373            writer.print("             lazilyLoad=true");
1374          }
1375          else
1376          {
1377            writer.print("lazilyLoad=true");
1378            added = true;
1379          }
1380        }
1381    
1382        writer.println(")");
1383        if (d.isSingleValued())
1384        {
1385          writer.println("  private " + type + ' ' + fieldName + ';');
1386        }
1387        else
1388        {
1389          writer.println("  private " + type + "[] " + fieldName + ';');
1390        }
1391      }
1392    
1393    
1394    
1395      /**
1396       * Writes getter, setter, and filter creation methods for the specified
1397       * attribute.
1398       *
1399       * @param  writer     The writer to use to write the methods.
1400       * @param  d          The attribute type definition to be written.
1401       * @param  type       The name of the Java type to use for the attribute.
1402       * @param  addSetter  Indicates whether to write a setter method.
1403       */
1404      static void writeFieldMethods(final PrintWriter writer,
1405                                    final AttributeTypeDefinition d,
1406                                    final String type, final boolean addSetter)
1407      {
1408        writer.println();
1409        writer.println();
1410        writer.println();
1411    
1412        final String attrName  = d.getNameOrOID();
1413        final String fieldName = PersistUtils.toJavaIdentifier(attrName);
1414        final String capFieldName = capitalize(fieldName);
1415    
1416        if (d.isSingleValued())
1417        {
1418          if (type.equals("DN"))
1419          {
1420            writer.println("  /**");
1421            writer.println("   * Retrieves the first value for the field " +
1422                 "associated with the");
1423            writer.println("   * " + attrName + " attribute as a DN, if present.");
1424            writer.println("   *");
1425            writer.println("   * @return  The first value for the field " +
1426                 "associated with the");
1427            writer.println("   *          " + attrName + " attribute, or");
1428            writer.println("   *          {@code null} if the field does not " +
1429                 "have a value.");
1430            writer.println("   */");
1431            writer.println("  public DN get" + capFieldName + "DN()");
1432            writer.println("  {");
1433            writer.println("    return " + fieldName + ';');
1434            writer.println("  }");
1435    
1436            writer.println();
1437            writer.println();
1438            writer.println();
1439    
1440            writer.println("  /**");
1441            writer.println("   * Retrieves the object referenced by the DN held " +
1442                 "in the");
1443            writer.println("   * " + attrName + " attribute, if present.");
1444            writer.println("   *");
1445            writer.println("   * @param  <T>  The type of object to return.");
1446            writer.println("   *");
1447            writer.println("   * @param  connection  The connection to use to " +
1448                 "retrieve the entry.  It must");
1449            writer.println("   *                     not be {@code null}.");
1450            writer.println("   * @param  type        The type of object as which " +
1451                 "to decode the entry.  It");
1452            writer.println("   *                     must not be {@code null}, " +
1453                 "and the class must be marked");
1454            writer.println("   *                     with the {@code LDAPObject} " +
1455                 "annotation type.");
1456            writer.println("   *");
1457            writer.println("   * @return  The object decoded from the entry with " +
1458                 "the associated DN, or");
1459            writer.println("   *          {@code null} if the field does not " +
1460                 "have a value or the referenced");
1461            writer.println("   *          entry does not exist.");
1462            writer.println("   *");
1463            writer.println("   * @throws  LDAPException  If a problem occurs " +
1464                 "while attempting to retrieve");
1465            writer.println("   *                         the entry or decode it " +
1466                 "as an object of the");
1467            writer.println("   *                         specified type.");
1468            writer.println("   */");
1469            writer.println("  public <T> T get" + capFieldName + "Object(");
1470            writer.println("                    final LDAPInterface connection,");
1471            writer.println("                    final Class<T> type)");
1472            writer.println("         throws LDAPException");
1473            writer.println("  {");
1474            writer.println("    return PersistUtils.getEntryAsObject(" + fieldName +
1475                 ',');
1476            writer.println("         type, connection);");
1477            writer.println("  }");
1478    
1479            if (addSetter)
1480            {
1481              writer.println();
1482              writer.println();
1483              writer.println();
1484    
1485              writer.println("  /**");
1486              writer.println("   * Sets the value for the field associated with " +
1487                   "the");
1488              writer.println("   * " + attrName + " attribute.");
1489              writer.println("   *");
1490              writer.println("   * @param  v  The value for the field associated " +
1491                   "with the");
1492              writer.println("   *            " + attrName + " attribute.");
1493              writer.println("   */");
1494              writer.println("  public void set" + capFieldName + "(final DN v)");
1495              writer.println("  {");
1496              writer.println("    this." + fieldName + " = v;");
1497              writer.println("  }");
1498    
1499              writer.println();
1500              writer.println();
1501              writer.println();
1502    
1503              writer.println("  /**");
1504              writer.println("   * Sets the value for the field associated with " +
1505                   "the");
1506              writer.println("   * " + attrName + " attribute.");
1507              writer.println("   *");
1508              writer.println("   * @param  v  The string representation of the " +
1509                   "value for the field associated");
1510              writer.println("   *            with the " + attrName +
1511                   " attribute.");
1512              writer.println("   *");
1513              writer.println("   * @throws  LDAPException  If the provided " +
1514                   "string cannot be parsed as a DN.");
1515              writer.println("   */");
1516              writer.println("  public void set" + capFieldName +
1517                   "(final String v)");
1518              writer.println("         throws LDAPException");
1519              writer.println("  {");
1520              writer.println("    if (v == null)");
1521              writer.println("    {");
1522              writer.println("      this." + fieldName + " = null;");
1523              writer.println("    }");
1524              writer.println("    else");
1525              writer.println("    {");
1526              writer.println("      this." + fieldName + " = new DN(v);");
1527              writer.println("    }");
1528              writer.println("  }");
1529            }
1530          }
1531          else
1532          {
1533            writer.println("  /**");
1534            writer.println("   * Retrieves the value for the field associated " +
1535                 "with the");
1536            writer.println("   * " + attrName + " attribute, if present.");
1537            writer.println("   *");
1538            writer.println("   * @return  The value for the field associated " +
1539                 "with the");
1540            writer.println("   *          " + attrName + " attribute, or");
1541            writer.println("   *          {@code null} if the field does not " +
1542                 "have a value.");
1543            writer.println("   */");
1544            writer.println("  public " + type + " get" + capFieldName + "()");
1545            writer.println("  {");
1546            writer.println("    return " + fieldName + ';');
1547            writer.println("  }");
1548    
1549            if (addSetter)
1550            {
1551              writer.println();
1552              writer.println();
1553              writer.println();
1554    
1555              writer.println("  /**");
1556              writer.println("   * Sets the value for the field associated with " +
1557                   "the");
1558              writer.println("   * " + attrName + " attribute.");
1559              writer.println("   *");
1560              writer.println("   * @param  v  The value for the field associated " +
1561                   "with the");
1562              writer.println("   *            " + attrName + " attribute.");
1563              writer.println("   */");
1564              writer.println("  public void set" + capFieldName + "(final " + type +
1565                   " v)");
1566              writer.println("  {");
1567              writer.println("    this." + fieldName + " = v;");
1568              writer.println("  }");
1569            }
1570          }
1571        }
1572        else
1573        {
1574          if (type.equals("DN"))
1575          {
1576            writer.println("  /**");
1577            writer.println("   * Retrieves the first value for the field " +
1578                 "associated with the");
1579            writer.println("   * " + attrName + " attribute as a DN, if present.");
1580            writer.println("   *");
1581            writer.println("   * @return  The first value for the field " +
1582                 "associated with the");
1583            writer.println("   *          " + attrName + " attribute, or");
1584            writer.println("   *          {@code null} if that attribute was not " +
1585                 "present in the entry or");
1586            writer.println("   *          does not have any values.");
1587            writer.println("   */");
1588            writer.println("  public DN getFirst" + capFieldName + "DN()");
1589            writer.println("  {");
1590            writer.println("    if ((" + fieldName + " == null) ||");
1591            writer.println("        (" + fieldName + ".length == 0))");
1592            writer.println("    {");
1593            writer.println("      return null;");
1594            writer.println("    }");
1595            writer.println("    else");
1596            writer.println("    {");
1597            writer.println("      return " + fieldName + "[0];");
1598            writer.println("    }");
1599            writer.println("  }");
1600    
1601            writer.println();
1602            writer.println();
1603            writer.println();
1604    
1605            writer.println("  /**");
1606            writer.println("   * Retrieves the values for the field associated " +
1607                 "with the");
1608            writer.println("   * " + attrName + " attribute as DNs, if present.");
1609            writer.println("   *");
1610            writer.println("   * @return  The values for the field associated " +
1611                 "with the");
1612            writer.println("   *          " + attrName + " attribute, or");
1613            writer.println("   *          {@code null} if that attribute was not " +
1614                 "present in the entry.");
1615            writer.println("   */");
1616            writer.println("  public DN[] get" + capFieldName + "DNs()");
1617            writer.println("  {");
1618            writer.println("    return " + fieldName + ';');
1619            writer.println("  }");
1620    
1621            writer.println();
1622            writer.println();
1623            writer.println();
1624    
1625            writer.println("  /**");
1626            writer.println("   * Retrieves the values for the field associated " +
1627                 "with the");
1628            writer.println("   * " + attrName + " attribute as objects of the " +
1629                 "specified type,");
1630            writer.println("   * if present.");
1631            writer.println("   *");
1632            writer.println("   * @param  <T>  The type of object to return.");
1633            writer.println("   *");
1634            writer.println("   * @param  connection  The connection to use to " +
1635                 "retrieve the entries.  It");
1636            writer.println("   *                     must not be {@code null}.");
1637            writer.println("   * @param  type        The type of object as which " +
1638                 "the entries should be");
1639            writer.println("   *                     decoded.  It must not be " +
1640                 "{@code null}, and the class");
1641            writer.println("   *                     must be marked with the " +
1642                 "{@code LDAPObject} annotation");
1643            writer.println("   *                     type.");
1644            writer.println("   *");
1645            writer.println("   * @return  A {@code PersistedObjects} object that " +
1646                 "may be used to iterate");
1647            writer.println("   *          across the resulting objects.");
1648            writer.println("   *");
1649            writer.println("   * @throws  LDAPException  If the requested type " +
1650                 "cannot be used with the LDAP");
1651            writer.println("   *                         SDK persistence " +
1652                 "framework.");
1653            writer.println("   */");
1654            writer.println("  public <T> PersistedObjects<T> get" + capFieldName +
1655                 "Objects(");
1656            writer.println("                                      final " +
1657                 "LDAPInterface connection,");
1658            writer.println("                                      final Class<T> " +
1659                 "type)");
1660            writer.println("         throws LDAPException");
1661            writer.println("  {");
1662            writer.println("    return PersistUtils.getEntriesAsObjects(" +
1663                 fieldName + ',');
1664            writer.println("         type, connection);");
1665            writer.println("  }");
1666    
1667            if (addSetter)
1668            {
1669              writer.println();
1670              writer.println();
1671              writer.println();
1672    
1673              writer.println("  /**");
1674              writer.println("   * Sets the values for the field associated with " +
1675                   "the");
1676              writer.println("   * " + attrName + " attribute.");
1677              writer.println("   *");
1678              writer.println("   * @param  v  The values for the field " +
1679                   "associated with the");
1680              writer.println("   *            " + attrName + " attribute.");
1681              writer.println("   */");
1682              writer.println("  public void set" + capFieldName +
1683                   "(final DN... v)");
1684              writer.println("  {");
1685              writer.println("    this." + fieldName + " = v;");
1686              writer.println("  }");
1687    
1688              writer.println();
1689              writer.println();
1690              writer.println();
1691    
1692              writer.println("  /**");
1693              writer.println("   * Sets the values for the field associated with " +
1694                   "the");
1695              writer.println("   * " + attrName + " attribute.");
1696              writer.println("   *");
1697              writer.println("   * @param  v  The string representations of the " +
1698                   "values for the field");
1699              writer.println("   *            associated with the " + attrName +
1700                   " attribute.");
1701              writer.println("   *");
1702              writer.println("   * @throws  LDAPException  If any of the " +
1703                   "provided strings cannot be parsed as");
1704              writer.println("   *                         a DN.");
1705              writer.println("   */");
1706              writer.println("  public void set" + capFieldName +
1707                   "(final String... v)");
1708              writer.println("         throws LDAPException");
1709              writer.println("  {");
1710              writer.println("    if (v == null)");
1711              writer.println("    {");
1712              writer.println("      this." + fieldName + " = null;");
1713              writer.println("    }");
1714              writer.println("    else");
1715              writer.println("    {");
1716              writer.println("      this." + fieldName + " = new DN[v.length];");
1717              writer.println("      for (int i=0; i < v.length; i++)");
1718              writer.println("      {");
1719              writer.println("        this." + fieldName + "[i] = new DN(v[i]);");
1720              writer.println("      }");
1721              writer.println("    }");
1722              writer.println("  }");
1723            }
1724          }
1725          else
1726          {
1727            writer.println("  /**");
1728            writer.println("   * Retrieves the first value for the field " +
1729                 "associated with the");
1730            writer.println("   * " + attrName + " attribute, if present.");
1731            writer.println("   *");
1732            writer.println("   * @return  The first value for the field " +
1733                 "associated with the");
1734            writer.println("   *          " + attrName + " attribute, or");
1735            writer.println("   *          {@code null} if that attribute was not " +
1736                 "present in the entry or");
1737            writer.println("   *          does not have any values.");
1738            writer.println("   */");
1739            writer.println("  public " + type + " getFirst" + capFieldName + "()");
1740            writer.println("  {");
1741            writer.println("    if ((" + fieldName + " == null) ||");
1742            writer.println("        (" + fieldName + ".length == 0))");
1743            writer.println("    {");
1744            writer.println("      return null;");
1745            writer.println("    }");
1746            writer.println("    else");
1747            writer.println("    {");
1748            writer.println("      return " + fieldName + "[0];");
1749            writer.println("    }");
1750            writer.println("  }");
1751    
1752            writer.println();
1753            writer.println();
1754            writer.println();
1755    
1756            writer.println("  /**");
1757            writer.println("   * Retrieves the values for the field associated " +
1758                 "with the");
1759            writer.println("   * " + attrName + " attribute, if present.");
1760            writer.println("   *");
1761            writer.println("   * @return  The values for the field associated " +
1762                 "with the");
1763            writer.println("   *          " + attrName + " attribute, or");
1764            writer.println("   *          {@code null} if that attribute was not " +
1765                 "present in the entry.");
1766            writer.println("   */");
1767            writer.println("  public " + type + "[] get" + capFieldName + "()");
1768            writer.println("  {");
1769            writer.println("    return " + fieldName + ';');
1770            writer.println("  }");
1771    
1772            if (addSetter)
1773            {
1774              writer.println();
1775              writer.println();
1776              writer.println();
1777    
1778              writer.println("  /**");
1779              writer.println("   * Sets the values for the field associated with " +
1780                   "the");
1781              writer.println("   * " + attrName + " attribute.");
1782              writer.println("   *");
1783              writer.println("   * @param  v  The values for the field " +
1784                   "associated with the");
1785              writer.println("   *            " + attrName + " attribute.");
1786              writer.println("   */");
1787              writer.println("  public void set" + capFieldName + "(final " + type +
1788                   "... v)");
1789              writer.println("  {");
1790              writer.println("    this." + fieldName + " = v;");
1791              writer.println("  }");
1792            }
1793          }
1794        }
1795    
1796    
1797        writer.println();
1798        writer.println();
1799        writer.println();
1800    
1801        writer.println("  /**");
1802        writer.println("   * Generates a filter that may be used to search for " +
1803             "objects of this type");
1804        writer.println("   * using the " + attrName + " attribute.");
1805        writer.println("   * The resulting filter may be combined with other " +
1806             "filter elements to create a");
1807        writer.println("   * more complex filter.");
1808        writer.println("   *");
1809        writer.println("   * @param  filterType  The type of filter to generate.");
1810        writer.println("   * @param  value       The value to use to use for the " +
1811             "filter.  It may be");
1812        writer.println("   *                     {@code null} only for a filter " +
1813             "type of");
1814        writer.println("   *                     {@code PRESENCE}.");
1815        writer.println("   *");
1816        writer.println("   * @return  The generated search filter.");
1817        writer.println("   *");
1818        writer.println("   * @throws  LDAPPersistException  If a problem is " +
1819             "encountered while attempting");
1820        writer.println("   *                                to generate the " +
1821             "filter.");
1822        writer.println("   */");
1823        writer.println("  public static Filter generate" + capFieldName +
1824             "Filter(");
1825        writer.println("                            final PersistFilterType " +
1826             "filterType,");
1827        writer.println("                            final " + type + " value)");
1828        writer.println("         throws LDAPPersistException");
1829        writer.println("  {");
1830        writer.println("    final byte[] valueBytes;");
1831        writer.println("    if (filterType == PersistFilterType.PRESENCE)");
1832        writer.println("    {");
1833        writer.println("      valueBytes = null;");
1834        writer.println("    }");
1835        writer.println("    else");
1836        writer.println("    {");
1837        writer.println("      if (value == null)");
1838        writer.println("      {");
1839        writer.println("        throw new LDAPPersistException(\"Unable to " +
1840             "generate a filter of type \" +");
1841        writer.println("             filterType.name() + \" with a null value " +
1842             "for attribute \" +");
1843        writer.println("             \"" + attrName + "\");");
1844        writer.println("      }");
1845        writer.println();
1846        writer.println("      final LDAPObjectHandler<?> objectHandler =");
1847        writer.println("           getPersister().getObjectHandler();");
1848        writer.println("      final FieldInfo fieldInfo = " +
1849             "objectHandler.getFields().get(");
1850        writer.println("           \"" + toLowerCase(attrName) + "\");");
1851        writer.println();
1852        writer.println("      final DefaultObjectEncoder objectEncoder = new " +
1853             "DefaultObjectEncoder();");
1854        writer.println("      valueBytes = " +
1855             "objectEncoder.encodeFieldValue(fieldInfo.getField(),");
1856    
1857        if (d.isSingleValued())
1858        {
1859          writer.println("           value,");
1860        }
1861        else
1862        {
1863          writer.println("           new " + type + "[] { value },");
1864        }
1865    
1866        writer.println("           \"" + attrName + "\").getValueByteArray();");
1867        writer.println("    }");
1868        writer.println();
1869        writer.println("    switch (filterType)");
1870        writer.println("    {");
1871        writer.println("      case PRESENCE:");
1872        writer.println("        return Filter.createPresenceFilter(");
1873        writer.println("             \"" + attrName + "\");");
1874        writer.println("      case EQUALITY:");
1875        writer.println("        return Filter.createEqualityFilter(");
1876        writer.println("             \"" + attrName + "\",");
1877        writer.println("             valueBytes);");
1878        writer.println("      case STARTS_WITH:");
1879        writer.println("        return Filter.createSubstringFilter(");
1880        writer.println("             \"" + attrName + "\",");
1881        writer.println("             valueBytes, null, null);");
1882        writer.println("      case ENDS_WITH:");
1883        writer.println("        return Filter.createSubstringFilter(");
1884        writer.println("             \"" + attrName + "\",");
1885        writer.println("             null, null, valueBytes);");
1886        writer.println("      case CONTAINS:");
1887        writer.println("        return Filter.createSubstringFilter(");
1888        writer.println("             \"" + attrName + "\",");
1889        writer.println("             null, new byte[][] { valueBytes }, null);");
1890        writer.println("      case GREATER_OR_EQUAL:");
1891        writer.println("        return Filter.createGreaterOrEqualFilter(");
1892        writer.println("             \"" + attrName + "\",");
1893        writer.println("             valueBytes);");
1894        writer.println("      case LESS_OR_EQUAL:");
1895        writer.println("        return Filter.createLessOrEqualFilter(");
1896        writer.println("             \"" + attrName + "\",");
1897        writer.println("             valueBytes);");
1898        writer.println("      case APPROXIMATELY_EQUAL_TO:");
1899        writer.println("        return Filter.createApproximateMatchFilter(");
1900        writer.println("             \"" + attrName + "\",");
1901        writer.println("             valueBytes);");
1902        writer.println("      default:");
1903        writer.println("        // This should never happen.");
1904        writer.println("        throw new LDAPPersistException(\"Unrecognized " +
1905             "filter type \" +");
1906        writer.println("             filterType.name());");
1907        writer.println("    }");
1908        writer.println("  }");
1909      }
1910    
1911    
1912    
1913      /**
1914       * Writes a {@code toString} method for the generated class.
1915       *
1916       * @param  writer            The writer to use to write the methods.
1917       * @param  className         The base name (without package information) for
1918       *                           the generated class.
1919       * @param  requiredAttrs     The set of required attributes for the generated
1920       *                           class.
1921       * @param  optionalAttrs     The set of optional attributes for the generated
1922       *                           class.
1923       * @param  operationalAttrs  The set of operational attributes for the
1924       *                           generated class.
1925       */
1926      static void writeToString(final PrintWriter writer, 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      String getJavaType(final Schema schema, final AttributeTypeDefinition d)
2042      {
2043        if (! d.isSingleValued())
2044        {
2045          needArrays = true;
2046        }
2047    
2048        final String syntaxOID = d.getSyntaxOID(schema);
2049        if (syntaxOID == null)
2050        {
2051          return "String";
2052        }
2053    
2054        final String oid;
2055        final int bracePos = syntaxOID.indexOf('{');
2056        if (bracePos > 0)
2057        {
2058          oid = syntaxOID.substring(0, bracePos);
2059        }
2060        else
2061        {
2062          oid = syntaxOID;
2063        }
2064    
2065        if (oid.equals("1.3.6.1.4.1.1466.115.121.1.7"))
2066        {
2067          // Boolean
2068          return "Boolean";
2069        }
2070        else if (oid.equals("1.3.6.1.4.1.4203.1.1.2") ||
2071                 oid.equals("1.3.6.1.4.1.1466.115.121.1.5") ||
2072                 oid.equals("1.3.6.1.4.1.1466.115.121.1.8") ||
2073                 oid.equals("1.3.6.1.4.1.1466.115.121.1.9") ||
2074                 oid.equals("1.3.6.1.4.1.1466.115.121.1.10") ||
2075                 oid.equals("1.3.6.1.4.1.1466.115.121.1.28") ||
2076                 oid.equals("1.3.6.1.4.1.1466.115.121.1.40"))
2077        {
2078          // auth password
2079          // binary
2080          // certificate
2081          // certificate list
2082          // certificate pair
2083          // JPEG
2084          // octet string
2085          return "byte[]";
2086        }
2087        else if (oid.equals("1.3.6.1.4.1.1466.115.121.1.24"))
2088        {
2089          // generalized time.
2090          needDate = true;
2091          return "Date";
2092        }
2093        else if (oid.equals("1.3.6.1.4.1.1466.115.121.1.27"))
2094        {
2095          // integer
2096          return "Long";
2097        }
2098        else if (oid.equals("1.3.6.1.4.1.1466.115.121.1.12") ||
2099                 oid.equals("1.3.6.1.4.1.1466.115.121.1.34"))
2100        {
2101          // DN
2102          // name and optional UID
2103          needDN = true;
2104          if (! d.isSingleValued())
2105          {
2106            needPersistedObjects = true;
2107          }
2108          return "DN";
2109        }
2110        else
2111        {
2112          return "String";
2113        }
2114      }
2115    
2116    
2117    
2118      /**
2119       * {@inheritDoc}
2120       */
2121      @Override()
2122      public LinkedHashMap<String[],String> getExampleUsages()
2123      {
2124        final LinkedHashMap<String[],String> examples =
2125             new LinkedHashMap<String[],String>(1);
2126    
2127        final String[] args =
2128        {
2129          "--hostname", "server.example.com",
2130          "--port", "389",
2131          "--bindDN", "uid=admin,dc=example,dc=com",
2132          "--bindPassword", "password",
2133          "--outputDirectory", "src/com/example",
2134          "--structuralClass", "myStructuralClass",
2135          "--auxiliaryClass", "auxClass1",
2136          "--auxiliaryClass", "auxClass2",
2137          "--rdnAttribute", "cn",
2138          "--defaultParentDN", "dc=example,dc=com",
2139          "--packageName", "com.example",
2140          "--className", "MyObject"
2141        };
2142        examples.put(args, INFO_GEN_SOURCE_EXAMPLE_1.get());
2143    
2144        return examples;
2145      }
2146    }