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