001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 028 package org.opends.server.admin; 029 030 031 032 import java.util.Collections; 033 import java.util.LinkedList; 034 import java.util.List; 035 import java.util.regex.Matcher; 036 import java.util.regex.Pattern; 037 038 import org.opends.server.admin.std.client.RootCfgClient; 039 import org.opends.server.admin.std.meta.RootCfgDefn; 040 import org.opends.server.admin.std.server.RootCfg; 041 import org.opends.server.core.DirectoryServer; 042 import org.opends.server.types.AttributeType; 043 import org.opends.server.types.AttributeValue; 044 import org.opends.server.types.DN; 045 import org.opends.server.types.DirectoryException; 046 import org.opends.server.types.RDN; 047 048 049 050 /** 051 * A path which can be used to determine the location of a managed 052 * object instance. 053 * <p> 054 * A path is made up of zero or more elements each of which represents 055 * a managed object. Managed objects are arranged hierarchically with 056 * the root configuration being the top-most managed object. Elements 057 * are ordered such that the root configuration managed object is the 058 * first element and subsequent elements representing managed objects 059 * further down the hierarchy. 060 * <p> 061 * A path can be encoded into a string representation using the 062 * {@link #toString()} and {@link #toString(StringBuilder)} methods. 063 * Conversely, this string representation can be parsed using the 064 * {@link #valueOf(String)} method. 065 * <p> 066 * The string representation of a managed object path is similar in 067 * principle to a UNIX file-system path and is defined as follows: 068 * <ul> 069 * <li>the root element is represented by the string <code>/</code> 070 * <li>subordinate elements are arranged in big-endian order 071 * separated by a forward slash <code>/</code> character 072 * <li>an element representing a managed object associated with a 073 * one-to-one (singleton) or one-to-zero-or-one (optional) relation 074 * has the form <code>relation=</code><i>relation</i> 075 * <code>[+type=</code><i>definition</i><code>]</code>, where 076 * <i>relation</i> is the name of the relation and <i>definition</i> 077 * is the name of the referenced managed object's definition if 078 * required (usually this is implied by the relation itself) 079 * <li>an element representing a managed object associated with a 080 * one-to-many (instantiable) relation has the form 081 * <code>relation=</code><i>relation</i><code>[+type=</code> 082 * <i>definition</i><code>]</code><code>+name=</code><i>name</i>, 083 * where <i>relation</i> is the name of the relation and 084 * <i>definition</i> is the name of the referenced managed object's 085 * definition if required (usually this is implied by the relation 086 * itself), and <i>name</i> is the name of the managed object 087 * instance. 088 * </ul> 089 * The following path string representation identifies a connection 090 * handler instance (note that the <code>type</code> is not 091 * specified indicating that the path identifies a connection handler 092 * called <i>my handler</i> which can be any type of connection 093 * handler): 094 * 095 * <pre> 096 * /relation=connection-handler+name=my handler 097 * </pre> 098 * 099 * If the identified connection handler must be an LDAP connection 100 * handler then the above path should include the <code>type</code>: 101 * 102 * <pre> 103 * /relation=connection-handler+type=ldap-connection-handler+name=my handler 104 * </pre> 105 * 106 * The final example identifies the global configuration: 107 * 108 * <pre> 109 * /relation=global-configuration 110 * </pre> 111 * 112 * @param <C> 113 * The type of client managed object configuration that this 114 * path references. 115 * @param <S> 116 * The type of server managed object configuration that this 117 * path references. 118 */ 119 public final class ManagedObjectPath<C extends ConfigurationClient, 120 S extends Configuration> { 121 122 /** 123 * A serialize which is used to generate the toDN representation. 124 */ 125 private static final class DNSerializer implements 126 ManagedObjectPathSerializer { 127 128 // The current DN. 129 private DN dn; 130 131 // The LDAP profile. 132 private final LDAPProfile profile; 133 134 135 136 // Create a new DN builder. 137 private DNSerializer() { 138 this.dn = DN.nullDN(); 139 this.profile = LDAPProfile.getInstance(); 140 } 141 142 143 144 /** 145 * {@inheritDoc} 146 */ 147 public <C extends ConfigurationClient, S extends Configuration> 148 void appendManagedObjectPathElement( 149 InstantiableRelationDefinition<? super C, ? super S> r, 150 AbstractManagedObjectDefinition<C, S> d, String name) { 151 // Add the RDN sequence representing the relation. 152 appendManagedObjectPathElement((RelationDefinition<?, ?>) r); 153 154 // Now add the single RDN representing the named instance. 155 String type = profile.getInstantiableRelationChildRDNType(r); 156 AttributeType atype = DirectoryServer.getAttributeType( 157 type.toLowerCase(), true); 158 AttributeValue avalue = new AttributeValue(atype, name); 159 dn = dn.concat(RDN.create(atype, avalue)); 160 } 161 162 163 164 /** 165 * {@inheritDoc} 166 */ 167 public <C extends ConfigurationClient, S extends Configuration> 168 void appendManagedObjectPathElement( 169 OptionalRelationDefinition<? super C, ? super S> r, 170 AbstractManagedObjectDefinition<C, S> d) { 171 // Add the RDN sequence representing the relation. 172 appendManagedObjectPathElement((RelationDefinition<?, ?>) r); 173 } 174 175 176 177 /** 178 * {@inheritDoc} 179 */ 180 public <C extends ConfigurationClient, S extends Configuration> 181 void appendManagedObjectPathElement( 182 SingletonRelationDefinition<? super C, ? super S> r, 183 AbstractManagedObjectDefinition<C, S> d) { 184 // Add the RDN sequence representing the relation. 185 appendManagedObjectPathElement((RelationDefinition<?, ?>) r); 186 } 187 188 189 190 // Appends the RDN sequence representing the provided relation. 191 private void appendManagedObjectPathElement(RelationDefinition<?, ?> r) { 192 // Add the RDN sequence representing the relation. 193 try { 194 DN localName = DN.decode(profile.getRelationRDNSequence(r)); 195 dn = dn.concat(localName); 196 } catch (DirectoryException e) { 197 throw new RuntimeException(e); 198 } 199 } 200 201 202 203 // Gets the serialized DN value. 204 private DN toDN() { 205 return dn; 206 } 207 } 208 209 210 211 /** 212 * Abstract path element. 213 */ 214 private static abstract class Element<C extends ConfigurationClient, 215 S extends Configuration> { 216 217 // The type of managed object referenced by this element. 218 private final AbstractManagedObjectDefinition<C, S> definition; 219 220 221 222 /** 223 * Protected constructor. 224 * 225 * @param definition 226 * The type of managed object referenced by this element. 227 */ 228 protected Element(AbstractManagedObjectDefinition<C, S> definition) { 229 this.definition = definition; 230 } 231 232 233 234 /** 235 * Get the managed object definition associated with this element. 236 * 237 * @return Returns the managed object definition associated with 238 * this element. 239 */ 240 public final AbstractManagedObjectDefinition<C, S> 241 getManagedObjectDefinition() { 242 return definition; 243 } 244 245 246 247 /** 248 * Get the name associated with this element if applicable. 249 * 250 * @return Returns the name associated with this element if 251 * applicable. 252 */ 253 public String getName() { 254 return null; 255 } 256 257 258 259 /** 260 * Get the relation definition associated with this element. 261 * 262 * @return Returns the relation definition associated with this 263 * element. 264 */ 265 public abstract RelationDefinition<? super C, ? super S> 266 getRelationDefinition(); 267 268 269 270 /** 271 * Serialize this path element using the provided serialization 272 * strategy. 273 * 274 * @param serializer 275 * The managed object path serialization strategy. 276 */ 277 public abstract void serialize(ManagedObjectPathSerializer serializer); 278 } 279 280 281 282 /** 283 * A path element representing an instantiable managed object. 284 */ 285 private static final class InstantiableElement 286 <C extends ConfigurationClient, S extends Configuration> 287 extends Element<C, S> { 288 289 // Factory method. 290 private static final <C extends ConfigurationClient, 291 S extends Configuration> 292 InstantiableElement<C, S> create( 293 InstantiableRelationDefinition<? super C, ? super S> r, 294 AbstractManagedObjectDefinition<C, S> d, String name) { 295 return new InstantiableElement<C, S>(r, d, name); 296 } 297 298 // The name of the managed object. 299 private final String name; 300 301 // The instantiable relation. 302 private final InstantiableRelationDefinition<? super C, ? super S> r; 303 304 305 306 // Private constructor. 307 private InstantiableElement( 308 InstantiableRelationDefinition<? super C, ? super S> r, 309 AbstractManagedObjectDefinition<C, S> d, String name) { 310 super(d); 311 this.r = r; 312 this.name = name; 313 } 314 315 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override 321 public String getName() { 322 return name; 323 } 324 325 326 327 /** 328 * {@inheritDoc} 329 */ 330 @Override 331 public InstantiableRelationDefinition<? super C, ? super S> 332 getRelationDefinition() { 333 return r; 334 } 335 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 @Override 342 public void serialize(ManagedObjectPathSerializer serializer) { 343 serializer.appendManagedObjectPathElement(r, 344 getManagedObjectDefinition(), name); 345 } 346 } 347 348 349 350 /** 351 * A path element representing an optional managed object. 352 */ 353 private static final class OptionalElement 354 <C extends ConfigurationClient, S extends Configuration> 355 extends Element<C, S> { 356 357 // Factory method. 358 private static final <C extends ConfigurationClient, 359 S extends Configuration> OptionalElement<C, S> create( 360 OptionalRelationDefinition<? super C, ? super S> r, 361 AbstractManagedObjectDefinition<C, S> d) { 362 return new OptionalElement<C, S>(r, d); 363 } 364 365 // The optional relation. 366 private final OptionalRelationDefinition<? super C, ? super S> r; 367 368 369 370 // Private constructor. 371 private OptionalElement(OptionalRelationDefinition<? super C, ? super S> r, 372 AbstractManagedObjectDefinition<C, S> d) { 373 super(d); 374 this.r = r; 375 } 376 377 378 379 /** 380 * {@inheritDoc} 381 */ 382 @Override 383 public OptionalRelationDefinition<? super C, ? super S> 384 getRelationDefinition() { 385 return r; 386 } 387 388 389 390 /** 391 * {@inheritDoc} 392 */ 393 @Override 394 public void serialize(ManagedObjectPathSerializer serializer) { 395 serializer 396 .appendManagedObjectPathElement(r, getManagedObjectDefinition()); 397 } 398 } 399 400 401 402 /** 403 * A path element representing a singleton managed object. 404 */ 405 private static final class SingletonElement 406 <C extends ConfigurationClient, S extends Configuration> 407 extends Element<C, S> { 408 409 // Factory method. 410 private static final <C extends ConfigurationClient, 411 S extends Configuration> SingletonElement<C, S> create( 412 SingletonRelationDefinition<? super C, ? super S> r, 413 AbstractManagedObjectDefinition<C, S> d) { 414 return new SingletonElement<C, S>(r, d); 415 } 416 417 // The singleton relation. 418 private final SingletonRelationDefinition<? super C, ? super S> r; 419 420 421 422 // Private constructor. 423 private SingletonElement( 424 SingletonRelationDefinition<? super C, ? super S> r, 425 AbstractManagedObjectDefinition<C, S> d) { 426 super(d); 427 this.r = r; 428 } 429 430 431 432 /** 433 * {@inheritDoc} 434 */ 435 @Override 436 public SingletonRelationDefinition<? super C, ? super S> 437 getRelationDefinition() { 438 return r; 439 } 440 441 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override 447 public void serialize(ManagedObjectPathSerializer serializer) { 448 serializer 449 .appendManagedObjectPathElement(r, getManagedObjectDefinition()); 450 } 451 } 452 453 454 455 /** 456 * A serialize which is used to generate the toString 457 * representation. 458 */ 459 private static final class StringSerializer implements 460 ManagedObjectPathSerializer { 461 462 // Serialize to this string builder. 463 private final StringBuilder builder; 464 465 466 467 // Private constructor. 468 private StringSerializer(StringBuilder builder) { 469 this.builder = builder; 470 } 471 472 473 474 /** 475 * {@inheritDoc} 476 */ 477 public <M extends ConfigurationClient, N extends Configuration> 478 void appendManagedObjectPathElement( 479 InstantiableRelationDefinition<? super M, ? super N> r, 480 AbstractManagedObjectDefinition<M, N> d, String name) { 481 serializeElement(r, d); 482 483 // Be careful to escape any forward slashes in the name. 484 builder.append("+name="); 485 builder.append(name.replace("/", "//")); 486 } 487 488 489 490 /** 491 * {@inheritDoc} 492 */ 493 public <M extends ConfigurationClient, N extends Configuration> 494 void appendManagedObjectPathElement( 495 OptionalRelationDefinition<? super M, ? super N> r, 496 AbstractManagedObjectDefinition<M, N> d) { 497 serializeElement(r, d); 498 } 499 500 501 502 /** 503 * {@inheritDoc} 504 */ 505 public <M extends ConfigurationClient, N extends Configuration> 506 void appendManagedObjectPathElement( 507 SingletonRelationDefinition<? super M, ? super N> r, 508 AbstractManagedObjectDefinition<M, N> d) { 509 serializeElement(r, d); 510 } 511 512 513 514 // Common element serialization. 515 private <M, N> void serializeElement(RelationDefinition<?, ?> r, 516 AbstractManagedObjectDefinition<?, ?> d) { 517 // Always specify the relation name. 518 builder.append("/relation="); 519 builder.append(r.getName()); 520 521 // Only specify the type if it is a sub-type of the relation's 522 // type. 523 if (r.getChildDefinition() != d) { 524 builder.append("+type="); 525 builder.append(d.getName()); 526 } 527 } 528 } 529 530 // Single instance of a root path. 531 private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH = 532 new ManagedObjectPath<RootCfgClient, RootCfg>( 533 new LinkedList<Element<?, ?>>(), null, RootCfgDefn.getInstance()); 534 535 // A regular expression used to parse path elements. 536 private static final Pattern PE_REGEXP = Pattern 537 .compile("^\\s*relation=\\s*([^+]+)\\s*" 538 + "(\\+\\s*type=\\s*([^+]+)\\s*)?" 539 + "(\\+\\s*name=\\s*([^+]+)\\s*)?$"); 540 541 542 543 /** 544 * Creates a new managed object path representing the configuration 545 * root. 546 * 547 * @return Returns a new managed object path representing the 548 * configuration root. 549 */ 550 public static ManagedObjectPath<RootCfgClient, RootCfg> emptyPath() { 551 return EMPTY_PATH; 552 } 553 554 555 556 /** 557 * Returns a managed object path holding the value of the specified 558 * string. 559 * 560 * @param s 561 * The string to be parsed. 562 * @return Returns a managed object path holding the value of the 563 * specified string. 564 * @throws IllegalArgumentException 565 * If the string could not be parsed. 566 */ 567 public static ManagedObjectPath<?, ?> valueOf(String s) 568 throws IllegalArgumentException { 569 String ns = s.trim(); 570 571 // Check for root special case. 572 if (ns.equals("/")) { 573 return EMPTY_PATH; 574 } 575 576 // Parse the elements. 577 LinkedList<Element<?, ?>> elements = new LinkedList<Element<?, ?>>(); 578 Element<?, ?> lastElement = null; 579 AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn 580 .getInstance(); 581 582 if (!ns.startsWith("/")) { 583 throw new IllegalArgumentException("Invalid path \"" + ns 584 + "\": must begin with a \"/\""); 585 } 586 587 int start = 1; 588 while (true) { 589 // Get the next path element. 590 int end; 591 for (end = start; end < ns.length(); end++) { 592 char c = ns.charAt(end); 593 if (c == '/') { 594 if (end == (ns.length() - 1)) { 595 throw new IllegalArgumentException("Invalid path \"" + ns 596 + "\": must not end with a trailing \"/\""); 597 } 598 599 if (ns.charAt(end + 1) == '/') { 600 // Found an escaped forward slash. 601 end++; 602 } else { 603 // Found the end of this path element. 604 break; 605 } 606 } 607 } 608 609 // Get the next element. 610 String es = ns.substring(start, end); 611 612 Matcher m = PE_REGEXP.matcher(es); 613 if (!m.matches()) { 614 throw new IllegalArgumentException("Invalid path element \"" + es 615 + "\" in path \"" + ns + "\""); 616 } 617 618 // Mandatory. 619 String relation = m.group(1); 620 621 // Optional. 622 String type = m.group(3); 623 624 // Mandatory if relation is instantiable. 625 String name = m.group(5); 626 627 // Get the relation definition. 628 RelationDefinition<?, ?> r; 629 try { 630 r = definition.getRelationDefinition(relation); 631 } catch (IllegalArgumentException e) { 632 throw new IllegalArgumentException("Invalid path element \"" + es 633 + "\" in path \"" + ns + "\": unknown relation \"" + relation 634 + "\""); 635 } 636 637 // Append the next element. 638 lastElement = createElement(r, ns, es, type, name); 639 elements.add(lastElement); 640 definition = lastElement.getManagedObjectDefinition(); 641 642 // Update start to point to the beginning of the next element. 643 if (end < ns.length()) { 644 // Skip to the beginning of the next element 645 start = end + 1; 646 } else { 647 // We reached the end of the string. 648 break; 649 } 650 } 651 652 // Construct the new path. 653 return create(elements, lastElement); 654 } 655 656 657 658 // Factory method required in order to allow generic wild-card 659 // construction of new paths. 660 private static <C extends ConfigurationClient, S extends Configuration> 661 ManagedObjectPath<C, S> create( 662 LinkedList<Element<?, ?>> elements, Element<C, S> lastElement) { 663 return new ManagedObjectPath<C, S>(elements, lastElement 664 .getRelationDefinition(), lastElement.getManagedObjectDefinition()); 665 } 666 667 668 669 // Decode an element. 670 private static <C extends ConfigurationClient, S extends Configuration> 671 Element<? extends C, ? extends S> createElement( 672 RelationDefinition<C, S> r, String path, String element, String type, 673 String name) { 674 // First determine the managed object definition. 675 AbstractManagedObjectDefinition<? extends C, ? extends S> d = null; 676 677 if (type != null) { 678 for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : r 679 .getChildDefinition().getAllChildren()) { 680 if (child.getName().equals(type)) { 681 d = child; 682 break; 683 } 684 } 685 686 if (d == null) { 687 throw new IllegalArgumentException("Invalid path element \"" + element 688 + "\" in path \"" + path + "\": unknown sub-type \"" + type + "\""); 689 } 690 } else { 691 d = r.getChildDefinition(); 692 } 693 694 if (r instanceof InstantiableRelationDefinition) { 695 InstantiableRelationDefinition<C, S> ir = 696 (InstantiableRelationDefinition<C, S>) r; 697 698 if (name == null) { 699 throw new IllegalArgumentException("Invalid path element \"" + element 700 + "\" in path \"" + path 701 + "\": no instance name for instantiable relation"); 702 } 703 704 return InstantiableElement.create(ir, d, name); 705 } else if (r instanceof OptionalRelationDefinition) { 706 OptionalRelationDefinition<C, S> or = 707 (OptionalRelationDefinition<C, S>) r; 708 709 if (name != null) { 710 throw new IllegalArgumentException("Invalid path element \"" + element 711 + "\" in path \"" + path 712 + "\": instance name specified for optional relation"); 713 } 714 715 return OptionalElement.create(or, d); 716 } else if (r instanceof SingletonRelationDefinition) { 717 SingletonRelationDefinition<C, S> sr = 718 (SingletonRelationDefinition<C, S>) r; 719 720 if (name != null) { 721 throw new IllegalArgumentException("Invalid path element \"" + element 722 + "\" in path \"" + path 723 + "\": instance name specified for singleton relation"); 724 } 725 726 return SingletonElement.create(sr, d); 727 } else { 728 throw new IllegalArgumentException("Invalid path element \"" + element 729 + "\" in path \"" + path + "\": unsupported relation type"); 730 } 731 } 732 733 // The managed object definition in this path. 734 private final AbstractManagedObjectDefinition<C, S> d; 735 736 // The list of path elements in this path. 737 private final List<Element<?, ?>> elements; 738 739 // The last relation definition in this path. 740 private final RelationDefinition<? super C, ? super S> r; 741 742 743 744 // Private constructor. 745 private ManagedObjectPath(LinkedList<Element<?, ?>> elements, 746 RelationDefinition<? super C, ? super S> r, 747 AbstractManagedObjectDefinition<C, S> d) { 748 this.elements = Collections.unmodifiableList(elements); 749 this.r = r; 750 this.d = d; 751 } 752 753 754 755 /** 756 * Creates a new managed object path which has the same structure as 757 * this path except that the final path element is associated with 758 * the specified managed object definition. 759 * 760 * @param <CC> 761 * The type of client managed object configuration that 762 * this path will reference. 763 * @param <SS> 764 * The type of server managed object configuration that 765 * this path will reference. 766 * @param nd 767 * The new managed object definition. 768 * @return Returns a new managed object path which has the same 769 * structure as this path except that the final path element 770 * is associated with the specified managed object 771 * definition. 772 */ 773 @SuppressWarnings("unchecked") 774 public <CC extends C, SS extends S> ManagedObjectPath<CC, SS> asSubType( 775 AbstractManagedObjectDefinition<CC, SS> nd) { 776 if (r instanceof InstantiableRelationDefinition) { 777 InstantiableRelationDefinition<? super C, ? super S> ir = 778 (InstantiableRelationDefinition<? super C, ? super S>) r; 779 if (elements.size() == 0) { 780 return parent().child(ir, nd, null); 781 } else { 782 return parent().child(ir, nd, 783 elements.get(elements.size() - 1).getName()); 784 } 785 } else if (r instanceof OptionalRelationDefinition) { 786 OptionalRelationDefinition<? super C, ? super S> or = 787 (OptionalRelationDefinition<? super C, ? super S>) r; 788 return parent().child(or, nd); 789 } else { 790 SingletonRelationDefinition<? super C, ? super S> sr = 791 (SingletonRelationDefinition<? super C, ? super S>) r; 792 return parent().child(sr, nd); 793 } 794 } 795 796 797 798 /** 799 * Creates a new child managed object path beneath the provided 800 * parent path having the specified managed object definition. 801 * 802 * @param <M> 803 * The type of client managed object configuration that the 804 * child path references. 805 * @param <N> 806 * The type of server managed object configuration that the 807 * child path references. 808 * @param r 809 * The instantiable relation referencing the child. 810 * @param d 811 * The managed object definition associated with the child 812 * (must be a sub-type of the relation). 813 * @param name 814 * The relative name of the child managed object. 815 * @return Returns a new child managed object path beneath the 816 * provided parent path. 817 * @throws IllegalArgumentException 818 * If the provided name is empty or blank. 819 */ 820 public <M extends ConfigurationClient, N extends Configuration> 821 ManagedObjectPath<M, N> child( 822 InstantiableRelationDefinition<? super M, ? super N> r, 823 AbstractManagedObjectDefinition<M, N> d, String name) 824 throws IllegalArgumentException { 825 if (name.trim().length() == 0) { 826 throw new IllegalArgumentException( 827 "Empty or blank managed object names are not allowed"); 828 } 829 LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>( 830 elements); 831 celements.add(new InstantiableElement<M, N>(r, d, name)); 832 return new ManagedObjectPath<M, N>(celements, r, d); 833 } 834 835 836 837 /** 838 * Creates a new child managed object path beneath the provided 839 * parent path using the relation's child managed object definition. 840 * 841 * @param <M> 842 * The type of client managed object configuration that the 843 * child path references. 844 * @param <N> 845 * The type of server managed object configuration that the 846 * child path references. 847 * @param r 848 * The instantiable relation referencing the child. 849 * @param name 850 * The relative name of the child managed object. 851 * @return Returns a new child managed object path beneath the 852 * provided parent path. 853 * @throws IllegalArgumentException 854 * If the provided name is empty or blank. 855 */ 856 public <M extends ConfigurationClient, N extends Configuration> 857 ManagedObjectPath<M, N> child( 858 InstantiableRelationDefinition<M, N> r, String name) 859 throws IllegalArgumentException { 860 return child(r, r.getChildDefinition(), name); 861 } 862 863 864 865 /** 866 * Creates a new child managed object path beneath the provided 867 * parent path having the specified managed object definition. 868 * 869 * @param <M> 870 * The type of client managed object configuration that the 871 * child path references. 872 * @param <N> 873 * The type of server managed object configuration that the 874 * child path references. 875 * @param r 876 * The optional relation referencing the child. 877 * @param d 878 * The managed object definition associated with the child 879 * (must be a sub-type of the relation). 880 * @return Returns a new child managed object path beneath the 881 * provided parent path. 882 */ 883 public <M extends ConfigurationClient, N extends Configuration> 884 ManagedObjectPath<M, N> child( 885 OptionalRelationDefinition<? super M, ? super N> r, 886 AbstractManagedObjectDefinition<M, N> d) { 887 LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>( 888 elements); 889 celements.add(new OptionalElement<M, N>(r, d)); 890 return new ManagedObjectPath<M, N>(celements, r, d); 891 } 892 893 894 895 /** 896 * Creates a new child managed object path beneath the provided 897 * parent path using the relation's child managed object definition. 898 * 899 * @param <M> 900 * The type of client managed object configuration that the 901 * child path references. 902 * @param <N> 903 * The type of server managed object configuration that the 904 * child path references. 905 * @param r 906 * The optional relation referencing the child. 907 * @return Returns a new child managed object path beneath the 908 * provided parent path. 909 */ 910 public <M extends ConfigurationClient, N extends Configuration> 911 ManagedObjectPath<M, N> child(OptionalRelationDefinition<M, N> r) { 912 return child(r, r.getChildDefinition()); 913 } 914 915 916 917 /** 918 * Creates a new child managed object path beneath the provided 919 * parent path having the specified managed object definition. 920 * 921 * @param <M> 922 * The type of client managed object configuration that the 923 * child path references. 924 * @param <N> 925 * The type of server managed object configuration that the 926 * child path references. 927 * @param r 928 * The singleton relation referencing the child. 929 * @param d 930 * The managed object definition associated with the child 931 * (must be a sub-type of the relation). 932 * @return Returns a new child managed object path beneath the 933 * provided parent path. 934 */ 935 public <M extends ConfigurationClient, N extends Configuration> 936 ManagedObjectPath<M, N> child( 937 SingletonRelationDefinition<? super M, ? super N> r, 938 AbstractManagedObjectDefinition<M, N> d) { 939 LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>( 940 elements); 941 celements.add(new SingletonElement<M, N>(r, d)); 942 return new ManagedObjectPath<M, N>(celements, r, d); 943 } 944 945 946 947 /** 948 * Creates a new child managed object path beneath the provided 949 * parent path using the relation's child managed object definition. 950 * 951 * @param <M> 952 * The type of client managed object configuration that the 953 * child path references. 954 * @param <N> 955 * The type of server managed object configuration that the 956 * child path references. 957 * @param r 958 * The singleton relation referencing the child. 959 * @return Returns a new child managed object path beneath the 960 * provided parent path. 961 */ 962 public <M extends ConfigurationClient, N extends Configuration> 963 ManagedObjectPath<M, N> child(SingletonRelationDefinition<M, N> r) { 964 return child(r, r.getChildDefinition()); 965 } 966 967 968 969 /** 970 * {@inheritDoc} 971 */ 972 @Override 973 public boolean equals(Object obj) { 974 if (obj == this) { 975 return true; 976 } else if (obj instanceof ManagedObjectPath) { 977 ManagedObjectPath<?, ?> other = (ManagedObjectPath<?, ?>) obj; 978 return toString().equals(other.toString()); 979 } else { 980 return false; 981 } 982 } 983 984 985 986 /** 987 * Get the definition of the managed object referred to by this 988 * path. 989 * <p> 990 * When the path is empty, the {@link RootCfgDefn} is returned. 991 * 992 * @return Returns the definition of the managed object referred to 993 * by this path, or the {@link RootCfgDefn} if the path is 994 * empty. 995 */ 996 public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() { 997 return d; 998 } 999 1000 1001 1002 /** 1003 * Get the name of the managed object referred to by this path if 1004 * applicable. 1005 * <p> 1006 * If there path does not refer to an instantiable managed object 1007 * <code>null</code> is returned. 1008 * 1009 * @return Returns the name of the managed object referred to by 1010 * this path, or <code>null</code> if the managed object 1011 * does not have a name. 1012 */ 1013 public String getName() { 1014 if (elements.isEmpty()) { 1015 return null; 1016 } else { 1017 return elements.get(elements.size() - 1).getName(); 1018 } 1019 } 1020 1021 1022 1023 /** 1024 * Get the relation definition of the managed object referred to by 1025 * this path. 1026 * <p> 1027 * When the path is empty, the <code>null</code> is returned. 1028 * 1029 * @return Returns the relation definition of the managed object 1030 * referred to by this path, or the <code>null</code> if 1031 * the path is empty. 1032 */ 1033 public RelationDefinition<? super C, ? super S> getRelationDefinition() { 1034 return r; 1035 } 1036 1037 1038 1039 /** 1040 * {@inheritDoc} 1041 */ 1042 @Override 1043 public int hashCode() { 1044 return toString().hashCode(); 1045 } 1046 1047 1048 1049 /** 1050 * Determine whether or not this path contains any path elements. 1051 * 1052 * @return Returns <code>true</code> if this path does not contain 1053 * any path elements. 1054 */ 1055 public boolean isEmpty() { 1056 return elements.isEmpty(); 1057 } 1058 1059 1060 1061 /** 1062 * Determines whether this managed object path references the same 1063 * location as the provided managed object path. 1064 * <p> 1065 * This method differs from <code>equals</code> in that it ignores 1066 * sub-type definitions. 1067 * 1068 * @param other 1069 * The managed object path to be compared. 1070 * @return Returns <code>true</code> if this managed object path 1071 * references the same location as the provided managed 1072 * object path. 1073 */ 1074 public boolean matches(ManagedObjectPath<?, ?> other) { 1075 DN thisDN = toDN(); 1076 DN otherDN = other.toDN(); 1077 return thisDN.equals(otherDN); 1078 } 1079 1080 1081 1082 /** 1083 * Creates a new parent managed object path representing the 1084 * immediate parent of this path. This method is a short-hand for 1085 * <code>parent(1)</code>. 1086 * 1087 * @return Returns a new parent managed object path representing the 1088 * immediate parent of this path. 1089 * @throws IllegalArgumentException 1090 * If this path does not have a parent (i.e. it is the 1091 * empty path). 1092 */ 1093 public ManagedObjectPath<?, ?> parent() throws IllegalArgumentException { 1094 return parent(1); 1095 } 1096 1097 1098 1099 /** 1100 * Creates a new parent managed object path the specified number of 1101 * path elements above this path. 1102 * 1103 * @param offset 1104 * The number of path elements (0 - means no offset, 1 1105 * means the parent, and 2 means the grand-parent). 1106 * @return Returns a new parent managed object path the specified 1107 * number of path elements above this path. 1108 * @throws IllegalArgumentException 1109 * If the offset is less than 0, or greater than the 1110 * number of path elements in this path. 1111 */ 1112 public ManagedObjectPath<?, ?> parent(int offset) 1113 throws IllegalArgumentException { 1114 if (offset < 0) { 1115 throw new IllegalArgumentException("Negative offset"); 1116 } 1117 1118 if (offset > elements.size()) { 1119 throw new IllegalArgumentException( 1120 "Offset is greater than the number of path elements"); 1121 } 1122 1123 // An offset of 0 leaves the path unchanged. 1124 if (offset == 0) { 1125 return this; 1126 } 1127 1128 // Return the empty path if the parent has zero elements. 1129 if (elements.size() == offset) { 1130 return emptyPath(); 1131 } 1132 1133 LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>( 1134 elements.subList(0, elements.size() - offset)); 1135 return create(celements, celements.getLast()); 1136 } 1137 1138 1139 1140 /** 1141 * Creates a new managed object path which has the same structure as 1142 * this path except that the final path element is renamed. The 1143 * final path element must comprise of an instantiable relation. 1144 * 1145 * @param newName 1146 * The new name of the final path element. 1147 * @return Returns a new managed object path which has the same 1148 * structure as this path except that the final path element 1149 * is renamed. 1150 * @throws IllegalStateException 1151 * If this managed object path is empty or if its final 1152 * path element does not comprise of an instantiable 1153 * relation. 1154 */ 1155 @SuppressWarnings("unchecked") 1156 public ManagedObjectPath<C, S> rename(String newName) 1157 throws IllegalStateException { 1158 if (elements.size() == 0) { 1159 throw new IllegalStateException("Cannot rename an empty path"); 1160 } 1161 1162 if (r instanceof InstantiableRelationDefinition) { 1163 InstantiableRelationDefinition<? super C, ? super S> ir = 1164 (InstantiableRelationDefinition<? super C, ? super S>) r; 1165 return parent().child(ir, d, newName); 1166 } else { 1167 throw new IllegalStateException("Not an instantiable relation"); 1168 } 1169 } 1170 1171 1172 1173 /** 1174 * Serialize this managed object path using the provided 1175 * serialization strategy. 1176 * <p> 1177 * The path elements will be passed to the serializer in big-endian 1178 * order: starting from the root element and proceeding down to the 1179 * leaf. 1180 * 1181 * @param serializer 1182 * The managed object path serialization strategy. 1183 */ 1184 public void serialize(ManagedObjectPathSerializer serializer) { 1185 for (Element<?, ?> element : elements) { 1186 element.serialize(serializer); 1187 } 1188 } 1189 1190 1191 1192 /** 1193 * Get the number of path elements in this managed object path. 1194 * 1195 * @return Returns the number of path elements (0 - means no offset, 1196 * 1 means the parent, and 2 means the grand-parent). 1197 */ 1198 public int size() { 1199 return elements.size(); 1200 } 1201 1202 1203 1204 /** 1205 * Creates a DN representation of this managed object path. 1206 * 1207 * @return Returns a DN representation of this managed object path. 1208 */ 1209 public DN toDN() { 1210 // Use a simple serializer to create the contents. 1211 DNSerializer serializer = new DNSerializer(); 1212 serialize(serializer); 1213 return serializer.toDN(); 1214 } 1215 1216 1217 1218 /** 1219 * {@inheritDoc} 1220 */ 1221 @Override 1222 public String toString() { 1223 StringBuilder builder = new StringBuilder(); 1224 toString(builder); 1225 return builder.toString(); 1226 } 1227 1228 1229 1230 /** 1231 * Appends a string representation of this managed object path to 1232 * the provided string builder. 1233 * 1234 * @param builder 1235 * Append the string representation to this builder. 1236 * @see #toString() 1237 */ 1238 public void toString(final StringBuilder builder) { 1239 if (isEmpty()) { 1240 // Special treatment of root configuration paths. 1241 builder.append('/'); 1242 } else { 1243 // Use a simple serializer to create the contents. 1244 ManagedObjectPathSerializer serializer = new StringSerializer(builder); 1245 serialize(serializer); 1246 } 1247 } 1248 1249 }