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