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    package org.apache.commons.jxpath.ri.model.beans;
018    
019    import java.util.Locale;
020    
021    import org.apache.commons.jxpath.JXPathContext;
022    import org.apache.commons.jxpath.JXPathIntrospector;
023    import org.apache.commons.jxpath.ri.Compiler;
024    import org.apache.commons.jxpath.ri.QName;
025    import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
026    import org.apache.commons.jxpath.ri.compiler.NodeTest;
027    import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
028    import org.apache.commons.jxpath.ri.model.NodeIterator;
029    import org.apache.commons.jxpath.ri.model.NodePointer;
030    import 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 16:59:48 -0500 (Mon, 16 Jun 2008) $
037     */
038    public 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    }