1/* 2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.ws.policy.sourcemodel; 27 28import com.sun.xml.internal.ws.policy.privateutil.LocalizationMessages; 29import com.sun.xml.internal.ws.policy.privateutil.PolicyLogger; 30import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils; 31import com.sun.xml.internal.ws.policy.sourcemodel.wspolicy.XmlToken; 32 33import java.util.Collection; 34import java.util.Collections; 35import java.util.Iterator; 36import java.util.LinkedList; 37 38/** 39 * The general representation of a single node within a {@link com.sun.xml.internal.ws.policy.sourcemodel.PolicySourceModel} instance. 40 * The model node is created via factory methods of the {@link com.sun.xml.internal.ws.policy.sourcemodel.PolicySourceModel} instance. 41 * It may also hold {@link com.sun.xml.internal.ws.policy.sourcemodel.AssertionData} instance in case its type is {@code ModelNode.Type.ASSERTION}. 42 * 43 * @author Marek Potociar 44 */ 45public final class ModelNode implements Iterable<ModelNode>, Cloneable { 46 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(ModelNode.class); 47 48 /** 49 * Policy source model node type enumeration 50 */ 51 public static enum Type { 52 POLICY(XmlToken.Policy), 53 ALL(XmlToken.All), 54 EXACTLY_ONE(XmlToken.ExactlyOne), 55 POLICY_REFERENCE(XmlToken.PolicyReference), 56 ASSERTION(XmlToken.UNKNOWN), 57 ASSERTION_PARAMETER_NODE(XmlToken.UNKNOWN); 58 59 private XmlToken token; 60 61 Type(XmlToken token) { 62 this.token = token; 63 } 64 65 public XmlToken getXmlToken() { 66 return token; 67 } 68 69 /** 70 * Method checks the PSM state machine if the creation of new child of given type is plausible for a node element 71 * with type set to this type instance. 72 * 73 * @param childType The type. 74 * @return True if the type is supported, false otherwise 75 */ 76 private boolean isChildTypeSupported(final Type childType) { 77 switch (this) { 78 case POLICY: 79 case ALL: 80 case EXACTLY_ONE: 81 switch (childType) { 82 case ASSERTION_PARAMETER_NODE: 83 return false; 84 default: 85 return true; 86 } 87 case POLICY_REFERENCE: 88 return false; 89 case ASSERTION: 90 switch (childType) { 91 case POLICY: 92 case POLICY_REFERENCE: 93 case ASSERTION_PARAMETER_NODE: 94 return true; 95 default: 96 return false; 97 } 98 case ASSERTION_PARAMETER_NODE: 99 switch (childType) { 100 case ASSERTION_PARAMETER_NODE: 101 return true; 102 default: 103 return false; 104 } 105 default: 106 throw LOGGER.logSevereException(new IllegalStateException( 107 LocalizationMessages.WSP_0060_POLICY_ELEMENT_TYPE_UNKNOWN(this))); 108 } 109 } 110 } 111 112 // comon model node attributes 113 private LinkedList<ModelNode> children; 114 private Collection<ModelNode> unmodifiableViewOnContent; 115 private final ModelNode.Type type; 116 private ModelNode parentNode; 117 private PolicySourceModel parentModel; 118 119 // attributes used only in 'POLICY_REFERENCE' model node 120 private PolicyReferenceData referenceData; 121 private PolicySourceModel referencedModel; 122 123 // attibutes used only in 'ASSERTION' or 'ASSERTION_PARAMETER_NODE' model node 124 private AssertionData nodeData; 125 126 /** 127 * The factory method creates and initializes the POLICY model node and sets it's parent model reference to point to 128 * the model supplied as an input parameter. This method is intended to be used ONLY from {@link PolicySourceModel} during 129 * the initialization of its own internal structures. 130 * 131 * @param model policy source model to be used as a parent model of the newly created {@link ModelNode}. Must not be {@code null} 132 * @return POLICY model node with the parent model reference initialized to the model supplied as an input parameter 133 * @throws IllegalArgumentException if the {@code model} input parameter is {@code null} 134 */ 135 static ModelNode createRootPolicyNode(final PolicySourceModel model) throws IllegalArgumentException { 136 if (model == null) { 137 throw LOGGER.logSevereException(new IllegalArgumentException(LocalizationMessages.WSP_0039_POLICY_SRC_MODEL_INPUT_PARAMETER_MUST_NOT_BE_NULL())); 138 } 139 return new ModelNode(ModelNode.Type.POLICY, model); 140 } 141 142 private ModelNode(Type type, PolicySourceModel parentModel) { 143 this.type = type; 144 this.parentModel = parentModel; 145 this.children = new LinkedList<ModelNode>(); 146 this.unmodifiableViewOnContent = Collections.unmodifiableCollection(this.children); 147 } 148 149 private ModelNode(Type type, PolicySourceModel parentModel, AssertionData data) { 150 this(type, parentModel); 151 152 this.nodeData = data; 153 } 154 155 private ModelNode(PolicySourceModel parentModel, PolicyReferenceData data) { 156 this(Type.POLICY_REFERENCE, parentModel); 157 158 this.referenceData = data; 159 } 160 161 private void checkCreateChildOperationSupportForType(final Type type) throws UnsupportedOperationException { 162 if (!this.type.isChildTypeSupported(type)) { 163 throw LOGGER.logSevereException(new UnsupportedOperationException(LocalizationMessages.WSP_0073_CREATE_CHILD_NODE_OPERATION_NOT_SUPPORTED(type, this.type))); 164 } 165 } 166 167 /** 168 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 169 * Each node is created with respect to its enclosing policy source model. 170 * 171 * @return A new Policy node. 172 */ 173 public ModelNode createChildPolicyNode() { 174 checkCreateChildOperationSupportForType(Type.POLICY); 175 176 final ModelNode node = new ModelNode(ModelNode.Type.POLICY, parentModel); 177 this.addChild(node); 178 179 return node; 180 } 181 182 /** 183 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 184 * Each node is created with respect to its enclosing policy source model. 185 * 186 * @return A new All node. 187 */ 188 public ModelNode createChildAllNode() { 189 checkCreateChildOperationSupportForType(Type.ALL); 190 191 final ModelNode node = new ModelNode(ModelNode.Type.ALL, parentModel); 192 this.addChild(node); 193 194 return node; 195 } 196 197 /** 198 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 199 * Each node is created with respect to its enclosing policy source model. 200 * 201 * @return A new ExactlyOne node. 202 */ 203 public ModelNode createChildExactlyOneNode() { 204 checkCreateChildOperationSupportForType(Type.EXACTLY_ONE); 205 206 final ModelNode node = new ModelNode(ModelNode.Type.EXACTLY_ONE, parentModel); 207 this.addChild(node); 208 209 return node; 210 } 211 212 /** 213 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 214 * Each node is created with respect to its enclosing policy source model. 215 * 216 * @return A new policy assertion node. 217 */ 218 public ModelNode createChildAssertionNode() { 219 checkCreateChildOperationSupportForType(Type.ASSERTION); 220 221 final ModelNode node = new ModelNode(ModelNode.Type.ASSERTION, parentModel); 222 this.addChild(node); 223 224 return node; 225 } 226 227 /** 228 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 229 * Each node is created with respect to its enclosing policy source model. 230 * 231 * @param nodeData The policy assertion data. 232 * @return A new policy assertion node. 233 */ 234 public ModelNode createChildAssertionNode(final AssertionData nodeData) { 235 checkCreateChildOperationSupportForType(Type.ASSERTION); 236 237 final ModelNode node = new ModelNode(Type.ASSERTION, parentModel, nodeData); 238 this.addChild(node); 239 240 return node; 241 } 242 243 /** 244 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 245 * Each node is created with respect to its enclosing policy source model. 246 * 247 * @return A new assertion parameter node. 248 */ 249 public ModelNode createChildAssertionParameterNode() { 250 checkCreateChildOperationSupportForType(Type.ASSERTION_PARAMETER_NODE); 251 252 final ModelNode node = new ModelNode(ModelNode.Type.ASSERTION_PARAMETER_NODE, parentModel); 253 this.addChild(node); 254 255 return node; 256 } 257 258 /** 259 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 260 * Each node is created with respect to its enclosing policy source model. 261 * 262 * @param nodeData The assertion parameter data. 263 * @return A new assertion parameter node. 264 */ 265 ModelNode createChildAssertionParameterNode(final AssertionData nodeData) { 266 checkCreateChildOperationSupportForType(Type.ASSERTION_PARAMETER_NODE); 267 268 final ModelNode node = new ModelNode(Type.ASSERTION_PARAMETER_NODE, parentModel, nodeData); 269 this.addChild(node); 270 271 return node; 272 } 273 274 /** 275 * Factory method that creates new policy source model node as specified by a factory method name and input parameters. 276 * Each node is created with respect to its enclosing policy source model. 277 * 278 * @param referenceData The PolicyReference data. 279 * @return A new PolicyReference node. 280 */ 281 ModelNode createChildPolicyReferenceNode(final PolicyReferenceData referenceData) { 282 checkCreateChildOperationSupportForType(Type.POLICY_REFERENCE); 283 284 final ModelNode node = new ModelNode(parentModel, referenceData); 285 this.parentModel.addNewPolicyReference(node); 286 this.addChild(node); 287 288 return node; 289 } 290 291 Collection<ModelNode> getChildren() { 292 return unmodifiableViewOnContent; 293 } 294 295 /** 296 * Sets the parent model reference on the node and its children. The method may be invoked only on the root node 297 * of the policy source model (or - in general - on a model node that dose not reference a parent node). Otherwise an 298 * exception is thrown. 299 * 300 * @param model new parent policy source model to be set. 301 * @throws IllegalAccessException in case this node references a parent node (i.e. is not a root node of the model). 302 */ 303 void setParentModel(final PolicySourceModel model) throws IllegalAccessException { 304 if (parentNode != null) { 305 throw LOGGER.logSevereException(new IllegalAccessException(LocalizationMessages.WSP_0049_PARENT_MODEL_CAN_NOT_BE_CHANGED())); 306 } 307 308 this.updateParentModelReference(model); 309 } 310 311 /** 312 * The method updates the parentModel reference on current model node instance and all of it's children 313 * 314 * @param model new policy source model reference. 315 */ 316 private void updateParentModelReference(final PolicySourceModel model) { 317 this.parentModel = model; 318 319 for (ModelNode child : children) { 320 child.updateParentModelReference(model); 321 } 322 } 323 324 /** 325 * Returns the parent policy source model that contains this model node. 326 * 327 * @return the parent policy source model 328 */ 329 public PolicySourceModel getParentModel() { 330 return parentModel; 331 } 332 333 /** 334 * Returns the type of this policy source model node. 335 * 336 * @return actual type of this policy source model node 337 */ 338 public ModelNode.Type getType() { 339 return type; 340 } 341 342 /** 343 * Returns the parent referenced by this policy source model node. 344 * 345 * @return current parent of this policy source model node or {@code null} if the node does not have a parent currently. 346 */ 347 public ModelNode getParentNode() { 348 return parentNode; 349 } 350 351 /** 352 * Returns the data for this policy source model node (if any). The model node data are expected to be not {@code null} only in 353 * case the type of this node is ASSERTION or ASSERTION_PARAMETER_NODE. 354 * 355 * @return the data of this policy source model node or {@code null} if the node does not have any data associated to it 356 * attached. 357 */ 358 public AssertionData getNodeData() { 359 return nodeData; 360 } 361 362 /** 363 * Returns the policy reference data for this policy source model node. The policy reference data are expected to be not {@code null} only in 364 * case the type of this node is POLICY_REFERENCE. 365 * 366 * @return the policy reference data for this policy source model node or {@code null} if the node does not have any policy reference data 367 * attached. 368 */ 369 PolicyReferenceData getPolicyReferenceData() { 370 return referenceData; 371 } 372 373 /** 374 * The method may be used to set or replace assertion data set for this node. If there are assertion data set already, 375 * those are replaced by a new reference and eventualy returned from the method. 376 * <p/> 377 * This method is supported only in case this model node instance's type is {@code ASSERTION} or {@code ASSERTION_PARAMETER_NODE}. 378 * If used from other node types, an exception is thrown. 379 * 380 * @param newData new assertion data to be set. 381 * @return old and replaced assertion data if any or {@code null} otherwise. 382 * 383 * @throws UnsupportedOperationException in case this method is called on nodes of type other than {@code ASSERTION} 384 * or {@code ASSERTION_PARAMETER_NODE} 385 */ 386 public AssertionData setOrReplaceNodeData(final AssertionData newData) { 387 if (!isDomainSpecific()) { 388 throw LOGGER.logSevereException(new UnsupportedOperationException(LocalizationMessages.WSP_0051_OPERATION_NOT_SUPPORTED_FOR_THIS_BUT_ASSERTION_RELATED_NODE_TYPE(type))); 389 } 390 391 final AssertionData oldData = this.nodeData; 392 this.nodeData = newData; 393 394 return oldData; 395 } 396 397 /** 398 * The method specifies whether the model node instance represents assertion related node, it means whether its type 399 * is 'ASSERTION' or 'ASSERTION_PARAMETER_NODE'. This is, for example, the way to determine whether the node supports 400 * setting a {@link AssertionData} object via {@link #setOrReplaceNodeData(AssertionData)} method or not. 401 * 402 * @return {@code true} or {@code false} according to whether the node instance represents assertion related node or not. 403 */ 404 boolean isDomainSpecific() { 405 return type == Type.ASSERTION || type == Type.ASSERTION_PARAMETER_NODE; 406 } 407 408 /** 409 * Appends the specified child node to the end of the children list of this node and sets it's parent to reference 410 * this node. 411 * 412 * @param child node to be appended to the children list of this node. 413 * @return {@code true} (as per the general contract of the {@code Collection.add} method). 414 * 415 * @throws NullPointerException if the specified node is {@code null}. 416 * @throws IllegalArgumentException if child has a parent node set already to point to some node 417 */ 418 private boolean addChild(final ModelNode child) { 419 children.add(child); 420 child.parentNode = this; 421 422 return true; 423 } 424 425 void setReferencedModel(final PolicySourceModel model) { 426 if (this.type != Type.POLICY_REFERENCE) { 427 throw LOGGER.logSevereException(new UnsupportedOperationException(LocalizationMessages.WSP_0050_OPERATION_NOT_SUPPORTED_FOR_THIS_BUT_POLICY_REFERENCE_NODE_TYPE(type))); 428 } 429 430 referencedModel = model; 431 } 432 433 PolicySourceModel getReferencedModel() { 434 return referencedModel; 435 } 436 437 /** 438 * Returns the number of child policy source model nodes. If this model node contains 439 * more than {@code Integer.MAX_VALUE} children, returns {@code Integer.MAX_VALUE}. 440 * 441 * @return the number of children of this node. 442 */ 443 public int childrenSize() { 444 return children.size(); 445 } 446 447 /** 448 * Returns true if the node has at least one child node. 449 * 450 * @return true if the node has at least one child node, false otherwise. 451 */ 452 public boolean hasChildren() { 453 return !children.isEmpty(); 454 } 455 456 /** 457 * Iterates through all child nodes. 458 * 459 * @return An iterator for the child nodes 460 */ 461 public Iterator<ModelNode> iterator() { 462 return children.iterator(); 463 } 464 465 /** 466 * An {@code Object.equals(Object obj)} method override. Method ignores the parent source model. It means that two 467 * model nodes may be the same even if they belong to different models. 468 * <p/> 469 * If parent model comparison is desired, it must be accomplished separately. To perform that, the reference equality 470 * test is sufficient ({@code nodeA.getParentModel() == nodeB.getParentModel()}), since all model nodes are created 471 * for specific model instances. 472 */ 473 @Override 474 public boolean equals(final Object obj) { 475 if (this == obj) { 476 return true; 477 } 478 479 if (!(obj instanceof ModelNode)) { 480 return false; 481 } 482 483 boolean result = true; 484 final ModelNode that = (ModelNode) obj; 485 486 result = result && this.type.equals(that.type); 487 // result = result && ((this.parentNode == null) ? that.parentNode == null : this.parentNode.equals(that.parentNode)); 488 result = result && ((this.nodeData == null) ? that.nodeData == null : this.nodeData.equals(that.nodeData)); 489 result = result && ((this.children == null) ? that.children == null : this.children.equals(that.children)); 490 491 return result; 492 } 493 494 /** 495 * An {@code Object.hashCode()} method override. 496 */ 497 @Override 498 public int hashCode() { 499 int result = 17; 500 501 result = 37 * result + this.type.hashCode(); 502 result = 37 * result + ((this.parentNode == null) ? 0 : this.parentNode.hashCode()); 503 result = 37 * result + ((this.nodeData == null) ? 0 : this.nodeData.hashCode()); 504 result = 37 * result + this.children.hashCode(); 505 506 return result; 507 } 508 509 /** 510 * Returns a string representation of the object. In general, the <code>toString</code> method 511 * returns a string that "textually represents" this object. 512 * 513 * @return a string representation of the object. 514 */ 515 @Override 516 public String toString() { 517 return toString(0, new StringBuffer()).toString(); 518 } 519 520 /** 521 * A helper method that appends indented string representation of this instance to the input string buffer. 522 * 523 * @param indentLevel indentation level to be used. 524 * @param buffer buffer to be used for appending string representation of this instance 525 * @return modified buffer containing new string representation of the instance 526 */ 527 public StringBuffer toString(final int indentLevel, final StringBuffer buffer) { 528 final String indent = PolicyUtils.Text.createIndent(indentLevel); 529 final String innerIndent = PolicyUtils.Text.createIndent(indentLevel + 1); 530 531 buffer.append(indent).append(type).append(" {").append(PolicyUtils.Text.NEW_LINE); 532 if (type == Type.ASSERTION) { 533 if (nodeData == null) { 534 buffer.append(innerIndent).append("no assertion data set"); 535 } else { 536 nodeData.toString(indentLevel + 1, buffer); 537 } 538 buffer.append(PolicyUtils.Text.NEW_LINE); 539 } else if (type == Type.POLICY_REFERENCE) { 540 if (referenceData == null) { 541 buffer.append(innerIndent).append("no policy reference data set"); 542 } else { 543 referenceData.toString(indentLevel + 1, buffer); 544 } 545 buffer.append(PolicyUtils.Text.NEW_LINE); 546 } else if (type == Type.ASSERTION_PARAMETER_NODE) { 547 if (nodeData == null) { 548 buffer.append(innerIndent).append("no parameter data set"); 549 } 550 else { 551 nodeData.toString(indentLevel + 1, buffer); 552 } 553 buffer.append(PolicyUtils.Text.NEW_LINE); 554 } 555 556 if (children.size() > 0) { 557 for (ModelNode child : children) { 558 child.toString(indentLevel + 1, buffer).append(PolicyUtils.Text.NEW_LINE); 559 } 560 } else { 561 buffer.append(innerIndent).append("no child nodes").append(PolicyUtils.Text.NEW_LINE); 562 } 563 564 buffer.append(indent).append('}'); 565 return buffer; 566 } 567 568 @Override 569 protected ModelNode clone() throws CloneNotSupportedException { 570 final ModelNode clone = (ModelNode) super.clone(); 571 572 if (this.nodeData != null) { 573 clone.nodeData = this.nodeData.clone(); 574 } 575 576 // no need to clone PolicyReferenceData, since those are immutable 577 578 if (this.referencedModel != null) { 579 clone.referencedModel = this.referencedModel.clone(); 580 } 581 582 583 clone.children = new LinkedList<ModelNode>(); 584 clone.unmodifiableViewOnContent = Collections.unmodifiableCollection(clone.children); 585 586 for (ModelNode thisChild : this.children) { 587 clone.addChild(thisChild.clone()); 588 } 589 590 return clone; 591 } 592 593 PolicyReferenceData getReferenceData() { 594 return referenceData; 595 } 596} 597