001/* 002 * Copyright 2009-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2014 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 */ 021package com.unboundid.ldap.sdk.persist; 022 023 024 025import java.io.File; 026import java.io.FileWriter; 027import java.io.OutputStream; 028import java.io.PrintWriter; 029import java.io.Serializable; 030import java.util.Arrays; 031import java.util.Collection; 032import java.util.Date; 033import java.util.Iterator; 034import java.util.LinkedHashMap; 035import java.util.TreeMap; 036import java.util.TreeSet; 037 038import com.unboundid.ldap.sdk.DN; 039import com.unboundid.ldap.sdk.Entry; 040import com.unboundid.ldap.sdk.Filter; 041import com.unboundid.ldap.sdk.LDAPConnection; 042import com.unboundid.ldap.sdk.LDAPException; 043import com.unboundid.ldap.sdk.LDAPInterface; 044import com.unboundid.ldap.sdk.ReadOnlyEntry; 045import com.unboundid.ldap.sdk.ResultCode; 046import com.unboundid.ldap.sdk.Version; 047import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 048import com.unboundid.ldap.sdk.schema.ObjectClassDefinition; 049import com.unboundid.ldap.sdk.schema.ObjectClassType; 050import com.unboundid.ldap.sdk.schema.Schema; 051import com.unboundid.util.LDAPCommandLineTool; 052import com.unboundid.util.Mutable; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.args.ArgumentException; 056import com.unboundid.util.args.ArgumentParser; 057import com.unboundid.util.args.BooleanArgument; 058import com.unboundid.util.args.DNArgument; 059import com.unboundid.util.args.FileArgument; 060import com.unboundid.util.args.StringArgument; 061 062import static com.unboundid.ldap.sdk.persist.PersistMessages.*; 063import static com.unboundid.util.Debug.*; 064import 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) 075public final class GenerateSourceFromSchema 076 extends LDAPCommandLineTool 077 implements Serializable 078{ 079 /** 080 * The serial version UID for this serializable class. 081 */ 082 private static final long serialVersionUID = 3488976364950590266L; 083 084 085 086 /** 087 * A pre-allocated empty tree set. 088 */ 089 private static final TreeSet<String> EMPTY_TREE_SET = new TreeSet<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}