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