001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jxpath.ri.model.container;
019
020import java.util.Locale;
021
022import org.apache.commons.jxpath.Container;
023import org.apache.commons.jxpath.ri.QName;
024import org.apache.commons.jxpath.ri.compiler.NodeTest;
025import org.apache.commons.jxpath.ri.model.NodeIterator;
026import org.apache.commons.jxpath.ri.model.NodePointer;
027import org.apache.commons.jxpath.util.ValueUtils;
028
029/**
030 * Transparent pointer to a Container. The {@link #getValue()} method returns the contents of the container, rather than the container itself.
031 */
032public class ContainerPointer extends NodePointer {
033
034    private static final long serialVersionUID = 6140752946621686118L;
035
036    /**
037     * A Container.
038     */
039    private final Container container;
040
041    /**
042     * Supports {@link #getImmediateValuePointer()}.
043     */
044    private NodePointer valuePointer;
045
046    /**
047     * Constructs a new ContainerPointer.
048     *
049     * @param container Container object
050     * @param locale    Locale
051     */
052    public ContainerPointer(final Container container, final Locale locale) {
053        super(null, locale);
054        this.container = container;
055    }
056
057    /**
058     * Constructs a new ContainerPointer.
059     *
060     * @param parent    parent pointer
061     * @param container Container object
062     */
063    public ContainerPointer(final NodePointer parent, final Container container) {
064        super(parent);
065        this.container = container;
066    }
067
068    @Override
069    public String asPath() {
070        return parent == null ? "/" : parent.asPath();
071    }
072
073    @Override
074    public NodeIterator attributeIterator(final QName qName) {
075        return getValuePointer().attributeIterator(qName);
076    }
077
078    @Override
079    public NodeIterator childIterator(final NodeTest test, final boolean reverse, final NodePointer startWith) {
080        return getValuePointer().childIterator(test, reverse, startWith);
081    }
082
083    @Override
084    public int compareChildNodePointers(final NodePointer pointer1, final NodePointer pointer2) {
085        return pointer1.getIndex() - pointer2.getIndex();
086    }
087
088    @Override
089    public boolean equals(final Object object) {
090        if (object == this) {
091            return true;
092        }
093        if (!(object instanceof ContainerPointer)) {
094            return false;
095        }
096        final ContainerPointer other = (ContainerPointer) object;
097        return container == other.container && index == other.index;
098    }
099
100    @Override
101    public Object getBaseValue() {
102        return container;
103    }
104
105    @Override
106    public Object getImmediateNode() {
107        final Object value = getBaseValue();
108        if (index != WHOLE_COLLECTION) {
109            return index >= 0 && index < getLength() ? ValueUtils.getValue(value, index) : null;
110        }
111        return ValueUtils.getValue(value);
112    }
113
114    @Override
115    public NodePointer getImmediateValuePointer() {
116        if (valuePointer == null) {
117            final Object value = getImmediateNode();
118            valuePointer = newChildNodePointer(this, getName(), value);
119        }
120        return valuePointer;
121    }
122
123    @Override
124    public int getLength() {
125        final Object value = getBaseValue();
126        return value == null ? 1 : ValueUtils.getLength(value);
127    }
128
129    @Override
130    public QName getName() {
131        return null;
132    }
133
134    @Override
135    public String getNamespaceURI(final String prefix) {
136        return getValuePointer().getNamespaceURI(prefix);
137    }
138
139    @Override
140    public int hashCode() {
141        return System.identityHashCode(container) + index;
142    }
143
144    @Override
145    public boolean isCollection() {
146        final Object value = getBaseValue();
147        return value != null && ValueUtils.isCollection(value);
148    }
149
150    /**
151     * This type of node is auxiliary.
152     *
153     * @return {@code true}.
154     */
155    @Override
156    public boolean isContainer() {
157        return true;
158    }
159
160    @Override
161    public boolean isLeaf() {
162        return getValuePointer().isLeaf();
163    }
164
165    @Override
166    public NodeIterator namespaceIterator() {
167        return getValuePointer().namespaceIterator();
168    }
169
170    @Override
171    public NodePointer namespacePointer(final String namespace) {
172        return getValuePointer().namespacePointer(namespace);
173    }
174
175    @Override
176    public void setValue(final Object value) {
177        // TODO: what if this is a collection?
178        container.setValue(value);
179    }
180
181    @Override
182    public boolean testNode(final NodeTest nodeTest) {
183        return getValuePointer().testNode(nodeTest);
184    }
185}