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.dynabeans;
  17. import java.util.Arrays;
  18. import org.apache.commons.beanutils.DynaBean;
  19. import org.apache.commons.beanutils.DynaClass;
  20. import org.apache.commons.beanutils.DynaProperty;
  21. import org.apache.commons.jxpath.JXPathException;
  22. import org.apache.commons.jxpath.ri.model.NodePointer;
  23. import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
  24. import org.apache.commons.jxpath.util.TypeUtils;
  25. import org.apache.commons.jxpath.util.ValueUtils;
  26. /**
  27. * Pointer pointing to a property of a DynaBean.
  28. *
  29. * @author Dmitri Plotnikov
  30. * @version $Revision: 1.12 $ $Date: 2004/04/04 22:06:35 $
  31. */
  32. public class DynaBeanPropertyPointer extends PropertyPointer {
  33. private DynaBean dynaBean;
  34. private String name;
  35. private String[] names;
  36. public DynaBeanPropertyPointer(NodePointer parent, DynaBean dynaBean) {
  37. super(parent);
  38. this.dynaBean = dynaBean;
  39. }
  40. public Object getBaseValue() {
  41. return dynaBean.get(getPropertyName());
  42. }
  43. /**
  44. * This type of node is auxiliary.
  45. */
  46. public boolean isContainer() {
  47. return true;
  48. }
  49. /**
  50. * Number of the DP object's properties.
  51. */
  52. public int getPropertyCount() {
  53. return getPropertyNames().length;
  54. }
  55. /**
  56. * Names of all properties, sorted alphabetically
  57. *
  58. * @todo do something about the sorting
  59. */
  60. public String[] getPropertyNames() {
  61. if (names == null) {
  62. DynaClass dynaClass = dynaBean.getDynaClass();
  63. DynaProperty properties[] = dynaClass.getDynaProperties();
  64. int count = properties.length;
  65. boolean hasClass = dynaClass.getDynaProperty("class") != null;
  66. if (hasClass) {
  67. count--; // Exclude "class" from properties
  68. }
  69. names = new String[count];
  70. for (int i = 0, j = 0; i < properties.length; i++) {
  71. String name = properties[i].getName();
  72. if (!hasClass || !name.equals("class")) {
  73. names[j++] = name;
  74. }
  75. }
  76. Arrays.sort(names);
  77. }
  78. return names;
  79. }
  80. /**
  81. * Returns the name of the currently selected property or "*"
  82. * if none has been selected.
  83. */
  84. public String getPropertyName() {
  85. if (name == null) {
  86. String names[] = getPropertyNames();
  87. if (propertyIndex >= 0 && propertyIndex < names.length) {
  88. name = names[propertyIndex];
  89. }
  90. else {
  91. name = "*";
  92. }
  93. }
  94. return name;
  95. }
  96. /**
  97. * Select a property by name.
  98. */
  99. public void setPropertyName(String propertyName) {
  100. setPropertyIndex(UNSPECIFIED_PROPERTY);
  101. this.name = propertyName;
  102. }
  103. /**
  104. * Index of the currently selected property in the list of all
  105. * properties sorted alphabetically.
  106. */
  107. public int getPropertyIndex() {
  108. if (propertyIndex == UNSPECIFIED_PROPERTY) {
  109. String names[] = getPropertyNames();
  110. for (int i = 0; i < names.length; i++) {
  111. if (names[i].equals(name)) {
  112. propertyIndex = i;
  113. name = null;
  114. break;
  115. }
  116. }
  117. }
  118. return super.getPropertyIndex();
  119. }
  120. /**
  121. * Index a property by its index in the list of all
  122. * properties sorted alphabetically.
  123. */
  124. public void setPropertyIndex(int index) {
  125. if (propertyIndex != index) {
  126. super.setPropertyIndex(index);
  127. name = null;
  128. }
  129. }
  130. /**
  131. * If index == WHOLE_COLLECTION, the value of the property, otherwise
  132. * the value of the index'th element of the collection represented by the
  133. * property. If the property is not a collection, index should be zero
  134. * and the value will be the property itself.
  135. */
  136. public Object getImmediateNode() {
  137. String name = getPropertyName();
  138. if (name.equals("*")) {
  139. return null;
  140. }
  141. Object value;
  142. if (index == WHOLE_COLLECTION) {
  143. value = ValueUtils.getValue(dynaBean.get(name));
  144. }
  145. else if (isIndexedProperty()) {
  146. // DynaClass at this point is not based on whether
  147. // the property is indeed indexed, but rather on
  148. // whether it is an array or List. Therefore
  149. // the indexed set may fail.
  150. try {
  151. value = ValueUtils.getValue(dynaBean.get(name, index));
  152. }
  153. catch (ArrayIndexOutOfBoundsException ex) {
  154. value = null;
  155. }
  156. catch (IllegalArgumentException ex) {
  157. value = dynaBean.get(name);
  158. value = ValueUtils.getValue(value, index);
  159. }
  160. }
  161. else {
  162. value = dynaBean.get(name);
  163. if (ValueUtils.isCollection(value)) {
  164. value = ValueUtils.getValue(value, index);
  165. }
  166. else if (index != 0) {
  167. value = null;
  168. }
  169. }
  170. return value;
  171. }
  172. /**
  173. * Returns true if the bean has the currently selected property
  174. */
  175. protected boolean isActualProperty() {
  176. DynaClass dynaClass = dynaBean.getDynaClass();
  177. return dynaClass.getDynaProperty(getPropertyName()) != null;
  178. }
  179. protected boolean isIndexedProperty() {
  180. DynaClass dynaClass = dynaBean.getDynaClass();
  181. DynaProperty property = dynaClass.getDynaProperty(name);
  182. return property.isIndexed();
  183. }
  184. /**
  185. * If index == WHOLE_COLLECTION, change the value of the property, otherwise
  186. * change the value of the index'th element of the collection
  187. * represented by the property.
  188. */
  189. public void setValue(Object value) {
  190. setValue(index, value);
  191. }
  192. public void remove() {
  193. if (index == WHOLE_COLLECTION) {
  194. dynaBean.set(getPropertyName(), null);
  195. }
  196. else if (isIndexedProperty()) {
  197. dynaBean.set(getPropertyName(), index, null);
  198. }
  199. else if (isCollection()) {
  200. Object collection = ValueUtils.remove(getBaseValue(), index);
  201. dynaBean.set(getPropertyName(), collection);
  202. }
  203. else if (index == 0) {
  204. dynaBean.set(getPropertyName(), null);
  205. }
  206. }
  207. private void setValue(int index, Object value) {
  208. if (index == WHOLE_COLLECTION) {
  209. dynaBean.set(getPropertyName(), convert(value, false));
  210. }
  211. else if (isIndexedProperty()) {
  212. dynaBean.set(getPropertyName(), index, convert(value, true));
  213. }
  214. else {
  215. Object baseValue = dynaBean.get(getPropertyName());
  216. ValueUtils.setValue(baseValue, index, value);
  217. }
  218. }
  219. private Object convert(Object value, boolean element) {
  220. DynaClass dynaClass = (DynaClass) dynaBean.getDynaClass();
  221. DynaProperty property = dynaClass.getDynaProperty(getPropertyName());
  222. Class type = property.getType();
  223. if (element) {
  224. if (type.isArray()) {
  225. type = type.getComponentType();
  226. }
  227. else {
  228. return value; // No need to convert
  229. }
  230. }
  231. try {
  232. return TypeUtils.convert(value, type);
  233. }
  234. catch (Exception ex) {
  235. throw new JXPathException(
  236. "Cannot convert value of class "
  237. + (value == null ? "null" : value.getClass().getName())
  238. + " to type "
  239. + type,
  240. ex);
  241. }
  242. }
  243. }