001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.util;
017:
018: import java.lang.reflect.Array;
019: import java.lang.reflect.Modifier;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Set;
027:
028: import org.apache.commons.beanutils.ConvertUtils;
029: import org.apache.commons.beanutils.Converter;
030: import org.apache.commons.jxpath.JXPathException;
031: import org.apache.commons.jxpath.NodeSet;
032: import org.apache.commons.jxpath.Pointer;
033:
034: /**
035: * The default implementation of TypeConverter.
036: *
037: * @author Dmitri Plotnikov
038: * @version $Revision: 1.15 $ $Date: 2004/07/25 13:16:04 $
039: */
040: public class BasicTypeConverter implements TypeConverter {
041:
042: /**
043: * Returns true if it can convert the supplied
044: * object to the specified class.
045: */
046: public boolean canConvert(Object object, Class toType) {
047: if (object == null) {
048: return true;
049: }
050:
051: if (toType == Object.class) {
052: return true;
053: }
054:
055: Class fromType = object.getClass();
056: if (fromType.equals(toType)) {
057: return true;
058: }
059:
060: if (toType.isAssignableFrom(fromType)) {
061: return true;
062: }
063:
064: if (toType == String.class) {
065: return true;
066: }
067:
068: if (object instanceof Boolean) {
069: if (toType == boolean.class
070: || Number.class.isAssignableFrom(toType)) {
071: return true;
072: }
073: } else if (object instanceof Number) {
074: if (toType.isPrimitive()
075: || Number.class.isAssignableFrom(toType)) {
076: return true;
077: }
078: } else if (object instanceof Character) {
079: if (toType == char.class) {
080: return true;
081: }
082: } else if (object instanceof String) {
083: if (toType.isPrimitive()) {
084: return true;
085: }
086: if (toType == Boolean.class || toType == Character.class
087: || toType == Byte.class || toType == Short.class
088: || toType == Integer.class || toType == Long.class
089: || toType == Float.class || toType == Double.class) {
090: return true;
091: }
092: } else if (fromType.isArray()) {
093: // Collection -> array
094: if (toType.isArray()) {
095: Class cType = toType.getComponentType();
096: int length = Array.getLength(object);
097: for (int i = 0; i < length; i++) {
098: Object value = Array.get(object, i);
099: if (!canConvert(value, cType)) {
100: return false;
101: }
102: }
103: return true;
104: } else if (Collection.class.isAssignableFrom(toType)) {
105: return canCreateCollection(toType);
106: } else {
107: if (Array.getLength(object) > 0) {
108: Object value = Array.get(object, 0);
109: return canConvert(value, toType);
110: } else {
111: return canConvert("", toType);
112: }
113: }
114: } else if (object instanceof Collection) {
115: // Collection -> array
116: if (toType.isArray()) {
117: Class cType = toType.getComponentType();
118: Iterator it = ((Collection) object).iterator();
119: while (it.hasNext()) {
120: Object value = it.next();
121: if (!canConvert(value, cType)) {
122: return false;
123: }
124: }
125: return true;
126: } else if (Collection.class.isAssignableFrom(toType)) {
127: return canCreateCollection(toType);
128: } else {
129: if (((Collection) object).size() > 0) {
130: Object value;
131: if (object instanceof List) {
132: value = ((List) object).get(0);
133: } else {
134: Iterator it = ((Collection) object).iterator();
135: value = it.next();
136: }
137: return canConvert(value, toType);
138: } else {
139: return canConvert("", toType);
140: }
141: }
142: } else if (object instanceof NodeSet) {
143: return canConvert(((NodeSet) object).getValues(), toType);
144: } else if (object instanceof Pointer) {
145: return canConvert(((Pointer) object).getValue(), toType);
146: }
147: return ConvertUtils.lookup(toType) != null;
148: }
149:
150: /**
151: * Converts the supplied object to the specified
152: * type. Throws a runtime exception if the conversion is
153: * not possible.
154: */
155: public Object convert(Object object, Class toType) {
156: if (object == null) {
157: if (toType.isPrimitive()) {
158: return convertNullToPrimitive(toType);
159: }
160: return null;
161: }
162:
163: if (toType == Object.class) {
164: if (object instanceof NodeSet) {
165: return convert(((NodeSet) object).getValues(), toType);
166: } else if (object instanceof Pointer) {
167: return convert(((Pointer) object).getValue(), toType);
168: }
169: return object;
170: }
171:
172: Class fromType = object.getClass();
173: if (fromType.equals(toType)
174: || toType.isAssignableFrom(fromType)) {
175: return object;
176: }
177:
178: if (fromType.isArray()) {
179: int length = Array.getLength(object);
180: if (toType.isArray()) {
181: Class cType = toType.getComponentType();
182:
183: Object array = Array.newInstance(cType, length);
184: for (int i = 0; i < length; i++) {
185: Object value = Array.get(object, i);
186: Array.set(array, i, convert(value, cType));
187: }
188: return array;
189: } else if (Collection.class.isAssignableFrom(toType)) {
190: Collection collection = allocateCollection(toType);
191: for (int i = 0; i < length; i++) {
192: collection.add(Array.get(object, i));
193: }
194: return unmodifiableCollection(collection);
195: } else {
196: if (length > 0) {
197: Object value = Array.get(object, 0);
198: return convert(value, toType);
199: } else {
200: return convert("", toType);
201: }
202: }
203: } else if (object instanceof Collection) {
204: int length = ((Collection) object).size();
205: if (toType.isArray()) {
206: Class cType = toType.getComponentType();
207: Object array = Array.newInstance(cType, length);
208: Iterator it = ((Collection) object).iterator();
209: for (int i = 0; i < length; i++) {
210: Object value = it.next();
211: Array.set(array, i, convert(value, cType));
212: }
213: return array;
214: } else if (Collection.class.isAssignableFrom(toType)) {
215: Collection collection = allocateCollection(toType);
216: collection.addAll((Collection) object);
217: return unmodifiableCollection(collection);
218: } else {
219: if (length > 0) {
220: Object value;
221: if (object instanceof List) {
222: value = ((List) object).get(0);
223: } else {
224: Iterator it = ((Collection) object).iterator();
225: value = it.next();
226: }
227: return convert(value, toType);
228: } else {
229: return convert("", toType);
230: }
231: }
232: } else if (object instanceof NodeSet) {
233: return convert(((NodeSet) object).getValues(), toType);
234: } else if (object instanceof Pointer) {
235: return convert(((Pointer) object).getValue(), toType);
236: } else if (toType == String.class) {
237: return object.toString();
238: } else if (object instanceof Boolean) {
239: if (toType == boolean.class) {
240: return object;
241: }
242: boolean value = ((Boolean) object).booleanValue();
243: return allocateNumber(toType, value ? 1 : 0);
244: } else if (object instanceof Number) {
245: double value = ((Number) object).doubleValue();
246: if (toType == boolean.class || toType == Boolean.class) {
247: return value == 0.0 ? Boolean.FALSE : Boolean.TRUE;
248: }
249: if (toType.isPrimitive()
250: || Number.class.isAssignableFrom(toType)) {
251: return allocateNumber(toType, value);
252: }
253: } else if (object instanceof Character) {
254: if (toType == char.class) {
255: return object;
256: }
257: } else if (object instanceof String) {
258: Object value = convertStringToPrimitive(object, toType);
259: if (value != null) {
260: return value;
261: }
262: }
263:
264: Converter converter = ConvertUtils.lookup(toType);
265: if (converter != null) {
266: return converter.convert(toType, object);
267: }
268:
269: throw new RuntimeException("Cannot convert "
270: + object.getClass() + " to " + toType);
271: }
272:
273: protected Object convertNullToPrimitive(Class toType) {
274: if (toType == boolean.class) {
275: return Boolean.FALSE;
276: }
277: if (toType == char.class) {
278: return new Character('\0');
279: }
280: if (toType == byte.class) {
281: return new Byte((byte) 0);
282: }
283: if (toType == short.class) {
284: return new Short((short) 0);
285: }
286: if (toType == int.class) {
287: return new Integer(0);
288: }
289: if (toType == long.class) {
290: return new Long(0L);
291: }
292: if (toType == float.class) {
293: return new Float(0.0f);
294: }
295: if (toType == double.class) {
296: return new Double(0.0);
297: }
298: return null;
299: }
300:
301: protected Object convertStringToPrimitive(Object object,
302: Class toType) {
303: if (toType == boolean.class || toType == Boolean.class) {
304: return Boolean.valueOf((String) object);
305: }
306: if (toType == char.class || toType == Character.class) {
307: return new Character(((String) object).charAt(0));
308: }
309: if (toType == byte.class || toType == Byte.class) {
310: return new Byte((String) object);
311: }
312: if (toType == short.class || toType == Short.class) {
313: return new Short((String) object);
314: }
315: if (toType == int.class || toType == Integer.class) {
316: return new Integer((String) object);
317: }
318: if (toType == long.class || toType == Long.class) {
319: return new Long((String) object);
320: }
321: if (toType == float.class || toType == Float.class) {
322: return new Float((String) object);
323: }
324: if (toType == double.class || toType == Double.class) {
325: return new Double((String) object);
326: }
327: return null;
328: }
329:
330: protected Number allocateNumber(Class type, double value) {
331: if (type == Byte.class || type == byte.class) {
332: return new Byte((byte) value);
333: }
334: if (type == Short.class || type == short.class) {
335: return new Short((short) value);
336: }
337: if (type == Integer.class || type == int.class) {
338: return new Integer((int) value);
339: }
340: if (type == Long.class || type == long.class) {
341: return new Long((long) value);
342: }
343: if (type == Float.class || type == float.class) {
344: return new Float((float) value);
345: }
346: if (type == Double.class || type == double.class) {
347: return new Double(value);
348: }
349: return null;
350: }
351:
352: protected boolean canCreateCollection(Class type) {
353: if (!type.isInterface()
354: && ((type.getModifiers() & Modifier.ABSTRACT) == 0)) {
355: return true;
356: }
357:
358: if (type == List.class) {
359: return true;
360: }
361:
362: if (type == Set.class) {
363: return true;
364: }
365: return false;
366: }
367:
368: protected Collection allocateCollection(Class type) {
369: if (!type.isInterface()
370: && ((type.getModifiers() & Modifier.ABSTRACT) == 0)) {
371: try {
372: return (Collection) type.newInstance();
373: } catch (Exception ex) {
374: throw new JXPathException(
375: "Cannot create collection of type: " + type, ex);
376: }
377: }
378:
379: if (type == List.class) {
380: return new ArrayList();
381: }
382: if (type == Set.class) {
383: return new HashSet();
384: }
385: throw new RuntimeException("Cannot create collection of type: "
386: + type);
387: }
388:
389: protected Collection unmodifiableCollection(Collection collection) {
390: if (collection instanceof List) {
391: return Collections.unmodifiableList((List) collection);
392: } else if (collection instanceof Set) {
393: return Collections.unmodifiableSet((Set) collection);
394: }
395: // Cannot wrap it into a proper unmodifiable collection,
396: // so we just return the original collection itself
397: return collection;
398: }
399:
400: static final class ValueNodeSet implements NodeSet {
401: private List values;
402: private List pointers;
403:
404: public ValueNodeSet(List values) {
405: this .values = values;
406: }
407:
408: public List getValues() {
409: return Collections.unmodifiableList(values);
410: }
411:
412: public List getNodes() {
413: return Collections.unmodifiableList(values);
414: }
415:
416: public List getPointers() {
417: if (pointers == null) {
418: pointers = new ArrayList();
419: for (int i = 0; i < values.size(); i++) {
420: pointers.add(new ValuePointer(values.get(i)));
421: }
422: pointers = Collections.unmodifiableList(pointers);
423: }
424: return pointers;
425: }
426: }
427:
428: static final class ValuePointer implements Pointer {
429: private Object bean;
430:
431: public ValuePointer(Object object) {
432: this .bean = object;
433: }
434:
435: public Object getValue() {
436: return bean;
437: }
438:
439: public Object getNode() {
440: return bean;
441: }
442:
443: public Object getRootNode() {
444: return bean;
445: }
446:
447: public void setValue(Object value) {
448: throw new UnsupportedOperationException();
449: }
450:
451: public Object clone() {
452: return this ;
453: }
454:
455: public int compareTo(Object object) {
456: return 0;
457: }
458:
459: public String asPath() {
460: if (bean == null) {
461: return "null()";
462: } else if (bean instanceof Number) {
463: String string = bean.toString();
464: if (string.endsWith(".0")) {
465: string = string.substring(0, string.length() - 2);
466: }
467: return string;
468: } else if (bean instanceof Boolean) {
469: return ((Boolean) bean).booleanValue() ? "true()"
470: : "false()";
471: } else if (bean instanceof String) {
472: return "'" + bean + "'";
473: }
474: return "{object of type " + bean.getClass().getName() + "}";
475: }
476: }
477: }
|