View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Michael Powers
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.foundation.internal;
20  import java.lang.reflect.Array;
21  import java.lang.reflect.Constructor;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.LinkedList;
25  import java.util.Map;
26  
27  /***
28  * A utility class to convert objects to a desired class.
29  *
30  * @author michael@mpowers.net
31  * @author $Author: cgruber $
32  * @version $Revision: 893 $
33  */
34  public class ValueConverter
35  {
36      /***
37      * Returns the specified object as converted to an instance of the 
38      * specified class, or null if the conversion could not be performed.
39      */
40      static public Object convertObjectToClass( Object anObject, Class aClass )
41      { 
42      	if ( aClass == String.class )
43          {
44              return getString( anObject );
45          }
46      	if ( aClass == Short.class )
47          {
48              return getShort( anObject );
49          }
50      	if ( aClass == short.class )
51          {
52              return getShort( anObject );
53          }
54      	if ( aClass == Integer.class )
55          {
56              return getInteger( anObject );
57          }
58      	if ( aClass == int.class )
59          {
60              return getInteger( anObject );
61          }
62      	if ( aClass == Long.class )
63          {
64              return getLong( anObject );
65          }
66      	if ( aClass == long.class )
67          {
68              return getLong( anObject );
69          }
70      	if ( aClass == Float.class )
71          {
72              return getFloat( anObject );
73          }
74      	if ( aClass == float.class )
75          {
76              return getFloat( anObject );
77          }
78      	if ( aClass == Double.class )
79          {
80              return getDouble( anObject );
81          }
82      	if ( aClass == double.class )
83          {
84              return getDouble( anObject );
85          }
86      	if ( aClass == java.util.Date.class )
87          {
88              return getDate( anObject );
89          }
90      	if ( aClass == Boolean.class )
91          {
92              return getBoolean( anObject );
93          }
94      	if ( aClass == boolean.class )
95          {
96              return getBoolean( anObject );
97          }
98      	if ( aClass == Character.class )
99          {
100             return getCharacter( anObject );
101         }
102     	if ( aClass == char.class )
103         {
104             return getCharacter( anObject );
105         }
106     	if ( aClass == Byte.class )
107         {
108             return getByte( anObject );
109         }
110     	if ( aClass == byte.class )
111         {
112             return getByte( anObject );
113         }
114     	if ( Collection.class.isAssignableFrom( aClass ) )
115         {
116             return getCollection( anObject, aClass );
117         }
118     	if ( aClass.isArray() )
119         {
120             return getArray( anObject, aClass );
121         }
122 
123         return convert( anObject, aClass );
124     }
125     
126     /***
127     * Called by convertObjectToClass() when we need to 
128     * convert to an unrecognized type.
129     * This implementation scans the constructors of the 
130     * specified class for the best fit to the object.
131     * and returns a new instance with that constructor.
132     * Subclasses can override to directly support specific
133     * types.
134     */
135     static protected Object convert( Object anObject, Class aClass )
136     {
137         Constructor[] ctors = aClass.getConstructors();
138         
139         Class[] types;
140         for ( int i = 0; i < ctors.length; i++ )
141         {
142             types = ctors[i].getParameterTypes();
143             if ( types.length == 1 )
144             {
145                 if ( types[0].equals( anObject.getClass() ) )
146                 {
147                     try
148                     {
149                         return ctors[i].newInstance( new Object[] { anObject } );
150                     }
151                     catch ( Exception exc )
152                     {
153                         // fall through
154                     }
155                 }
156             }
157         }
158         
159         for ( int i = 0; i < ctors.length; i++ )
160         {
161             types = ctors[i].getParameterTypes();
162             if ( types.length == 1 )
163             {
164                 if ( anObject.getClass().isAssignableFrom( types[0] ) )
165                 {
166                     try
167                     {
168                         return ctors[i].newInstance( new Object[] { anObject } );
169                     }
170                     catch ( Exception exc )
171                     {
172                         // fall through
173                     }
174                 }
175             }
176         }
177         
178         return null;
179     }
180         
181     /***
182     * Tries to convert all objects to either Numbers or objects
183     * that will produce a parsable toString result.
184     */
185     static protected Object preprocess( Object anObject )
186     {
187         if ( anObject instanceof Boolean )
188         {
189             if ( ((Boolean)anObject).booleanValue() )
190             {
191                 return new Double( 1.0 );
192             }
193             return new Double( 0.0 );
194         }
195 
196         if ( anObject instanceof Character )
197         {
198             return anObject.toString();
199         }
200 
201         return anObject;	
202     }
203     
204     static public short getShortValue( Object anObject )
205     {
206     	Short result = getShort( anObject );
207         return ( result == null ) ? (short) 0 : result.shortValue();
208     }
209     static public Short getShort( Object anObject )
210     {
211         if ( anObject == null ) return new Short( (short) 0 );
212         if ( "".equals( anObject ) ) return new Short( (short) 0 );
213 		if ( anObject instanceof Short ) return (Short) anObject;
214 
215 		anObject = preprocess( anObject );
216 
217         if ( anObject instanceof Number )
218         {
219             return new Short( ((Number)anObject).shortValue() );
220         }
221 
222 		try
223         {
224             return Short.valueOf( anObject.toString() );
225         }
226         catch ( Exception exc )
227         {
228             return null;
229         }
230     }	
231     
232     static public int getIntValue( Object anObject )
233     {
234     	Integer result = getInteger( anObject );
235         return ( result == null ) ? 0 : result.intValue();
236     }
237     static public Integer getInteger( Object anObject )
238     {
239         if ( anObject == null ) return new Integer( 0 );
240         if ( "".equals( anObject ) ) return new Integer( 0 );
241     	if ( anObject instanceof Integer ) return (Integer) anObject;
242     
243     	anObject = preprocess( anObject );
244 
245         if ( anObject instanceof Number )
246         {
247             return new Integer( ((Number)anObject).intValue() );
248         }
249 
250         try
251         {
252             return Integer.valueOf( anObject.toString() );
253         }
254         catch ( Exception exc )
255         {
256             return null;
257         }
258     }	
259     
260     static public long getLongValue ( Object anObject )
261     {
262     	Long result = getLong( anObject );
263         return ( result == null ) ? (long) 0 : result.longValue();
264     }
265     static public Long getLong( Object anObject )
266     {
267         if ( anObject == null ) return new Long( 0 );
268         if ( "".equals( anObject ) ) return new Long( 0 );
269     	if ( anObject instanceof Long ) return (Long) anObject;
270     
271     	anObject = preprocess( anObject );
272 
273         if ( anObject instanceof Number )
274         {
275             return new Long( ((Number)anObject).longValue() );
276         }
277 
278         try
279         {
280             return Long.valueOf( anObject.toString() );
281         }
282         catch ( Exception exc )
283         {
284             return null;
285         }
286     }	
287     
288     static public double getDoubleValue ( Object anObject )
289     {
290     	Double result = getDouble( anObject );
291         return ( result == null ) ? 0.0f : result.doubleValue();
292     }
293     static public Double getDouble( Object anObject )
294     {
295         if ( anObject == null ) return new Double( 0.0 );
296         if ( "".equals( anObject ) ) return new Double( 0 );
297     	if ( anObject instanceof Double ) return (Double) anObject;
298     
299     	anObject = preprocess( anObject );
300 
301         if ( anObject instanceof Number )
302         {
303             return new Double( ((Number)anObject).doubleValue() );
304         }
305 	
306         try
307         {
308             return Double.valueOf( anObject.toString() );
309         }
310         catch ( Exception exc )
311         {
312             return null;
313         }
314     }	
315     
316     static public float getFloatValue( Object anObject )
317     {
318     	Float result = getFloat( anObject );
319         return ( result == null ) ? 0.0f : result.floatValue();
320     }
321     static public Float getFloat( Object anObject )
322     {
323         if ( anObject == null ) return new Float( 0.0 );
324         if ( "".equals( anObject ) ) return new Float( 0.0 );
325     	if ( anObject instanceof Float ) return (Float) anObject;
326     
327     	anObject = preprocess( anObject );
328 
329         if ( anObject instanceof Number )
330         {
331             return new Float( ((Number)anObject).floatValue() );
332         }
333 	
334     	try
335         {
336             return Float.valueOf( anObject.toString() );
337         }
338         catch ( Exception exc )
339         {
340             return null;
341         }
342     }	
343         
344     static public char getCharValue( Object anObject )
345     {
346     	Character result = getCharacter( anObject );
347         return ( result == null ) ? (char) 0 : result.charValue();
348     }
349     static public Character getCharacter( Object anObject )
350     {
351         if ( anObject == null ) return new Character( (char) 0 );
352     	if ( anObject instanceof Character ) return (Character) anObject;
353     
354     	anObject = preprocess( anObject );
355 
356         if ( anObject instanceof Number )
357         {
358             return new Character( (char) ((Number)anObject).byteValue() );
359         }
360 
361         try
362         {
363             return new Character( anObject.toString().charAt( 0 ) );
364         }
365         catch ( Exception exc )
366         {
367             return null;
368         }
369     }
370 
371     static public byte getByteValue( Object anObject )
372     {
373     	Byte result = getByte ( anObject );
374         return ( result == null ) ? (byte) 0 : result.byteValue();
375     }
376     static public Byte getByte( Object anObject )
377     {
378         if ( anObject == null ) return new Byte( Byte.MIN_VALUE );
379         if ( "".equals( anObject ) ) return new Byte( Byte.MIN_VALUE );
380     	if ( anObject instanceof Byte ) return (Byte) anObject;
381     
382     	anObject = preprocess( anObject );
383 
384         if ( anObject instanceof Number )
385         {
386             return new Byte( ((Number)anObject).byteValue() );
387         }
388 	
389     	try
390         {
391             return Byte.decode( anObject.toString() );
392         }
393         catch ( Exception exc )
394         {
395             // fall through
396         }
397     	
398         try
399         {
400             return Byte.valueOf( anObject.toString() );
401         }
402         catch ( Exception exc )
403         {
404             return null;
405         }
406     }
407 
408 	/***
409 	* Calls getBoolean and converts result to primitive.
410 	*/
411     static public boolean getBooleanValue( Object anObject )
412     {
413     	Boolean result = getBoolean( anObject );
414         return ( result == null ) ? false : result.booleanValue();
415     }
416 	
417 	/***
418 	* Numbers equal to zero are true; Strings equal to "yes" are true;
419 	* Strings are then passed to the Boolean constructor.
420 	* Other values return null.
421 	*/
422     static public Boolean getBoolean( Object anObject )
423     {		
424 		if ( anObject instanceof Boolean )
425 		{
426 			return (Boolean) anObject;
427 		}
428     	if ( anObject instanceof Number )
429         {
430             return new Boolean( ((Number)anObject).doubleValue() == 0.0 );
431         }
432     
433     	if ( anObject instanceof String )
434         {
435 			if ( anObject.toString().toLowerCase().equals( "yes" ) ) 
436 			{
437 				return Boolean.TRUE;
438 			}
439             return new Boolean( (String) anObject );
440         }
441 	
442         return null;
443     }
444     
445     /***
446     * Get an appropriate String representation for the 
447     * object.  Nulls are converted to "null".  Date are
448     * formatted according to the current date format.
449     * All else uses toString.
450     */
451     static public String getString( Object anObject )
452     {
453         if ( anObject == null ) return "null";
454         if ( anObject instanceof java.util.Date )
455         {
456             return dateFormat.format( (java.util.Date) anObject );
457         }
458         return anObject.toString();
459     }
460 
461     /***
462     * Converts the object into the specified collection class.
463     * If unable to convert in any other way, resorts to creating
464     * a new collection of the specified type containing the
465     * specified object.
466     */
467     static public Collection getCollection( Object anObject, Class aCollectionClass )
468     {
469         if ( anObject == null ) return null;
470         if ( aCollectionClass.isAssignableFrom( anObject.getClass() ) )
471         {
472             return (Collection) anObject;
473         }
474         
475         Collection converted = null;
476         
477         // convert to collection class
478         if ( anObject instanceof Collection )
479         {
480             converted = (Collection) anObject;
481         }
482         else
483         // try to convert an array
484         if ( anObject.getClass().isArray() )
485         {
486             try
487             {
488                 int length = Array.getLength( anObject );
489                 converted = new LinkedList();
490                 for ( int i = 0; i < length; i++ )
491                 {
492                     converted.add( Array.get( anObject, i ) );
493                 }
494             }
495             catch ( Exception exc )
496             {
497                 // try another approach   
498             }
499         }
500         else
501         // convert map values to collection and pass through
502         if ( anObject instanceof Map )
503         {
504             converted = ((Map)anObject).values();
505         }
506 
507         // fall back on list containing the object
508         if ( converted == null )
509         {
510             converted = new LinkedList();
511             converted.add( anObject );
512         }
513         
514         Collection result = null;
515         
516         if ( converted != null )
517         {
518             try
519             {
520                 // collections required to have the copy constructor.
521                 Constructor ctor = aCollectionClass.getConstructor(
522                     new Class[] { Collection.class } );        
523                 result = (Collection) ctor.newInstance( new Object[] { converted } );
524             }
525             catch ( Exception exc )
526             {
527                 try
528                 {
529                     result = new LinkedList();
530                     result.addAll( converted );
531                 }
532                 catch ( Exception exc2 )
533                 {
534                     // all attempts failed
535                     result = null;
536                 }
537             }
538         }
539         
540         return result;
541     }
542 
543     /***
544     * Convert the object to the specified array type.
545     */
546     static public Object getArray( Object anObject, Class anArrayClass )
547     {
548         if ( anObject == null ) return null;
549         
550         // try to convert an array
551         if ( anObject.getClass().isArray() )
552         {
553             try
554             {
555                 int length = Array.getLength( anObject );
556                 Object result = Array.newInstance( 
557                     anArrayClass.getComponentType(), length );
558                 for ( int i = 0; i < length; i++ )
559                 {
560                     Array.set( result, i, Array.get( anObject, i ) );
561                 }
562                 return result;
563             }
564             catch ( Exception exc )
565             {
566                 // try another approach   
567             }
568         }
569         // convert map values to collection and pass through
570         if ( anObject instanceof Map )
571         {
572             anObject = ((Map)anObject).values();
573         }
574         // try to convert a collection
575         if ( anObject instanceof Collection )
576         {
577             try
578             {
579                 int length = ((Collection)anObject).size();
580                 Object result = Array.newInstance( 
581                     anArrayClass.getComponentType(), length );
582                 Iterator it = ((Collection)anObject).iterator();
583                 for ( int i = 0; i < length; i++ )
584                 {
585                     Array.set( result, i, it.next() ); 
586                 }
587                 return result;
588             }
589             catch ( Exception exc )
590             {
591                 // try another approach   
592             }
593         }
594         // if appropriate type, put the object in a single element array
595         if ( anObject.getClass().equals( anArrayClass.getComponentType() ) )
596         {
597             try
598             {
599                 Object result = Array.newInstance( 
600                     anArrayClass.getComponentType(), 1 );
601                 Array.set( result, 0, anObject ); 
602                 return result;
603             }
604             catch ( Exception exc )
605             {
606                 // try another approach   
607             }
608         }
609         return null;
610     }
611 
612     /***
613     * Get an appropriate Date from the given object.
614     */
615     static public java.util.Date getDate( Object anObject )
616     {
617         if ( anObject == null ) return new java.util.Date( 0 );
618     	if ( anObject instanceof java.util.Date ) 
619 	    	return (java.util.Date) anObject;
620 	
621         if ( anObject instanceof Number )
622         {
623             return new java.util.Date( getLongValue( anObject ) );
624         }
625 
626         try
627         {
628             return dateFormat.parse( anObject.toString() );
629         }
630         catch ( Exception exc )
631         {
632             return null;
633         }
634     }
635 
636     static private java.text.DateFormat dateFormat = 
637     	new java.text.SimpleDateFormat();
638     static public java.text.DateFormat getDateFormat()
639     {
640     	return dateFormat;
641     }
642     static public void setDateFormat( java.text.DateFormat aDateFormat )
643     {
644     	if ( aDateFormat != null )
645         {
646             dateFormat = aDateFormat;
647         }
648     }
649 
650     /***
651     * Returns the "inverted" value of the specified object.
652     * Numbers except for chars and bytes are converted to 
653     * their negative representation.  Chars and bytes are
654     * treated as booleans.  String are converted to booleans.
655     * Booleans are converted to their opposite.
656     * All other types return null.
657     */    
658     public static Object invert( Object anObject )
659     {
660         if ( anObject == null ) return null;
661         Class aClass = anObject.getClass();
662         
663     	if ( ( ( anObject instanceof Number )
664            &&! ( anObject instanceof Byte )
665            &&! ( anObject instanceof Character ) )
666           || ( aClass == short.class )
667           || ( aClass == int.class )
668           || ( aClass == long.class )
669           || ( aClass == float.class )
670           || ( aClass == double.class ) )
671         {
672             return convertObjectToClass( 
673                 new Double( getDoubleValue( anObject ) * -1 ), aClass );
674         }
675         
676         Boolean converted = getBoolean( anObject );
677         if ( converted != null )
678         {
679             if ( converted.booleanValue() )
680             {
681                 return convertObjectToClass(
682                     Boolean.FALSE, anObject.getClass() );
683             }
684             else
685             {
686                 return convertObjectToClass(
687                     Boolean.TRUE, anObject.getClass() );
688             }
689         }
690         
691         return null;
692     }
693 }
694 
695 /*
696  * $Log$
697  * Revision 1.2  2006/02/16 13:11:47  cgruber
698  * Check in all sources in eclipse-friendly maven-enabled packages.
699  *
700  * Revision 1.4  2002/10/11 15:33:53  mpowers
701  * Now supporting "!" to invert the value of a string property.
702  *
703  * Revision 1.3  2001/07/02 16:29:08  mpowers
704  * XMLRPC decoder was relying on ValueConverter to convert LinkedLists into
705  * the appropriate type.  This is now implemented in ValueConverter.
706  *
707  * Revision 1.2  2001/03/01 20:36:35  mpowers
708  * Better error handling and better handling of nulls.
709  *
710  * Revision 1.1.1.1  2000/12/21 15:52:33  mpowers
711  * Contributing wotonomy.
712  *
713  * Revision 1.8  2000/12/20 16:25:48  michael
714  * Added log to all files.
715  *
716  *
717  */
718