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