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}