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.util;
  17. import java.lang.reflect.Array;
  18. import java.lang.reflect.Modifier;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashSet;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.Set;
  26. import org.apache.commons.beanutils.ConvertUtils;
  27. import org.apache.commons.beanutils.Converter;
  28. import org.apache.commons.jxpath.JXPathException;
  29. import org.apache.commons.jxpath.NodeSet;
  30. import org.apache.commons.jxpath.Pointer;
  31. /**
  32. * The default implementation of TypeConverter.
  33. *
  34. * @author Dmitri Plotnikov
  35. * @version $Revision: 1.15 $ $Date: 2004/07/25 13:16:04 $
  36. */
  37. public class BasicTypeConverter implements TypeConverter {
  38. /**
  39. * Returns true if it can convert the supplied
  40. * object to the specified class.
  41. */
  42. public boolean canConvert(Object object, Class toType) {
  43. if (object == null) {
  44. return true;
  45. }
  46. if (toType == Object.class) {
  47. return true;
  48. }
  49. Class fromType = object.getClass();
  50. if (fromType.equals(toType)) {
  51. return true;
  52. }
  53. if (toType.isAssignableFrom(fromType)) {
  54. return true;
  55. }
  56. if (toType == String.class) {
  57. return true;
  58. }
  59. if (object instanceof Boolean) {
  60. if (toType == boolean.class
  61. || Number.class.isAssignableFrom(toType)) {
  62. return true;
  63. }
  64. }
  65. else if (object instanceof Number) {
  66. if (toType.isPrimitive()
  67. || Number.class.isAssignableFrom(toType)) {
  68. return true;
  69. }
  70. }
  71. else if (object instanceof Character) {
  72. if (toType == char.class) {
  73. return true;
  74. }
  75. }
  76. else if (object instanceof String) {
  77. if (toType.isPrimitive()) {
  78. return true;
  79. }
  80. if (toType == Boolean.class
  81. || toType == Character.class
  82. || toType == Byte.class
  83. || toType == Short.class
  84. || toType == Integer.class
  85. || toType == Long.class
  86. || toType == Float.class
  87. || toType == Double.class) {
  88. return true;
  89. }
  90. }
  91. else if (fromType.isArray()) {
  92. // Collection -> array
  93. if (toType.isArray()) {
  94. Class cType = toType.getComponentType();
  95. int length = Array.getLength(object);
  96. for (int i = 0; i < length; i++) {
  97. Object value = Array.get(object, i);
  98. if (!canConvert(value, cType)) {
  99. return false;
  100. }
  101. }
  102. return true;
  103. }
  104. else if (Collection.class.isAssignableFrom(toType)) {
  105. return canCreateCollection(toType);
  106. }
  107. else {
  108. if (Array.getLength(object) > 0) {
  109. Object value = Array.get(object, 0);
  110. return canConvert(value, toType);
  111. }
  112. else {
  113. return canConvert("", toType);
  114. }
  115. }
  116. }
  117. else if (object instanceof Collection) {
  118. // Collection -> array
  119. if (toType.isArray()) {
  120. Class cType = toType.getComponentType();
  121. Iterator it = ((Collection) object).iterator();
  122. while (it.hasNext()) {
  123. Object value = it.next();
  124. if (!canConvert(value, cType)) {
  125. return false;
  126. }
  127. }
  128. return true;
  129. }
  130. else if (Collection.class.isAssignableFrom(toType)) {
  131. return canCreateCollection(toType);
  132. }
  133. else {
  134. if (((Collection) object).size() > 0) {
  135. Object value;
  136. if (object instanceof List) {
  137. value = ((List) object).get(0);
  138. }
  139. else {
  140. Iterator it = ((Collection) object).iterator();
  141. value = it.next();
  142. }
  143. return canConvert(value, toType);
  144. }
  145. else {
  146. return canConvert("", toType);
  147. }
  148. }
  149. }
  150. else if (object instanceof NodeSet) {
  151. return canConvert(((NodeSet) object).getValues(), toType);
  152. }
  153. else if (object instanceof Pointer) {
  154. return canConvert(((Pointer) object).getValue(), toType);
  155. }
  156. return ConvertUtils.lookup(toType) != null;
  157. }
  158. /**
  159. * Converts the supplied object to the specified
  160. * type. Throws a runtime exception if the conversion is
  161. * not possible.
  162. */
  163. public Object convert(Object object, Class toType) {
  164. if (object == null) {
  165. if (toType.isPrimitive()) {
  166. return convertNullToPrimitive(toType);
  167. }
  168. return null;
  169. }
  170. if (toType == Object.class) {
  171. if (object instanceof NodeSet) {
  172. return convert(((NodeSet) object).getValues(), toType);
  173. }
  174. else if (object instanceof Pointer) {
  175. return convert(((Pointer) object).getValue(), toType);
  176. }
  177. return object;
  178. }
  179. Class fromType = object.getClass();
  180. if (fromType.equals(toType) || toType.isAssignableFrom(fromType)) {
  181. return object;
  182. }
  183. if (fromType.isArray()) {
  184. int length = Array.getLength(object);
  185. if (toType.isArray()) {
  186. Class cType = toType.getComponentType();
  187. Object array = Array.newInstance(cType, length);
  188. for (int i = 0; i < length; i++) {
  189. Object value = Array.get(object, i);
  190. Array.set(array, i, convert(value, cType));
  191. }
  192. return array;
  193. }
  194. else if (Collection.class.isAssignableFrom(toType)) {
  195. Collection collection = allocateCollection(toType);
  196. for (int i = 0; i < length; i++) {
  197. collection.add(Array.get(object, i));
  198. }
  199. return unmodifiableCollection(collection);
  200. }
  201. else {
  202. if (length > 0) {
  203. Object value = Array.get(object, 0);
  204. return convert(value, toType);
  205. }
  206. else {
  207. return convert("", toType);
  208. }
  209. }
  210. }
  211. else if (object instanceof Collection) {
  212. int length = ((Collection) object).size();
  213. if (toType.isArray()) {
  214. Class cType = toType.getComponentType();
  215. Object array = Array.newInstance(cType, length);
  216. Iterator it = ((Collection) object).iterator();
  217. for (int i = 0; i < length; i++) {
  218. Object value = it.next();
  219. Array.set(array, i, convert(value, cType));
  220. }
  221. return array;
  222. }
  223. else if (Collection.class.isAssignableFrom(toType)) {
  224. Collection collection = allocateCollection(toType);
  225. collection.addAll((Collection) object);
  226. return unmodifiableCollection(collection);
  227. }
  228. else {
  229. if (length > 0) {
  230. Object value;
  231. if (object instanceof List) {
  232. value = ((List) object).get(0);
  233. }
  234. else {
  235. Iterator it = ((Collection) object).iterator();
  236. value = it.next();
  237. }
  238. return convert(value, toType);
  239. }
  240. else {
  241. return convert("", toType);
  242. }
  243. }
  244. }
  245. else if (object instanceof NodeSet) {
  246. return convert(((NodeSet) object).getValues(), toType);
  247. }
  248. else if (object instanceof Pointer) {
  249. return convert(((Pointer) object).getValue(), toType);
  250. }
  251. else if (toType == String.class) {
  252. return object.toString();
  253. }
  254. else if (object instanceof Boolean) {
  255. if (toType == boolean.class) {
  256. return object;
  257. }
  258. boolean value = ((Boolean) object).booleanValue();
  259. return allocateNumber(toType, value ? 1 : 0);
  260. }
  261. else if (object instanceof Number) {
  262. double value = ((Number) object).doubleValue();
  263. if (toType == boolean.class || toType == Boolean.class) {
  264. return value == 0.0 ? Boolean.FALSE : Boolean.TRUE;
  265. }
  266. if (toType.isPrimitive()
  267. || Number.class.isAssignableFrom(toType)) {
  268. return allocateNumber(toType, value);
  269. }
  270. }
  271. else if (object instanceof Character) {
  272. if (toType == char.class) {
  273. return object;
  274. }
  275. }
  276. else if (object instanceof String) {
  277. Object value = convertStringToPrimitive(object, toType);
  278. if (value != null) {
  279. return value;
  280. }
  281. }
  282. Converter converter = ConvertUtils.lookup(toType);
  283. if (converter != null) {
  284. return converter.convert(toType, object);
  285. }
  286. throw new RuntimeException(
  287. "Cannot convert " + object.getClass() + " to " + toType);
  288. }
  289. protected Object convertNullToPrimitive(Class toType) {
  290. if (toType == boolean.class) {
  291. return Boolean.FALSE;
  292. }
  293. if (toType == char.class) {
  294. return new Character('\0');
  295. }
  296. if (toType == byte.class) {
  297. return new Byte((byte) 0);
  298. }
  299. if (toType == short.class) {
  300. return new Short((short) 0);
  301. }
  302. if (toType == int.class) {
  303. return new Integer(0);
  304. }
  305. if (toType == long.class) {
  306. return new Long(0L);
  307. }
  308. if (toType == float.class) {
  309. return new Float(0.0f);
  310. }
  311. if (toType == double.class) {
  312. return new Double(0.0);
  313. }
  314. return null;
  315. }
  316. protected Object convertStringToPrimitive(Object object, Class toType) {
  317. if (toType == boolean.class || toType == Boolean.class) {
  318. return Boolean.valueOf((String) object);
  319. }
  320. if (toType == char.class || toType == Character.class) {
  321. return new Character(((String) object).charAt(0));
  322. }
  323. if (toType == byte.class || toType == Byte.class) {
  324. return new Byte((String) object);
  325. }
  326. if (toType == short.class || toType == Short.class) {
  327. return new Short((String) object);
  328. }
  329. if (toType == int.class || toType == Integer.class) {
  330. return new Integer((String) object);
  331. }
  332. if (toType == long.class || toType == Long.class) {
  333. return new Long((String) object);
  334. }
  335. if (toType == float.class || toType == Float.class) {
  336. return new Float((String) object);
  337. }
  338. if (toType == double.class || toType == Double.class) {
  339. return new Double((String) object);
  340. }
  341. return null;
  342. }
  343. protected Number allocateNumber(Class type, double value) {
  344. if (type == Byte.class || type == byte.class) {
  345. return new Byte((byte) value);
  346. }
  347. if (type == Short.class || type == short.class) {
  348. return new Short((short) value);
  349. }
  350. if (type == Integer.class || type == int.class) {
  351. return new Integer((int) value);
  352. }
  353. if (type == Long.class || type == long.class) {
  354. return new Long((long) value);
  355. }
  356. if (type == Float.class || type == float.class) {
  357. return new Float((float) value);
  358. }
  359. if (type == Double.class || type == double.class) {
  360. return new Double(value);
  361. }
  362. return null;
  363. }
  364. protected boolean canCreateCollection(Class type) {
  365. if (!type.isInterface()
  366. && ((type.getModifiers() & Modifier.ABSTRACT) == 0)) {
  367. return true;
  368. }
  369. if (type == List.class) {
  370. return true;
  371. }
  372. if (type == Set.class) {
  373. return true;
  374. }
  375. return false;
  376. }
  377. protected Collection allocateCollection(Class type) {
  378. if (!type.isInterface()
  379. && ((type.getModifiers() & Modifier.ABSTRACT) == 0)) {
  380. try {
  381. return (Collection) type.newInstance();
  382. }
  383. catch (Exception ex) {
  384. throw new JXPathException(
  385. "Cannot create collection of type: " + type,
  386. ex);
  387. }
  388. }
  389. if (type == List.class) {
  390. return new ArrayList();
  391. }
  392. if (type == Set.class) {
  393. return new HashSet();
  394. }
  395. throw new RuntimeException("Cannot create collection of type: " + type);
  396. }
  397. protected Collection unmodifiableCollection(Collection collection) {
  398. if (collection instanceof List) {
  399. return Collections.unmodifiableList((List) collection);
  400. }
  401. else if (collection instanceof Set) {
  402. return Collections.unmodifiableSet((Set) collection);
  403. }
  404. // Cannot wrap it into a proper unmodifiable collection,
  405. // so we just return the original collection itself
  406. return collection;
  407. }
  408. static final class ValueNodeSet implements NodeSet {
  409. private List values;
  410. private List pointers;
  411. public ValueNodeSet(List values) {
  412. this.values = values;
  413. }
  414. public List getValues() {
  415. return Collections.unmodifiableList(values);
  416. }
  417. public List getNodes() {
  418. return Collections.unmodifiableList(values);
  419. }
  420. public List getPointers() {
  421. if (pointers == null) {
  422. pointers = new ArrayList();
  423. for (int i = 0; i < values.size(); i++) {
  424. pointers.add(new ValuePointer(values.get(i)));
  425. }
  426. pointers = Collections.unmodifiableList(pointers);
  427. }
  428. return pointers;
  429. }
  430. }
  431. static final class ValuePointer implements Pointer {
  432. private Object bean;
  433. public ValuePointer(Object object) {
  434. this.bean = object;
  435. }
  436. public Object getValue() {
  437. return bean;
  438. }
  439. public Object getNode() {
  440. return bean;
  441. }
  442. public Object getRootNode() {
  443. return bean;
  444. }
  445. public void setValue(Object value) {
  446. throw new UnsupportedOperationException();
  447. }
  448. public Object clone() {
  449. return this;
  450. }
  451. public int compareTo(Object object) {
  452. return 0;
  453. }
  454. public String asPath() {
  455. if (bean == null) {
  456. return "null()";
  457. }
  458. else if (bean instanceof Number) {
  459. String string = bean.toString();
  460. if (string.endsWith(".0")) {
  461. string = string.substring(0, string.length() - 2);
  462. }
  463. return string;
  464. }
  465. else if (bean instanceof Boolean) {
  466. return ((Boolean) bean).booleanValue() ? "true()" : "false()";
  467. }
  468. else if (bean instanceof String) {
  469. return "'" + bean + "'";
  470. }
  471. return "{object of type " + bean.getClass().getName() + "}";
  472. }
  473. }
  474. }