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