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}