FileDocCategorySizeDatePackage
ModelNode.javaAPI DocExample24696Tue May 29 16:56:38 BST 2007com.sun.xml.ws.policy.sourcemodel

ModelNode.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.xml.ws.policy.sourcemodel;

import com.sun.xml.ws.policy.PolicyConstants;
import com.sun.xml.ws.policy.privateutil.LocalizationMessages;
import com.sun.xml.ws.policy.privateutil.PolicyLogger;
import com.sun.xml.ws.policy.privateutil.PolicyUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import javax.xml.namespace.QName;

/**
 * The general representation of a single node within a {@link com.sun.xml.ws.policy.sourcemodel.PolicySourceModel} instance.
 * The model node is created via factory methods of the {@link com.sun.xml.ws.policy.sourcemodel.PolicySourceModel} instance.
 * It may also hold {@link com.sun.xml.ws.policy.sourcemodel.AssertionData} instance in case its type is {@code ModelNode.Type.ASSERTION}.
 *
 * @author Marek Potociar
 */
public final class ModelNode implements Iterable<ModelNode>, Cloneable {
    private static final PolicyLogger LOGGER = PolicyLogger.getLogger(ModelNode.class);
    
    /**
     * Policy source model node type enumeration
     */
    public static enum Type {
        POLICY(new QName(PolicyConstants.POLICY_NAMESPACE_URI, "Policy")),
        ALL(new QName(PolicyConstants.POLICY_NAMESPACE_URI, "All")),
        EXACTLY_ONE(new QName(PolicyConstants.POLICY_NAMESPACE_URI, "ExactlyOne")),
        POLICY_REFERENCE(new QName(PolicyConstants.POLICY_NAMESPACE_URI, "PolicyReference")),
        ASSERTION(null),
        ASSERTION_PARAMETER_NODE(null);
        
        private QName qName;
        
        Type(QName qName) {
            this.qName = qName;
        }
        
        public QName asQName() {
            return qName;
        }
        
        /**
         * Method checks the PSM state machine if the creation of new child of given type is plausible for a node element
         * with type set to this type instance.
         */
        boolean isChildTypeSupported(final Type childType) {
            switch (this) {
                case POLICY:
                case ALL:
                case EXACTLY_ONE:
                    switch (childType) {
                        case ASSERTION_PARAMETER_NODE:
                            return false;
                        default:
                            return true;
                    }
                case POLICY_REFERENCE:
                    return false;
                case ASSERTION:
                    switch (childType) {
                        case POLICY:
                        case POLICY_REFERENCE:
                        case ASSERTION_PARAMETER_NODE:
                            return true;
                        default:
                            return false;
                    }
                case ASSERTION_PARAMETER_NODE:
                    switch (childType) {
                        case ASSERTION_PARAMETER_NODE:
                            return true;
                        default:
                            return false;
                    }
                default:
                    throw LOGGER.logSevereException(new IllegalStateException(LocalizationMessages.WSP_0060_MODEL_NODE_TYPE_UNKNOWN(this)));
            }
        }
    }
    
    // comon model node attributes
    private LinkedList<ModelNode> content;
    private Collection<ModelNode> unmodifiableViewOnContent;
    private final ModelNode.Type type;
    private ModelNode parentNode;
    private PolicySourceModel parentModel;
    
    // attributes used only in 'POLICY_REFERENCE' model node
    private PolicyReferenceData referenceData;
    private PolicySourceModel referencedModel;
    
    // attibutes used only in 'ASSERTION' or 'ASSERTION_PARAMETER_NODE' model node
    private AssertionData nodeData;
    
    /**
     * The factory method creates and initializes the POLICY model node and sets it's parent model reference to point to
     * the model supplied as an input parameter. This method is intended to be used ONLY from {@link PolicySourceModel} during
     * the initialization of its own internal structures.
     *
     * @param model policy source model to be used as a parent model of the newly created {@link ModelNode}. Must not be {@code null}
     * @return POLICY model node with the parent model reference initialized to the model supplied as an input parameter
     * @throws IllegalArgumentException if the {@code model} input parameter is {@code null}
     */
    static ModelNode createRootPolicyNode(final PolicySourceModel model) throws IllegalArgumentException {
        if (model == null) {
            throw LOGGER.logSevereException(new IllegalArgumentException(LocalizationMessages.WSP_0039_POLICY_SRC_MODEL_INPUT_PARAMETER_MUST_NOT_BE_NULL()));
        }
        return new ModelNode(ModelNode.Type.POLICY, model);
    }
    
    private ModelNode(Type type, PolicySourceModel parentModel) {
        this.type = type;
        this.parentModel = parentModel;
        this.content = new LinkedList<ModelNode>();
        this.unmodifiableViewOnContent = Collections.unmodifiableCollection(this.content);
    }
    
    private ModelNode(Type type, PolicySourceModel parentModel, AssertionData data) {
        this(type, parentModel);
        
        this.nodeData = data;
    }
    
    private ModelNode(PolicySourceModel parentModel, PolicyReferenceData data) {
        this(Type.POLICY_REFERENCE, parentModel);
        
        this.referenceData = data;
    }
    
    private void checkCreateChildOperationSupportForType(final Type type) throws UnsupportedOperationException {
        if (!this.type.isChildTypeSupported(type)) {
            throw LOGGER.logSevereException(new UnsupportedOperationException(LocalizationMessages.WSP_0073_CREATE_CHILD_NODE_OPERATION_NOT_SUPPORTED(type, this.type)));
        }
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildPolicyNode() {
        checkCreateChildOperationSupportForType(Type.POLICY);
        
        final ModelNode node = new ModelNode(ModelNode.Type.POLICY, parentModel);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildAllNode() {
        checkCreateChildOperationSupportForType(Type.ALL);
        
        final ModelNode node = new ModelNode(ModelNode.Type.ALL, parentModel);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildExactlyOneNode() {
        checkCreateChildOperationSupportForType(Type.EXACTLY_ONE);
        
        final ModelNode node = new ModelNode(ModelNode.Type.EXACTLY_ONE, parentModel);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildAssertionNode() {
        checkCreateChildOperationSupportForType(Type.ASSERTION);
        
        final ModelNode node = new ModelNode(ModelNode.Type.ASSERTION, parentModel);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildAssertionNode(final AssertionData nodeData) {
        checkCreateChildOperationSupportForType(Type.ASSERTION);
        
        final ModelNode node = new ModelNode(Type.ASSERTION, parentModel, nodeData);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildAssertionParameterNode() {
        checkCreateChildOperationSupportForType(Type.ASSERTION_PARAMETER_NODE);
        
        final ModelNode node = new ModelNode(ModelNode.Type.ASSERTION_PARAMETER_NODE, parentModel);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildAssertionParameterNode(final AssertionData nodeData) {
        checkCreateChildOperationSupportForType(Type.ASSERTION_PARAMETER_NODE);
        
        final ModelNode node = new ModelNode(Type.ASSERTION_PARAMETER_NODE, parentModel, nodeData);
        this.addChild(node);
        
        return node;
    }
    
    /**
     * TODO: proper java doc
     *
     * Factory method that creates new policy source model node as specified by a factory method name and input parameters.
     * Each node is created with respect to its enclosing policy source model.
     */
    public ModelNode createChildPolicyReferenceNode(final PolicyReferenceData referenceData) {
        checkCreateChildOperationSupportForType(Type.POLICY_REFERENCE);
        
        final ModelNode node = new ModelNode(parentModel, referenceData);
        this.parentModel.addNewPolicyReference(node);
        this.addChild(node);
        
        return node;
    }
    
    Collection<ModelNode> getContent() {
        return unmodifiableViewOnContent;
    }
    
//    Collection<ModelNode> getAssertionParameterNodeChildren() {
//        Collection<ModelNode> result = null;
//
//        if (isAssertionRelatedNode()) {
//
//            result = new LinkedList<ModelNode>();
//
//            for (ModelNode child : content) {
//                if (child.type == Type.ASSERTION_PARAMETER_NODE) {
//                    result.add(child);
//                }
//            }
//        }
//
//        return result;
//    }
//
    /**
     * Sets the parent model reference on the node and its children. The method may be invoked only on the root node
     * of the policy source model (or - in general - on a model node that dose not reference a parent node). Otherwise an
     * exception is thrown.
     *
     * @param model new parent policy source model to be set.
     * @throws IllegalAccessException in case this node references a parent node (i.e. is not a root node of the model).
     */
    void setParentModel(final PolicySourceModel model) throws IllegalAccessException {
        if (parentNode != null) {
            throw LOGGER.logSevereException(new IllegalAccessException(LocalizationMessages.WSP_0049_PARENT_MODEL_CAN_NOT_BE_CHANGED()));
        }
        
        this.updateParentModelReference(model);
    }
    
    /**
     * The method updates the parentModel reference on current model node instance and all of it's children
     *
     * @param model new policy source model reference.
     */
    private void updateParentModelReference(final PolicySourceModel model) {
        this.parentModel = model;
        
        for (ModelNode child : content) {
            child.updateParentModelReference(model);
        }
    }
    
    /**
     * Returns the parent policy source model that contains this model node.
     *
     * @return the parent policy source model
     */
    public PolicySourceModel getParentModel() {
        return parentModel;
    }
    
    /**
     * Returns the type of this policy source model node.
     *
     * @return actual type of this policy source model node
     */
    public ModelNode.Type getType() {
        return type;
    }
    
    /**
     * Returns the parent referenced by this policy source model node.
     *
     * @return current parent of this policy source model node or {@code null} if the node does not have a parent currently.
     */
    public ModelNode getParentNode() {
        return parentNode;
    }
    
    /**
     * Returns the data for this policy source model node (if any). The model node data are expected to be not {@code null} only in
     * case the type of this node is ASSERTION or ASSERTION_PARAMETER_NODE.
     *
     * @return the data of this policy source model node or {@code null} if the node does not have any data associated to it
     * attached.
     */
    public AssertionData getNodeData() {
        return nodeData;
    }
    
    /**
     * Returns the policy reference data for this policy source model node. The policy reference data are expected to be not {@code null} only in
     * case the type of this node is POLICY_REFERENCE.
     *
     * @return the policy reference data for this policy source model node or {@code null} if the node does not have any policy reference data
     * attached.
     */
    public PolicyReferenceData getPolicyReferenceData() {
        return referenceData;
    }
    
    /**
     * The method may be used to set or replace assertion data set for this node. If there are assertion data set already,
     * those are replaced by a new reference and eventualy returned from the method.
     * <p/>
     * This method is supported only in case this model node instance's type is {@code ASSERTION} or {@code ASSERTION_PARAMETER_NODE}.
     * If used from other node types, an exception is thrown.
     *
     * @param newData new assertion data to be set.
     * @return old and replaced assertion data if any or {@code null} otherwise.
     *
     * @throws UnsupportedOperationException in case this method is called on nodes of type other than {@code ASSERTION}
     * or {@code ASSERTION_PARAMETER_NODE}
     */
    public AssertionData setOrReplaceNodeData(final AssertionData newData) {
        if (!isAssertionRelatedNode()) {
            throw LOGGER.logSevereException(new UnsupportedOperationException(LocalizationMessages.WSP_0051_OPERATION_NOT_SUPPORTED_FOR_THIS_BUT_ASSERTION_RELATED_NODE_TYPE(type)));
        }
        
        final AssertionData oldData = this.nodeData;
        this.nodeData = newData;
        
        return oldData;
    }
    
    /**
     * The method specifies whether the model node instance represents assertion related node, it means whether its type
     * is 'ASSERTION' or 'ASSERTION_PARAMETER_NODE'. This is, for example, the way to determine whether the node supports
     * setting a {@link AssertionData} object via {@link #setOrReplaceNodeData(AssertionData)} method or not.
     *
     * @return {@code true} or {@code false} according to whether the node instance represents assertion related node or not.
     */
    boolean isAssertionRelatedNode() {
        return type == Type.ASSERTION || type == Type.ASSERTION_PARAMETER_NODE;
    }
    
    /**
     * Appends the specified child node to the end of the children list of this node and sets it's parent to reference
     * this node.
     *
     * @param child node to be appended to the children list of this node.
     * @return {@code true} (as per the general contract of the {@code Collection.add} method).
     *
     * @throws NullPointerException if the specified node is {@code null}.
     * @throws IllegalArgumentException if child has a parent node set already to point to some node
     */
    private boolean addChild(final ModelNode child) {
        content.add(child);
        child.parentNode = this;
        
        return true;
    }
    
    /**
     *
     */
    void setReferencedModel(final PolicySourceModel model) {
        if (this.type != Type.POLICY_REFERENCE) {
            throw LOGGER.logSevereException(new UnsupportedOperationException(LocalizationMessages.WSP_0050_OPERATION_NOT_SUPPORTED_FOR_THIS_BUT_POLICY_REFERENCE_NODE_TYPE(type)));
        }
        
        referencedModel = model;
    }
    
    PolicySourceModel getReferencedModel() {
        return referencedModel;
    }
    
    /**
     * Returns the number of child policy source model nodes. If this model node contains
     * more than {@code Integer.MAX_VALUE} children, returns {@code Integer.MAX_VALUE}.
     *
     * @return the number of children of this node.
     */
    public int childrenSize() {
        return content.size();
    }
    
    /**
     * Returns true if the node has at least one child node.
     *
     * @return true if the node has at least one child node, false otherwise.
     */
    public boolean hasChildren() {
        return !content.isEmpty();
    }
    
    /**
     * Iterates through all child nodes.
     *
     * @return An iterator for the child nodes
     */
    public Iterator<ModelNode> iterator() {
        return content.iterator();
    }
    
    /**
     * An {@code Object.equals(Object obj)} method override. Method ignores the parent source model. It means that two
     * model nodes may be the same even if they belong to different models.
     * <p/>
     * If parent model comparison is desired, it must be accomplished separately. To perform that, the reference equality
     * test is sufficient ({@code nodeA.getParentModel() == nodeB.getParentModel()}), since all model nodes are created
     * for specific model instances.
     */
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        
        if (!(obj instanceof ModelNode)) {
            return false;
        }
        
        boolean result = true;
        final ModelNode that = (ModelNode) obj;
        
        result = result && this.type.equals(that.type);
        // result = result && ((this.parentNode == null) ? that.parentNode == null : this.parentNode.equals(that.parentNode));
        result = result && ((this.nodeData == null) ? that.nodeData == null : this.nodeData.equals(that.nodeData));
        result = result && ((this.content == null) ? that.content == null : this.content.equals(that.content));
        
        return result;
    }
    
    /**
     * An {@code Object.hashCode()} method override.
     */
    public int hashCode() {
        int result = 17;
        
        result = 37 * result + this.type.hashCode();
        result = 37 * result + ((this.parentNode == null) ? 0 : this.parentNode.hashCode());
        result = 37 * result + ((this.nodeData == null) ? 0 : this.nodeData.hashCode());
        result = 37 * result + this.content.hashCode();
        
        return result;
    }
    
    /**
     * Returns a string representation of the object. In general, the <code>toString</code> method
     * returns a string that "textually represents" this object.
     *
     * @return  a string representation of the object.
     */
    public String toString() {
        return toString(0, new StringBuffer()).toString();
    }
    
    /**
     * A helper method that appends indented string representation of this instance to the input string buffer.
     *
     * @param indentLevel indentation level to be used.
     * @param buffer buffer to be used for appending string representation of this instance
     * @return modified buffer containing new string representation of the instance
     */
    public StringBuffer toString(final int indentLevel, final StringBuffer buffer) {
        final String indent = PolicyUtils.Text.createIndent(indentLevel);
        final String innerIndent = PolicyUtils.Text.createIndent(indentLevel + 1);
        
        buffer.append(indent).append(type).append(" {").append(PolicyUtils.Text.NEW_LINE);
        if (type == Type.ASSERTION) {
            if (nodeData == null) {
                buffer.append(innerIndent).append("no assertion data set");
            } else {
                nodeData.toString(indentLevel + 1, buffer);
            }
            buffer.append(PolicyUtils.Text.NEW_LINE);
        } else if (type == Type.POLICY_REFERENCE) {
            if (referenceData == null) {
                buffer.append(innerIndent).append("no policy reference data set");
            } else {
                referenceData.toString(indentLevel + 1, buffer);
            }
            buffer.append(PolicyUtils.Text.NEW_LINE);
        }
        
        if (content.size() > 0) {
            for (ModelNode child : content) {
                child.toString(indentLevel + 1, buffer).append(PolicyUtils.Text.NEW_LINE);
            }
        } else {
            buffer.append(innerIndent).append("no child nodes").append(PolicyUtils.Text.NEW_LINE);
        }
        
        buffer.append(indent).append('}');
        return buffer;
    }
    
    protected ModelNode clone() throws CloneNotSupportedException {
        final ModelNode clone = (ModelNode) super.clone();
        
        if (this.nodeData != null) {
            clone.nodeData = this.nodeData.clone();
        }
        
        // no need to clone PolicyReferenceData, since those are immutable
        
        if (this.referencedModel != null) {
            clone.referencedModel = this.referencedModel.clone();
        }
        
        
        clone.content = new LinkedList<ModelNode>();
        clone.unmodifiableViewOnContent = Collections.unmodifiableCollection(clone.content);
        
        for (ModelNode thisChild : this.content) {
            clone.addChild(thisChild.clone());
        }
        
        return clone;
    }
    
    public PolicyReferenceData getReferenceData() {
        return referenceData;
    }
    
}