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