1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.jxpath.ri.model.beans;
  17. import org.apache.commons.jxpath.AbstractFactory;
  18. import org.apache.commons.jxpath.JXPathContext;
  19. import org.apache.commons.jxpath.JXPathException;
  20. import org.apache.commons.jxpath.JXPathIntrospector;
  21. import org.apache.commons.jxpath.ri.QName;
  22. import org.apache.commons.jxpath.ri.model.NodePointer;
  23. import org.apache.commons.jxpath.util.ValueUtils;
  24. /**
  25. * A pointer allocated by a PropertyOwnerPointer to represent the value of
  26. * a property of the parent object.
  27. *
  28. * @author Dmitri Plotnikov
  29. * @version $Revision: 1.14 $ $Date: 2004/04/04 22:06:36 $
  30. */
  31. public abstract class PropertyPointer extends NodePointer {
  32. public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE;
  33. protected int propertyIndex = UNSPECIFIED_PROPERTY;
  34. protected Object bean;
  35. /**
  36. * Takes a javabean, a descriptor of a property of that object and
  37. * an offset within that property (starting with 0).
  38. */
  39. public PropertyPointer(NodePointer parent) {
  40. super(parent);
  41. }
  42. public int getPropertyIndex() {
  43. return propertyIndex;
  44. }
  45. public void setPropertyIndex(int index) {
  46. if (propertyIndex != index) {
  47. propertyIndex = index;
  48. setIndex(WHOLE_COLLECTION);
  49. }
  50. }
  51. public Object getBean() {
  52. if (bean == null) {
  53. bean = getImmediateParentPointer().getNode();
  54. }
  55. return bean;
  56. }
  57. public QName getName() {
  58. return new QName(null, getPropertyName());
  59. }
  60. public abstract String getPropertyName();
  61. public abstract void setPropertyName(String propertyName);
  62. public abstract int getPropertyCount();
  63. public abstract String[] getPropertyNames();
  64. protected abstract boolean isActualProperty();
  65. public boolean isActual() {
  66. if (!isActualProperty()) {
  67. return false;
  68. }
  69. return super.isActual();
  70. }
  71. private static final Object UNINITIALIZED = new Object();
  72. private Object value = UNINITIALIZED;
  73. public Object getImmediateNode() {
  74. if (value == UNINITIALIZED) {
  75. if (index == WHOLE_COLLECTION) {
  76. value = ValueUtils.getValue(getBaseValue());
  77. }
  78. else {
  79. value = ValueUtils.getValue(getBaseValue(), index);
  80. }
  81. }
  82. return value;
  83. }
  84. public boolean isCollection() {
  85. Object value = getBaseValue();
  86. return value != null && ValueUtils.isCollection(value);
  87. }
  88. public boolean isLeaf() {
  89. Object value = getNode();
  90. return value == null
  91. || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
  92. }
  93. /**
  94. * If the property contains a collection, then the length of that
  95. * collection, otherwise - 1.
  96. */
  97. public int getLength() {
  98. return ValueUtils.getLength(getBaseValue());
  99. }
  100. /**
  101. * Returns a NodePointer that can be used to access the currently
  102. * selected property value.
  103. */
  104. public NodePointer getImmediateValuePointer() {
  105. return NodePointer.newChildNodePointer(
  106. this,
  107. getName(),
  108. getImmediateNode());
  109. }
  110. public NodePointer createPath(JXPathContext context) {
  111. if (getImmediateNode() == null) {
  112. AbstractFactory factory = getAbstractFactory(context);
  113. int inx = (index == WHOLE_COLLECTION ? 0 : index);
  114. boolean success =
  115. factory.createObject(
  116. context,
  117. this,
  118. getBean(),
  119. getPropertyName(),
  120. inx);
  121. if (!success) {
  122. throw new JXPathException(
  123. "Factory "
  124. + factory
  125. + " could not create an object for path: "
  126. + asPath());
  127. }
  128. }
  129. return this;
  130. }
  131. public NodePointer createPath(JXPathContext context, Object value) {
  132. // If neccessary, expand collection
  133. if (index != WHOLE_COLLECTION && index >= getLength()) {
  134. createPath(context);
  135. }
  136. setValue(value);
  137. return this;
  138. }
  139. public NodePointer createChild(
  140. JXPathContext context,
  141. QName name,
  142. int index,
  143. Object value)
  144. {
  145. PropertyPointer prop = (PropertyPointer) clone();
  146. if (name != null) {
  147. prop.setPropertyName(name.toString());
  148. }
  149. prop.setIndex(index);
  150. return prop.createPath(context, value);
  151. }
  152. public NodePointer createChild(
  153. JXPathContext context,
  154. QName name,
  155. int index)
  156. {
  157. PropertyPointer prop = (PropertyPointer) clone();
  158. if (name != null) {
  159. prop.setPropertyName(name.toString());
  160. }
  161. prop.setIndex(index);
  162. return prop.createPath(context);
  163. }
  164. public int hashCode() {
  165. return getImmediateParentPointer().hashCode() + propertyIndex + index;
  166. }
  167. public boolean equals(Object object) {
  168. if (object == this) {
  169. return true;
  170. }
  171. if (!(object instanceof PropertyPointer)) {
  172. return false;
  173. }
  174. PropertyPointer other = (PropertyPointer) object;
  175. if (parent != other.parent) {
  176. if (parent == null || !parent.equals(other.parent)) {
  177. return false;
  178. }
  179. }
  180. if (getPropertyIndex() != other.getPropertyIndex()
  181. || !getPropertyName().equals(other.getPropertyName())) {
  182. return false;
  183. }
  184. int iThis = (index == WHOLE_COLLECTION ? 0 : index);
  185. int iOther = (other.index == WHOLE_COLLECTION ? 0 : other.index);
  186. return iThis == iOther;
  187. }
  188. public int compareChildNodePointers(
  189. NodePointer pointer1,
  190. NodePointer pointer2)
  191. {
  192. return getValuePointer().compareChildNodePointers(pointer1, pointer2);
  193. }
  194. private AbstractFactory getAbstractFactory(JXPathContext context) {
  195. AbstractFactory factory = context.getFactory();
  196. if (factory == null) {
  197. throw new JXPathException(
  198. "Factory is not set on the "
  199. + "JXPathContext - cannot create path: "
  200. + asPath());
  201. }
  202. return factory;
  203. }
  204. }