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