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