    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.beans;
    019
    020import java.util.Locale;
    021
    022import org.apache.commons.jxpath.JXPathContext;
    023import org.apache.commons.jxpath.JXPathIntrospector;
    024import org.apache.commons.jxpath.ri.Compiler;
    025import org.apache.commons.jxpath.ri.QName;
    026import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
    027import org.apache.commons.jxpath.ri.compiler.NodeTest;
    028import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
    029import org.apache.commons.jxpath.ri.model.NodeIterator;
    030import org.apache.commons.jxpath.ri.model.NodePointer;
    031import org.apache.commons.jxpath.util.ValueUtils;
    032
    033/**
    034 * Transparent pointer to a collection (array or Collection).
    035 */
    036public class CollectionPointer extends NodePointer {
    037
    038    private static final long serialVersionUID = 8620254915563256588L;
    039
    040    /**
    041     * The collection itself supporting {@link #getBaseValue()}.
    042     */
    043    private Object collection;
    044
    045    /**
    046     * Supports {@link #getValuePointer()}.
    047     */
    048    private NodePointer valuePointer;
    049
    050    /**
    051     * Constructs a new CollectionPointer.
    052     *
    053     * @param parent     parent NodePointer
    054     * @param collection value
    055     */
    056    public CollectionPointer(final NodePointer parent, final Object collection) {
    057        super(parent);
    058        this.collection = collection;
    059    }
    060
    061    /**
    062     * Constructs a new CollectionPointer.
    063     *
    064     * @param collection value
    065     * @param locale     Locale
    066     */
    067    public CollectionPointer(final Object collection, final Locale locale) {
    068        super(null, locale);
    069        this.collection = collection;
    070    }
    071
    072    @Override
    073    public String asPath() {
    074        final StringBuilder buffer = new StringBuilder();
    075        final NodePointer parent = getImmediateParentPointer();
    076        if (parent != null) {
    077            buffer.append(parent.asPath());
    078            if (index != WHOLE_COLLECTION) {
    079                // Address the list[1][2] case
    080                if (parent.getIndex() != WHOLE_COLLECTION) {
    081                    buffer.append("/.");
    082                }
    083                buffer.append("[").append(index + 1).append(']');
    084            }
    085        } else if (index != WHOLE_COLLECTION) {
    086            buffer.append("/.[").append(index + 1).append(']');
    087        } else {
    088            buffer.append("/");
    089        }
    090        return buffer.toString();
    091    }
    092
    093    @Override
    094    public NodeIterator attributeIterator(final QName qName) {
    095        return index == WHOLE_COLLECTION ? new CollectionAttributeNodeIterator(this, qName) : getValuePointer().attributeIterator(qName);
    096    }
    097
    098    @Override
    099    public NodeIterator childIterator(final NodeTest test, final boolean reverse, final NodePointer startWith) {
    100        if (index == WHOLE_COLLECTION) {
    101            return new CollectionChildNodeIterator(this, test, reverse, startWith);
    102        }
    103        return getValuePointer().childIterator(test, reverse, startWith);
    104    }
    105
    106    @Override
    107    public int compareChildNodePointers(final NodePointer pointer1, final NodePointer pointer2) {
    108        return pointer1.getIndex() - pointer2.getIndex();
    109    }
    110
    111    @Override
    112    public NodePointer createChild(final JXPathContext context, final QName qName, final int index) {
    113        final NodePointer ptr = (NodePointer) clone();
    114        ptr.setIndex(index);
    115        return ptr.createPath(context);
    116    }
    117
    118    @Override
    119    public NodePointer createChild(final JXPathContext context, final QName qName, final int index, final Object value) {
    120        final NodePointer ptr = (NodePointer) clone();
    121        ptr.setIndex(index);
    122        return ptr.createPath(context, value);
    123    }
    124
    125    @Override
    126    public NodePointer createPath(final JXPathContext context) {
    127        if (ValueUtils.getLength(getBaseValue()) <= index) {
    128            collection = ValueUtils.expandCollection(getNode(), index + 1);
    129        }
    130        return this;
    131    }
    132
    133    @Override
    134    public NodePointer createPath(final JXPathContext context, final Object value) {
    135        final NodePointer ptr = createPath(context);
    136        ptr.setValue(value);
    137        return ptr;
    138    }
    139
    140    @Override
    141    public boolean equals(final Object object) {
    142        if (object == this) {
    143            return true;
    144        }
    145        if (!(object instanceof CollectionPointer)) {
    146            return false;
    147        }
    148        final CollectionPointer other = (CollectionPointer) object;
    149        return collection == other.collection && index == other.index;
    150    }
    151
    152    @Override
    153    public Object getBaseValue() {
    154        return collection;
    155    }
    156
    157    @Override
    158    public Object getImmediateNode() {
    159        return index == WHOLE_COLLECTION ? ValueUtils.getValue(collection) : ValueUtils.getValue(collection, index);
    160    }
    161
    162    @Override
    163    public int getLength() {
    164        return ValueUtils.getLength(getBaseValue());
    165    }
    166
    167    @Override
    168    public QName getName() {
    169        return null;
    170    }
    171
    172    @Override
    173    public NodePointer getValuePointer() {
    174        if (valuePointer == null) {
    175            if (index == WHOLE_COLLECTION) {
    176                valuePointer = this;
    177            } else {
    178                final Object value = getImmediateNode();
    179                valuePointer = newChildNodePointer(this, getName(), value);
    180            }
    181        }
    182        return valuePointer;
    183    }
    184
    185    @Override
    186    public int hashCode() {
    187        return System.identityHashCode(collection) + index;
    188    }
    189
    190    @Override
    191    public boolean isCollection() {
    192        return true;
    193    }
    194
    195    @Override
    196    public boolean isContainer() {
    197        return index != WHOLE_COLLECTION;
    198    }
    199
    200    @Override
    201    public boolean isLeaf() {
    202        final Object value = getNode();
    203        return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
    204    }
    205
    206    @Override
    207    public NodeIterator namespaceIterator() {
    208        return index == WHOLE_COLLECTION ? null : getValuePointer().namespaceIterator();
    209    }
    210
    211    @Override
    212    public NodePointer namespacePointer(final String namespace) {
    213        return index == WHOLE_COLLECTION ? null : getValuePointer().namespacePointer(namespace);
    214    }
    215
    216    @Override
    217    public void setIndex(final int index) {
    218        super.setIndex(index);
    219        valuePointer = null;
    220    }
    221
    222    @Override
    223    public void setValue(final Object value) {
    224        if (index == WHOLE_COLLECTION) {
    225            parent.setValue(value);
    226        } else {
    227            ValueUtils.setValue(collection, index, value);
    228        }
    229    }
    230
    231    @Override
    232    public boolean testNode(final NodeTest test) {
    233        if (index == WHOLE_COLLECTION) {
    234            if (test == null) {
    235                return true;
    236            }
    237            if (test instanceof NodeNameTest) {
    238                return false;
    239            }
    240            return test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE;
    241        }
    242        return getValuePointer().testNode(test);
    243    }
    244}