nodepointer.java

来自「JXPath」· Java 代码 · 共 765 行 · 第 1/2 页

JAVA
765
字号
/*
 * Copyright 1999-2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.jxpath.ri.model;

import java.util.Locale;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.ri.Compiler;
import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
import org.apache.commons.jxpath.ri.NamespaceResolver;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
import org.apache.commons.jxpath.ri.compiler.NodeTest;
import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
import org.apache.commons.jxpath.ri.model.beans.NullPointer;

/**
 * Common superclass for Pointers of all kinds.  A NodePointer maps to
 * a deterministic XPath that represents the location of a node in an 
 * object graph. This XPath uses only simple axes: child, namespace and
 * attribute and only simple, context-independent predicates.
 *
 * @author Dmitri Plotnikov
 * @version $Revision: 1.25 $ $Date: 2004/04/01 02:55:32 $
 */
public abstract class NodePointer implements Pointer {

    public static final int WHOLE_COLLECTION = Integer.MIN_VALUE;
    protected int index = WHOLE_COLLECTION;
    public static final String UNKNOWN_NAMESPACE = "<<unknown namespace>>";
    private boolean attribute = false;
    private transient Object rootNode;
    private NamespaceResolver namespaceResolver;
    
    /**
     * Allocates an entirely new NodePointer by iterating through all installed
     * NodePointerFactories until it finds one that can create a pointer.
     */
    public static NodePointer newNodePointer(
        QName name,
        Object bean,
        Locale locale) 
    {
        NodePointer pointer = null;
        if (bean == null) {
            pointer = new NullPointer(name, locale);
            return pointer;
        }
        
        NodePointerFactory[] factories =
            JXPathContextReferenceImpl.getNodePointerFactories();
        for (int i = 0; i < factories.length; i++) {
            pointer = factories[i].createNodePointer(name, bean, locale);
            if (pointer != null) {
                return pointer;
            }
        }
        throw new JXPathException(
            "Could not allocate a NodePointer for object of "
                + bean.getClass());
    }

    /**
     * Allocates an new child NodePointer by iterating through all installed
     * NodePointerFactories until it finds one that can create a pointer.
     */
    public static NodePointer newChildNodePointer(
        NodePointer parent,
        QName name,
        Object bean) 
    {
        NodePointerFactory[] factories =
            JXPathContextReferenceImpl.getNodePointerFactories();
        for (int i = 0; i < factories.length; i++) {
            NodePointer pointer =
                factories[i].createNodePointer(parent, name, bean);
            if (pointer != null) {
                return pointer;
            }
        }
        throw new JXPathException(
            "Could not allocate a NodePointer for object of "
                + bean.getClass());
    }

    protected NodePointer parent;
    protected Locale locale;
//    private NamespaceManager namespaceManager;

    protected NodePointer(NodePointer parent) {
        this.parent = parent;
    }

    protected NodePointer(NodePointer parent, Locale locale) {
        this.parent = parent;
        this.locale = locale;
    }

    public NamespaceResolver getNamespaceResolver() {
        if (namespaceResolver == null && parent != null) {
            namespaceResolver = parent.getNamespaceResolver();
        }
        return namespaceResolver;
    }
    
    public void setNamespaceResolver(NamespaceResolver namespaceResolver) {
        this.namespaceResolver = namespaceResolver;
    }
    
    public NodePointer getParent() {
        NodePointer pointer = parent;
        while (pointer != null && pointer.isContainer()) {
            pointer = pointer.getImmediateParentPointer();
        }
        return pointer;
    }
    
    public NodePointer getImmediateParentPointer() {
        return parent;
    }

    /**
     * Set to true if the pointer represents the "attribute::" axis.
     */
    public void setAttribute(boolean attribute) {
        this.attribute = attribute;
    }

    /**
     * Returns true if the pointer represents the "attribute::" axis.
     */
    public boolean isAttribute() {
        return attribute;
    }

    /**
     * Returns true if this Pointer has no parent.
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * If true, this node does not have children
     */
    public abstract boolean isLeaf();

    /**
     * @deprecated Please use !isContainer()
     */
    public boolean isNode() {
        return !isContainer();
    }
     
    /**
     * If true, this node is axiliary and can only be used as an intermediate in
     * the chain of pointers.
     */
    public boolean isContainer() {
        return false;
    }

    /**
     * If the pointer represents a collection, the index identifies
     * an element of that collection.  The default value of <code>index</code>
     * is <code>WHOLE_COLLECTION</code>, which just means that the pointer
     * is not indexed at all.
     * Note: the index on NodePointer starts with 0, not 1.
     */
    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    /**
     * Returns <code>true</code> if the value of the pointer is an array or
     * a Collection.
     */
    public abstract boolean isCollection();

    /**
     * If the pointer represents a collection (or collection element),
     * returns the length of the collection.
     * Otherwise returns 1 (even if the value is null).
     */
    public abstract int getLength();

    /**
     * By default, returns <code>getNode()</code>, can be overridden to
     * return a "canonical" value, like for instance a DOM element should
     * return its string value.
     */
    public Object getValue() {
        NodePointer valuePointer = getValuePointer();
        if (valuePointer != this) {
            return valuePointer.getValue();
        }
        // Default behavior is to return the same as getNode() 
        return getNode();
    }

    /**
     * If this pointer manages a transparent container, like a variable,
     * this method returns the pointer to the contents.
     * Only an auxiliary (non-node) pointer can (and should) return a
     * value pointer other than itself.
     * Note that you probably don't want to override 
     * <code>getValuePointer()</code> directly.  Override the
     * <code>getImmediateValuePointer()</code> method instead.  The
     * <code>getValuePointer()</code> method is calls
     * <code>getImmediateValuePointer()</code> and, if the result is not
     * <code>this</code>, invokes <code>getValuePointer()</code> recursively.
     * The idea here is to open all nested containers. Let's say we have a
     * container within a container within a container. The
     * <code>getValuePointer()</code> method should then open all those
     * containers and return the pointer to the ultimate contents. It does so
     * with the above recursion.
     */
    public NodePointer getValuePointer() {
        NodePointer ivp = getImmediateValuePointer();
        if (ivp != this) {
            return ivp.getValuePointer();
        }
        return this;
    }

    /**
     * @see #getValuePointer()
     * 
     * @return NodePointer is either <code>this</code> or a pointer
     *   for the immediately contained value.
     */
    public NodePointer getImmediateValuePointer() {
        return this;
    }
    
    /**
     * An actual pointer points to an existing part of an object graph, even
     * if it is null. A non-actual pointer represents a part that does not exist
     * at all.
     * For instance consider the pointer "/address/street".
     * If both <em>address</em> and <em>street</em> are not null,
     * the pointer is actual.
     * If <em>address</em> is not null, but <em>street</em> is null,
     * the pointer is still actual.
     * If <em>address</em> is null, the pointer is not actual.
     * (In JavaBeans) if <em>address</em> is not a property of the root bean,
     * a Pointer for this path cannot be obtained at all - actual or otherwise.
     */
    public boolean isActual() {
        if (index == WHOLE_COLLECTION) {
            return true;
        }
        else {
            return index >= 0 && index < getLength();
        }
    }

    /**
     * Returns the name of this node. Can be null.
     */
    public abstract QName getName();

    /**
     * Returns the value represented by the pointer before indexing.
     * So, if the node represents an element of a collection, this
     * method returns the collection itself.
     */
    public abstract Object getBaseValue();

    /**
     * Returns the object the pointer points to; does not convert it
     * to a "canonical" type.
     *
     * @deprecated 1.1 Please use getNode()
     */
    public Object getNodeValue() {
        return getNode();
    }

    /**
     * Returns the object the pointer points to; does not convert it
     * to a "canonical" type. Opens containers, properties etc and returns
     * the ultimate contents.
     */
    public Object getNode() {
        return getValuePointer().getImmediateNode();
    }
    
    public Object getRootNode() {
        if (rootNode == null) {
            if (parent != null) {
                rootNode = parent.getRootNode();
            }
            else {
                rootNode = getImmediateNode();
            }
        }
        return rootNode;
    }
    
    /**
     * Returns the object the pointer points to; does not convert it
     * to a "canonical" type.
     */
    public abstract Object getImmediateNode();

    /**
     * Converts the value to the required type and changes the corresponding
     * object to that value.
     */
    public abstract void setValue(Object value);

    /**
     * Compares two child NodePointers and returns a positive number,
     * zero or a positive number according to the order of the pointers.
     */
    public abstract int compareChildNodePointers(
            NodePointer pointer1, NodePointer pointer2);

    /**
     * Checks if this Pointer matches the supplied NodeTest.
     */
    public boolean testNode(NodeTest test) {
        if (test == null) {
            return true;
        }
        else if (test instanceof NodeNameTest) {
            if (isContainer()) {
                return false;
            }
            NodeNameTest nodeNameTest = (NodeNameTest) test;
            QName testName = nodeNameTest.getNodeName();
            QName nodeName = getName();
            if (nodeName == null) {
                return false;
            }
            
            String testPrefix = testName.getPrefix();
            String nodePrefix = nodeName.getPrefix();
            if (!equalStrings(testPrefix, nodePrefix)) {
                String testNS = getNamespaceURI(testPrefix);
                String nodeNS = getNamespaceURI(nodePrefix);
                if (!equalStrings(testNS, nodeNS)) {
                    return false;
                }
            }
            if (nodeNameTest.isWildcard()) {
                return true;
            }
            return testName.getName().equals(nodeName.getName());
        }
        else if (test instanceof NodeTypeTest) {
            if (((NodeTypeTest) test).getNodeType()
                == Compiler.NODE_TYPE_NODE) {
                return isNode();
            }
        }
        return false;
    }

    private static boolean equalStrings(String s1, String s2) {
        if (s1 == null && s2 != null) {
            return false;
        }
        if (s1 != null && !s1.equals(s2)) {
            return false;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?