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 java.beans.IndexedPropertyDescriptor;
  18. import java.beans.PropertyDescriptor;
  19. import org.apache.commons.jxpath.JXPathBeanInfo;
  20. import org.apache.commons.jxpath.JXPathContext;
  21. import org.apache.commons.jxpath.JXPathException;
  22. import org.apache.commons.jxpath.ri.model.NodePointer;
  23. import org.apache.commons.jxpath.util.ValueUtils;
  24. /**
  25. * Pointer pointing to a property of a JavaBean.
  26. *
  27. * @author Dmitri Plotnikov
  28. * @version $Revision: 1.17 $ $Date: 2004/04/04 22:06:36 $
  29. */
  30. public class BeanPropertyPointer extends PropertyPointer {
  31. private String propertyName;
  32. private JXPathBeanInfo beanInfo;
  33. private PropertyDescriptor propertyDescriptors[];
  34. private PropertyDescriptor propertyDescriptor;
  35. private String[] names;
  36. private static final Object UNINITIALIZED = new Object();
  37. private Object baseValue = UNINITIALIZED;
  38. private Object value = UNINITIALIZED;
  39. private static final int UNKNOWN_LENGTH_MAX_COUNT = 10000;
  40. public BeanPropertyPointer(NodePointer parent, JXPathBeanInfo beanInfo) {
  41. super(parent);
  42. this.beanInfo = beanInfo;
  43. }
  44. /**
  45. * This type of node is auxiliary.
  46. */
  47. public boolean isContainer() {
  48. return true;
  49. }
  50. /**
  51. * Number of the bean's properties.
  52. */
  53. public int getPropertyCount() {
  54. return getPropertyDescriptors().length;
  55. }
  56. /**
  57. * Names of all properties, sorted alphabetically
  58. */
  59. public String[] getPropertyNames() {
  60. if (names == null) {
  61. PropertyDescriptor pds[] = getPropertyDescriptors();
  62. names = new String[pds.length];
  63. for (int i = 0; i < names.length; i++) {
  64. names[i] = pds[i].getName();
  65. }
  66. }
  67. return names;
  68. }
  69. /**
  70. * Select a property by name
  71. */
  72. public void setPropertyName(String propertyName) {
  73. setPropertyIndex(UNSPECIFIED_PROPERTY);
  74. this.propertyName = propertyName;
  75. }
  76. /**
  77. * Selects a property by its offset in the alphabetically sorted list.
  78. */
  79. public void setPropertyIndex(int index) {
  80. if (propertyIndex != index) {
  81. super.setPropertyIndex(index);
  82. propertyName = null;
  83. propertyDescriptor = null;
  84. baseValue = UNINITIALIZED;
  85. value = UNINITIALIZED;
  86. }
  87. }
  88. /**
  89. * The value of the currently selected property.
  90. */
  91. public Object getBaseValue() {
  92. if (baseValue == UNINITIALIZED) {
  93. PropertyDescriptor pd = getPropertyDescriptor();
  94. if (pd == null) {
  95. return null;
  96. }
  97. baseValue = ValueUtils.getValue(getBean(), pd);
  98. }
  99. return baseValue;
  100. }
  101. public void setIndex(int index) {
  102. if (this.index != index) {
  103. // When dealing with a scalar, index == 0 is equivalent to
  104. // WHOLE_COLLECTION, so do not change it.
  105. if (this.index != WHOLE_COLLECTION
  106. || index != 0
  107. || isCollection()) {
  108. super.setIndex(index);
  109. value = UNINITIALIZED;
  110. }
  111. }
  112. }
  113. /**
  114. * If index == WHOLE_COLLECTION, the value of the property, otherwise
  115. * the value of the index'th element of the collection represented by the
  116. * property. If the property is not a collection, index should be zero
  117. * and the value will be the property itself.
  118. */
  119. public Object getImmediateNode() {
  120. if (value == UNINITIALIZED) {
  121. if (index == WHOLE_COLLECTION) {
  122. value = ValueUtils.getValue(getBaseValue());
  123. }
  124. else {
  125. PropertyDescriptor pd = getPropertyDescriptor();
  126. if (pd == null) {
  127. value = null;
  128. }
  129. else {
  130. value = ValueUtils.getValue(getBean(), pd, index);
  131. }
  132. }
  133. }
  134. return value;
  135. }
  136. protected boolean isActualProperty() {
  137. return getPropertyDescriptor() != null;
  138. }
  139. public boolean isCollection() {
  140. PropertyDescriptor pd = getPropertyDescriptor();
  141. if (pd == null) {
  142. return false;
  143. }
  144. if (pd instanceof IndexedPropertyDescriptor) {
  145. return true;
  146. }
  147. int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
  148. if (hint == -1) {
  149. return false;
  150. }
  151. if (hint == 1) {
  152. return true;
  153. }
  154. Object value = getBaseValue();
  155. return value != null && ValueUtils.isCollection(value);
  156. }
  157. /**
  158. * If the property contains a collection, then the length of that
  159. * collection, otherwise - 1.
  160. */
  161. public int getLength() {
  162. PropertyDescriptor pd = getPropertyDescriptor();
  163. if (pd == null) {
  164. return 1;
  165. }
  166. if (pd instanceof IndexedPropertyDescriptor) {
  167. return ValueUtils.getIndexedPropertyLength(
  168. getBean(),
  169. (IndexedPropertyDescriptor) pd);
  170. }
  171. int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
  172. if (hint == -1) {
  173. return 1;
  174. }
  175. return ValueUtils.getLength(getBaseValue());
  176. }
  177. /**
  178. * If index == WHOLE_COLLECTION, change the value of the property, otherwise
  179. * change the value of the index'th element of the collection
  180. * represented by the property.
  181. */
  182. public void setValue(Object value) {
  183. PropertyDescriptor pd = getPropertyDescriptor();
  184. if (pd == null) {
  185. throw new JXPathException(
  186. "Cannot set property: " + asPath() + " - no such property");
  187. }
  188. if (index == WHOLE_COLLECTION) {
  189. ValueUtils.setValue(getBean(), pd, value);
  190. }
  191. else {
  192. ValueUtils.setValue(getBean(), pd, index, value);
  193. }
  194. this.value = value;
  195. }
  196. /**
  197. * @see PropertyPointer#createPath(JXPathContext)
  198. */
  199. public NodePointer createPath(JXPathContext context) {
  200. if (getImmediateNode() == null) {
  201. super.createPath(context);
  202. baseValue = UNINITIALIZED;
  203. value = UNINITIALIZED;
  204. }
  205. return this;
  206. }
  207. public void remove() {
  208. if (index == WHOLE_COLLECTION) {
  209. setValue(null);
  210. }
  211. else if (isCollection()) {
  212. Object collection = ValueUtils.remove(getBaseValue(), index);
  213. ValueUtils.setValue(getBean(), getPropertyDescriptor(), collection);
  214. }
  215. else if (index == 0) {
  216. index = WHOLE_COLLECTION;
  217. setValue(null);
  218. }
  219. }
  220. /**
  221. * Name of the currently selected property.
  222. */
  223. public String getPropertyName() {
  224. if (propertyName == null) {
  225. PropertyDescriptor pd = getPropertyDescriptor();
  226. if (pd != null) {
  227. propertyName = pd.getName();
  228. }
  229. }
  230. return propertyName != null ? propertyName : "*";
  231. }
  232. /**
  233. * Finds the property descriptor corresponding to the current property
  234. * index.
  235. */
  236. private PropertyDescriptor getPropertyDescriptor() {
  237. if (propertyDescriptor == null) {
  238. int inx = getPropertyIndex();
  239. if (inx == UNSPECIFIED_PROPERTY) {
  240. propertyDescriptor =
  241. beanInfo.getPropertyDescriptor(propertyName);
  242. }
  243. else {
  244. PropertyDescriptor propertyDescriptors[] =
  245. getPropertyDescriptors();
  246. if (inx >= 0 && inx < propertyDescriptors.length) {
  247. propertyDescriptor = propertyDescriptors[inx];
  248. }
  249. else {
  250. propertyDescriptor = null;
  251. }
  252. }
  253. }
  254. return propertyDescriptor;
  255. }
  256. protected PropertyDescriptor[] getPropertyDescriptors() {
  257. if (propertyDescriptors == null) {
  258. propertyDescriptors = beanInfo.getPropertyDescriptors();
  259. }
  260. return propertyDescriptors;
  261. }
  262. }