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