1. /*
  2. * Copyright 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.beanutils;
  17. import java.util.List;
  18. import java.util.ArrayList;
  19. import java.util.Map;
  20. import java.util.HashMap;
  21. import java.util.Date;
  22. import java.lang.reflect.Array;
  23. import java.math.BigDecimal;
  24. import java.math.BigInteger;
  25. import java.io.Serializable;
  26. import org.apache.commons.logging.Log;
  27. import org.apache.commons.logging.LogFactory;
  28. /**
  29. * <p>DynaBean which automatically adds properties to the <code>DynaClass</code>
  30. * and provides <i>Lazy List</i> and <i>Lazy Map</i> features.</p>
  31. *
  32. * <p>DynaBeans deal with three types of properties - <i>simple</i>, <i>indexed</i> and <i>mapped</i> and
  33. * have the following <code>get()</code> and <code>set()</code> methods for
  34. * each of these types:</p>
  35. * <ul>
  36. * <li><i>Simple</i> property methods - <code>get(name)</code> and <code>set(name, value)</code></li>
  37. * <li><i>Indexed</i> property methods - <code>get(name, index)</code> and <code>set(name, index, value)</code></li>
  38. * <li><i>Mapped</i> property methods - <code>get(name, key)</code> and <code>set(name, key, value)</code></li>
  39. * </ul>
  40. *
  41. * <p><b><u>Getting Property Values</u></b></p>
  42. * <p>Calling any of the <code>get()</code> methods, for a property which
  43. * doesn't exist, returns <code>null</code> in this implementation.</p>
  44. *
  45. * <p><b><u>Setting Simple Properties</u></b></p>
  46. * <p>The <code>LazyDynaBean</code> will automatically add a property to the <code>DynaClass</code>
  47. * if it doesn't exist when the <code>set(name, value)</code> method is called.</p>
  48. *
  49. * <code>DynaBean myBean = new LazyDynaBean();</code></br>
  50. * <code>myBean.set("myProperty", "myValue");</code></br>
  51. *
  52. * <p><b><u>Setting Indexed Properties</u></b></p>
  53. * <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add
  54. * a property with an <code>ArrayList</code> type to the <code>DynaClass</code> when
  55. * the <code>set(name, index, value)</code> method is called.
  56. * It will also instantiate a new <code>ArrayList</code> and automatically <i>grow</i>
  57. * the <code>List</code> so that it is big enough to accomodate the index being set.
  58. * <code>ArrayList</code> is the default indexed property that LazyDynaBean uses but
  59. * this can be easily changed by overriding the <code>newIndexedProperty(name)</code>
  60. * method.</p>
  61. *
  62. * <code>DynaBean myBean = new LazyDynaBean();</code></br>
  63. * <code>myBean.set("myIndexedProperty", 0, "myValue1");</code></br>
  64. * <code>myBean.set("myIndexedProperty", 1, "myValue2");</code></br>
  65. *
  66. * <p>If the indexed property <b>does</b> exist in the <code>DynaClass</code> but is set to
  67. * <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a
  68. * new <code>List</code> or <code>Array</code> as specified by the property's type
  69. * in the <code>DynaClass</code> and automatically <i>grow</i> the <code>List</code>
  70. * or <code>Array</code> so that it is big enough to accomodate the index being set.</p>
  71. *
  72. * <code>DynaBean myBean = new LazyDynaBean();</code></br>
  73. * <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br>
  74. * <code>myClass.add("myIndexedProperty", int[].class);</code></br>
  75. * <code>myBean.set("myIndexedProperty", 0, new Integer(10));</code></br>
  76. * <code>myBean.set("myIndexedProperty", 1, new Integer(20));</code></br>
  77. *
  78. * <p><b><u>Setting Mapped Properties</u></b></p>
  79. * <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add
  80. * a property with a <code>HashMap</code> type to the <code>DynaClass</code> and
  81. * instantiate a new <code>HashMap</code> in the DynaBean when the
  82. * <code>set(name, key, value)</code> method is called. <code>HashMap</code> is the default
  83. * mapped property that LazyDynaBean uses but this can be easily changed by overriding
  84. * the <code>newMappedProperty(name)</code> method.</p>
  85. *
  86. * <code>DynaBean myBean = new LazyDynaBean();</code></br>
  87. * <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br>
  88. *
  89. * <p>If the mapped property <b>does</b> exist in the <code>DynaClass</code> but is set to
  90. * <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a
  91. * new <code>Map</code> as specified by the property's type in the <code>DynaClass</code>.</p>
  92. *
  93. * <code>DynaBean myBean = new LazyDynaBean();</code></br>
  94. * <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br>
  95. * <code>myClass.add("myMappedProperty", TreeMap.class);</code></br>
  96. * <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br>
  97. *
  98. * <p><b><u><i>Restricted</i> DynaClass</u></b></p>
  99. * <p><code>MutableDynaClass</code> have a facility to <i>restrict</i> the <code>DynaClass</code>
  100. * so that its properties cannot be modified. If the <code>MutableDynaClass</code> is
  101. * restricted then calling any of the <code>set()</code> methods for a property which
  102. * doesn't exist will result in a <code>IllegalArgumentException</code> being thrown.</p>
  103. *
  104. * @see LazyDynaClass
  105. * @author Niall Pemberton
  106. */
  107. public class LazyDynaBean implements DynaBean, Serializable {
  108. /**
  109. * Commons Logging
  110. */
  111. private static Log logger = LogFactory.getLog(LazyDynaBean.class);
  112. protected static final BigInteger BigInteger_ZERO = new BigInteger("0");
  113. protected static final BigDecimal BigDecimal_ZERO = new BigDecimal("0");
  114. protected static final Character Character_SPACE = new Character(' ');
  115. protected static final Byte Byte_ZERO = new Byte((byte)0);
  116. protected static final Short Short_ZERO = new Short((short)0);
  117. protected static final Integer Integer_ZERO = new Integer(0);
  118. protected static final Long Long_ZERO = new Long((long)0);
  119. protected static final Float Float_ZERO = new Float((byte)0);
  120. protected static final Double Double_ZERO = new Double((byte)0);
  121. /**
  122. * The <code>MutableDynaClass</code> "base class" that this DynaBean
  123. * is associated with.
  124. */
  125. protected Map values;
  126. /**
  127. * The <code>MutableDynaClass</code> "base class" that this DynaBean
  128. * is associated with.
  129. */
  130. protected MutableDynaClass dynaClass;
  131. // ------------------- Constructors ----------------------------------
  132. /**
  133. * Construct a new <code>LazyDynaBean</code> with a <code>LazyDynaClass</code> instance.
  134. */
  135. public LazyDynaBean() {
  136. this(new LazyDynaClass());
  137. }
  138. /**
  139. * Construct a new <code>LazyDynaBean</code> with a <code>LazyDynaClass</code> instance.
  140. *
  141. * @param name Name of this DynaBean class
  142. */
  143. public LazyDynaBean(String name) {
  144. this(new LazyDynaClass(name));
  145. }
  146. /**
  147. * Construct a new <code>DynaBean</code> associated with the specified
  148. * <code>DynaClass</code> instance - if its not a <code>MutableDynaClass</code>
  149. * then a new <code>LazyDynaClass</code> is created and the properties copied.
  150. *
  151. * @param dynaClass The DynaClass we are associated with
  152. */
  153. public LazyDynaBean(DynaClass dynaClass) {
  154. values = newMap();
  155. if (dynaClass instanceof MutableDynaClass) {
  156. this.dynaClass = (MutableDynaClass)dynaClass;
  157. } else {
  158. this.dynaClass = new LazyDynaClass(dynaClass.getName(), dynaClass.getDynaProperties());
  159. }
  160. }
  161. // ------------------- Public Methods ----------------------------------
  162. /**
  163. * Return the Map backing this <code>DynaBean</code>
  164. */
  165. public Map getMap() {
  166. return values;
  167. }
  168. /**
  169. * <p>Return the size of an indexed or mapped property.</p>
  170. *
  171. * @param name Name of the property
  172. * @exception IllegalArgumentException if no property name is specified
  173. */
  174. public int size(String name) {
  175. if (name == null) {
  176. throw new IllegalArgumentException("No property name specified");
  177. }
  178. Object value = values.get(name);
  179. if (value == null) {
  180. return 0;
  181. }
  182. if (value instanceof Map) {
  183. return ((Map)value).size();
  184. }
  185. if (value instanceof List) {
  186. return ((List)value).size();
  187. }
  188. if ((value.getClass().isArray())) {
  189. return Array.getLength(value);
  190. }
  191. return 0;
  192. }
  193. // ------------------- DynaBean Methods ----------------------------------
  194. /**
  195. * Does the specified mapped property contain a value for the specified
  196. * key value?
  197. *
  198. * @param name Name of the property to check
  199. * @param key Name of the key to check
  200. *
  201. * @exception IllegalArgumentException if no property name is specified
  202. */
  203. public boolean contains(String name, String key) {
  204. if (name == null) {
  205. throw new IllegalArgumentException("No property name specified");
  206. }
  207. Object value = values.get(name);
  208. if (value == null) {
  209. return false;
  210. }
  211. if (value instanceof Map) {
  212. return (((Map) value).containsKey(key));
  213. }
  214. return false;
  215. }
  216. /**
  217. * <p>Return the value of a simple property with the specified name.</p>
  218. *
  219. * <p><strong>N.B.</strong> Returns <code>null</code> if there is no property
  220. * of the specified name.</p>
  221. *
  222. * @param name Name of the property whose value is to be retrieved.
  223. * @exception IllegalArgumentException if no property name is specified
  224. */
  225. public Object get(String name) {
  226. if (name == null) {
  227. throw new IllegalArgumentException("No property name specified");
  228. }
  229. // Value found
  230. Object value = values.get(name);
  231. if (value != null) {
  232. return value;
  233. }
  234. // Property doesn't exist
  235. if (!isDynaProperty(name)) {
  236. return null;
  237. }
  238. // Property doesn't exist
  239. value = createProperty(name, dynaClass.getDynaProperty(name).getType());
  240. if (value != null) {
  241. set(name, value);
  242. }
  243. return value;
  244. }
  245. /**
  246. * <p>Return the value of an indexed property with the specified name.</p>
  247. *
  248. * <p><strong>N.B.</strong> Returns <code>null</code> if there is no 'indexed'
  249. * property of the specified name.</p>
  250. *
  251. * @param name Name of the property whose value is to be retrieved
  252. * @param index Index of the value to be retrieved
  253. *
  254. * @exception IllegalArgumentException if the specified property
  255. * exists, but is not indexed
  256. * @exception IndexOutOfBoundsException if the specified index
  257. * is outside the range of the underlying property
  258. */
  259. public Object get(String name, int index) {
  260. // If its not a property, then create default indexed property
  261. if (!isDynaProperty(name)) {
  262. set(name, defaultIndexedProperty(name));
  263. }
  264. // Get the indexed property
  265. Object indexedProperty = get(name);
  266. // Check that the property is indexed
  267. if (!dynaClass.getDynaProperty(name).isIndexed()) {
  268. throw new IllegalArgumentException
  269. ("Non-indexed property for '" + name + "[" + index + "]' "
  270. + dynaClass.getDynaProperty(name).getName());
  271. }
  272. // Grow indexed property to appropriate size
  273. indexedProperty = growIndexedProperty(name, indexedProperty, index);
  274. // Return the indexed value
  275. if (indexedProperty.getClass().isArray()) {
  276. return Array.get(indexedProperty, index);
  277. } else if (indexedProperty instanceof List) {
  278. return ((List)indexedProperty).get(index);
  279. } else {
  280. throw new IllegalArgumentException
  281. ("Non-indexed property for '" + name + "[" + index + "]' "
  282. + indexedProperty.getClass().getName());
  283. }
  284. }
  285. /**
  286. * <p>Return the value of a mapped property with the specified name.</p>
  287. *
  288. * <p><strong>N.B.</strong> Returns <code>null</code> if there is no 'mapped'
  289. * property of the specified name.</p>
  290. *
  291. * @param name Name of the property whose value is to be retrieved
  292. * @param key Key of the value to be retrieved
  293. *
  294. * @exception IllegalArgumentException if the specified property
  295. * exists, but is not mapped
  296. */
  297. public Object get(String name, String key) {
  298. // If its not a property, then create default mapped property
  299. if (!isDynaProperty(name)) {
  300. set(name, defaultMappedProperty(name));
  301. }
  302. // Get the mapped property
  303. Object mappedProperty = get(name);
  304. // Check that the property is mapped
  305. if (!dynaClass.getDynaProperty(name).isMapped()) {
  306. throw new IllegalArgumentException
  307. ("Non-mapped property for '" + name + "(" + key + ")' "
  308. + dynaClass.getDynaProperty(name).getType().getName());
  309. }
  310. // Get the value from the Map
  311. if (mappedProperty instanceof Map) {
  312. return (((Map) mappedProperty).get(key));
  313. } else {
  314. throw new IllegalArgumentException
  315. ("Non-mapped property for '" + name + "(" + key + ")'"
  316. + mappedProperty.getClass().getName());
  317. }
  318. }
  319. /**
  320. * Return the <code>DynaClass</code> instance that describes the set of
  321. * properties available for this DynaBean.
  322. */
  323. public DynaClass getDynaClass() {
  324. return (DynaClass)dynaClass;
  325. }
  326. /**
  327. * Remove any existing value for the specified key on the
  328. * specified mapped property.
  329. *
  330. * @param name Name of the property for which a value is to
  331. * be removed
  332. * @param key Key of the value to be removed
  333. *
  334. * @exception IllegalArgumentException if there is no property
  335. * of the specified name
  336. */
  337. public void remove(String name, String key) {
  338. if (name == null) {
  339. throw new IllegalArgumentException("No property name specified");
  340. }
  341. Object value = values.get(name);
  342. if (value == null) {
  343. return;
  344. }
  345. if (value instanceof Map) {
  346. ((Map) value).remove(key);
  347. } else {
  348. throw new IllegalArgumentException
  349. ("Non-mapped property for '" + name + "(" + key + ")'"
  350. + value.getClass().getName());
  351. }
  352. }
  353. /**
  354. * Set the value of a simple property with the specified name.
  355. *
  356. * @param name Name of the property whose value is to be set
  357. * @param value Value to which this property is to be set
  358. *
  359. * @exception IllegalArgumentException if this is not an existing property
  360. * name for our DynaClass and the MutableDynaClass is restricted
  361. * @exception ConversionException if the specified value cannot be
  362. * converted to the type required for this property
  363. * @exception NullPointerException if an attempt is made to set a
  364. * primitive property to null
  365. */
  366. public void set(String name, Object value) {
  367. // If the property doesn't exist, then add it
  368. if (!isDynaProperty(name)) {
  369. if (dynaClass.isRestricted()) {
  370. throw new IllegalArgumentException
  371. ("Invalid property name '" + name + "' (DynaClass is restricted)");
  372. }
  373. if (value == null) {
  374. dynaClass.add(name);
  375. } else {
  376. dynaClass.add(name, value.getClass());
  377. }
  378. }
  379. DynaProperty descriptor = dynaClass.getDynaProperty(name);
  380. if (value == null) {
  381. if (descriptor.getType().isPrimitive()) {
  382. throw new NullPointerException
  383. ("Primitive value for '" + name + "'");
  384. }
  385. } else if (!isAssignable(descriptor.getType(), value.getClass())) {
  386. throw new ConversionException
  387. ("Cannot assign value of type '" +
  388. value.getClass().getName() +
  389. "' to property '" + name + "' of type '" +
  390. descriptor.getType().getName() + "'");
  391. }
  392. // Set the property's value
  393. values.put(name, value);
  394. }
  395. /**
  396. * Set the value of an indexed property with the specified name.
  397. *
  398. * @param name Name of the property whose value is to be set
  399. * @param index Index of the property to be set
  400. * @param value Value to which this property is to be set
  401. *
  402. * @exception ConversionException if the specified value cannot be
  403. * converted to the type required for this property
  404. * @exception IllegalArgumentException if there is no property
  405. * of the specified name
  406. * @exception IllegalArgumentException if the specified property
  407. * exists, but is not indexed
  408. * @exception IndexOutOfBoundsException if the specified index
  409. * is outside the range of the underlying property
  410. */
  411. public void set(String name, int index, Object value) {
  412. // If its not a property, then create default indexed property
  413. if (!isDynaProperty(name)) {
  414. set(name, defaultIndexedProperty(name));
  415. }
  416. // Get the indexed property
  417. Object indexedProperty = get(name);
  418. // Check that the property is indexed
  419. if (!dynaClass.getDynaProperty(name).isIndexed()) {
  420. throw new IllegalArgumentException
  421. ("Non-indexed property for '" + name + "[" + index + "]'"
  422. + dynaClass.getDynaProperty(name).getType().getName());
  423. }
  424. // Grow indexed property to appropriate size
  425. indexedProperty = growIndexedProperty(name, indexedProperty, index);
  426. // Set the value in an array
  427. if (indexedProperty.getClass().isArray()) {
  428. Array.set(indexedProperty, index, value);
  429. } else if (indexedProperty instanceof List) {
  430. ((List)indexedProperty).set(index, value);
  431. } else {
  432. throw new IllegalArgumentException
  433. ("Non-indexed property for '" + name + "[" + index + "]' "
  434. + indexedProperty.getClass().getName());
  435. }
  436. }
  437. /**
  438. * Set the value of a mapped property with the specified name.
  439. *
  440. * @param name Name of the property whose value is to be set
  441. * @param key Key of the property to be set
  442. * @param value Value to which this property is to be set
  443. *
  444. * @exception ConversionException if the specified value cannot be
  445. * converted to the type required for this property
  446. * @exception IllegalArgumentException if there is no property
  447. * of the specified name
  448. * @exception IllegalArgumentException if the specified property
  449. * exists, but is not mapped
  450. */
  451. public void set(String name, String key, Object value) {
  452. // If the 'mapped' property doesn't exist, then add it
  453. if (!isDynaProperty(name)) {
  454. set(name, defaultMappedProperty(name));
  455. }
  456. // Get the mapped property
  457. Object mappedProperty = get(name);
  458. // Check that the property is mapped
  459. if (!dynaClass.getDynaProperty(name).isMapped()) {
  460. throw new IllegalArgumentException
  461. ("Non-mapped property for '" + name + "(" + key + ")'"
  462. + dynaClass.getDynaProperty(name).getType().getName());
  463. }
  464. // Set the value in the Map
  465. ((Map)mappedProperty).put(key, value);
  466. }
  467. // ------------------- protected Methods ----------------------------------
  468. protected Object growIndexedProperty(String name, Object indexedProperty, int index) {
  469. // Grow a List to the appropriate size
  470. if (indexedProperty instanceof List) {
  471. List list = (List)indexedProperty;
  472. while (index >= list.size()) {
  473. list.add(null);
  474. }
  475. }
  476. // Grow an Array to the appropriate size
  477. if ((indexedProperty.getClass().isArray())) {
  478. int length = Array.getLength(indexedProperty);
  479. if (index >= length) {
  480. Class componentType = indexedProperty.getClass().getComponentType();
  481. Object newArray = Array.newInstance(componentType, (index + 1));
  482. System.arraycopy(indexedProperty, 0, newArray, 0, length);
  483. indexedProperty = newArray;
  484. set(name, indexedProperty);
  485. int newLength = Array.getLength(indexedProperty);
  486. for (int i = length; i < newLength; i++) {
  487. Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
  488. }
  489. }
  490. }
  491. return indexedProperty;
  492. }
  493. /**
  494. * Create a new Instance of a Property
  495. */
  496. protected Object createProperty(String name, Class type) {
  497. // Create Lists, arrays or DynaBeans
  498. if (type.isArray() || List.class.isAssignableFrom(type)) {
  499. return createIndexedProperty(name, type);
  500. }
  501. if (Map.class.isAssignableFrom(type)) {
  502. return createMappedProperty(name, type);
  503. }
  504. if (DynaBean.class.isAssignableFrom(type)) {
  505. return createDynaBeanProperty(name, type);
  506. }
  507. if (type.isPrimitive()) {
  508. return createPrimitiveProperty(name, type);
  509. }
  510. if (Number.class.isAssignableFrom(type)) {
  511. return createNumberProperty(name, type);
  512. }
  513. return createOtherProperty(name, type);
  514. }
  515. /**
  516. * Create a new Instance of an 'Indexed' Property
  517. */
  518. protected Object createIndexedProperty(String name, Class type) {
  519. // Create the indexed object
  520. Object indexedProperty = null;
  521. if (type == null) {
  522. indexedProperty = defaultIndexedProperty(name);
  523. } else if (type.isArray()) {
  524. indexedProperty = Array.newInstance(type.getComponentType(), 0);
  525. } else if (List.class.isAssignableFrom(type)) {
  526. if (type.isInterface()) {
  527. indexedProperty = defaultIndexedProperty(name);
  528. } else {
  529. try {
  530. indexedProperty = type.newInstance();
  531. }
  532. catch (Exception ex) {
  533. throw new IllegalArgumentException
  534. ("Error instantiating indexed property of type '" +
  535. type.getName() + "' for '" + name + "' " + ex);
  536. }
  537. }
  538. } else {
  539. throw new IllegalArgumentException
  540. ("Non-indexed property of type '" + type.getName() + "' for '" + name + "'");
  541. }
  542. return indexedProperty;
  543. }
  544. /**
  545. * Create a new Instance of a 'Mapped' Property
  546. */
  547. protected Object createMappedProperty(String name, Class type) {
  548. // Create the mapped object
  549. Object mappedProperty = null;
  550. if (type == null) {
  551. mappedProperty = defaultMappedProperty(name);
  552. } else if (type.isInterface()) {
  553. mappedProperty = defaultMappedProperty(name);
  554. } else if (Map.class.isAssignableFrom(type)) {
  555. try {
  556. mappedProperty = type.newInstance();
  557. }
  558. catch (Exception ex) {
  559. throw new IllegalArgumentException
  560. ("Error instantiating mapped property of type '" + type.getName() + "' for '" + name + "' " + ex);
  561. }
  562. } else {
  563. throw new IllegalArgumentException
  564. ("Non-mapped property of type '" + type.getName() + "' for '" + name + "'");
  565. }
  566. return mappedProperty;
  567. }
  568. /**
  569. * Create a new Instance of a 'Mapped' Property
  570. */
  571. protected Object createDynaBeanProperty(String name, Class type) {
  572. try {
  573. return type.newInstance();
  574. }
  575. catch (Exception ex) {
  576. if (logger.isWarnEnabled()) {
  577. logger.warn("Error instantiating DynaBean property of type '" + type.getName() + "' for '" + name + "' " + ex);
  578. }
  579. return null;
  580. }
  581. }
  582. /**
  583. * Create a new Instance of a 'Primitive' Property
  584. */
  585. protected Object createPrimitiveProperty(String name, Class type) {
  586. if (type == Boolean.TYPE) {
  587. return Boolean.FALSE;
  588. } else if (type == Integer.TYPE) {
  589. return Integer_ZERO;
  590. } else if (type == Long.TYPE) {
  591. return Long_ZERO;
  592. } else if (type == Double.TYPE) {
  593. return Double_ZERO;
  594. } else if (type == Float.TYPE) {
  595. return Float_ZERO;
  596. } else if (type == Byte.TYPE) {
  597. return Byte_ZERO;
  598. } else if (type == Short.TYPE) {
  599. return Short_ZERO;
  600. } else if (type == Character.TYPE) {
  601. return Character_SPACE;
  602. } else {
  603. return null;
  604. }
  605. }
  606. /**
  607. * Create a new Instance of a 'Primitive' Property
  608. */
  609. protected Object createNumberProperty(String name, Class type) {
  610. return null;
  611. }
  612. /**
  613. * Create a new Instance of a 'Mapped' Property
  614. */
  615. protected Object createOtherProperty(String name, Class type) {
  616. if (type == String.class || type == Boolean.class ||
  617. type == Character.class || Date.class.isAssignableFrom(type)) {
  618. return null;
  619. }
  620. try {
  621. return type.newInstance();
  622. }
  623. catch (Exception ex) {
  624. if (logger.isWarnEnabled()) {
  625. logger.warn("Error instantiating property of type '" + type.getName() + "' for '" + name + "' " + ex);
  626. }
  627. return null;
  628. }
  629. }
  630. /**
  631. * <p>Creates a new <code>ArrayList</code> for an 'indexed' property
  632. * which doesn't exist.</p>
  633. *
  634. * <p>This method shouls be overriden if an alternative <code>List</code>
  635. * or <code>Array</code> implementation is required for 'indexed' properties.</p>
  636. *
  637. * @param name Name of the 'indexed property.
  638. */
  639. protected Object defaultIndexedProperty(String name) {
  640. return new ArrayList();
  641. }
  642. /**
  643. * <p>Creates a new <code>HashMap</code> for a 'mapped' property
  644. * which doesn't exist.</p>
  645. *
  646. * <p>This method can be overriden if an alternative <code>Map</code>
  647. * implementation is required for 'mapped' properties.</p>
  648. *
  649. * @param name Name of the 'mapped property.
  650. */
  651. protected Map defaultMappedProperty(String name) {
  652. return new HashMap();
  653. }
  654. /**
  655. * Indicates if there is a property with the specified name.
  656. */
  657. protected boolean isDynaProperty(String name) {
  658. if (name == null) {
  659. throw new IllegalArgumentException("No property name specified");
  660. }
  661. // Handle LazyDynaClasses
  662. if (dynaClass instanceof LazyDynaClass) {
  663. return ((LazyDynaClass)dynaClass).isDynaProperty(name);
  664. }
  665. // Handle other MutableDynaClass
  666. return dynaClass.getDynaProperty(name) == null ? false : true;
  667. }
  668. /**
  669. * Is an object of the source class assignable to the destination class?
  670. *
  671. * @param dest Destination class
  672. * @param source Source class
  673. */
  674. protected boolean isAssignable(Class dest, Class source) {
  675. if (dest.isAssignableFrom(source) ||
  676. ((dest == Boolean.TYPE) && (source == Boolean.class)) ||
  677. ((dest == Byte.TYPE) && (source == Byte.class)) ||
  678. ((dest == Character.TYPE) && (source == Character.class)) ||
  679. ((dest == Double.TYPE) && (source == Double.class)) ||
  680. ((dest == Float.TYPE) && (source == Float.class)) ||
  681. ((dest == Integer.TYPE) && (source == Integer.class)) ||
  682. ((dest == Long.TYPE) && (source == Long.class)) ||
  683. ((dest == Short.TYPE) && (source == Short.class))) {
  684. return (true);
  685. } else {
  686. return (false);
  687. }
  688. }
  689. /**
  690. * <p>Creates a new instance of the <code>Map</code>.</p>
  691. */
  692. protected Map newMap() {
  693. return new HashMap();
  694. }
  695. }