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 }