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