001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/resources/ClassChanger.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2007 by:
006 EXSE, Department of Geography, University of Bonn
007 http://www.giub.uni-bonn.de/exse/
008 lat/lon GmbH
009 http://www.lat-lon.de
010
011 It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
012 (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
013 SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite
014 Institut de Recherche pour le D�veloppement / US-Espace
015 mailto:seasnet@teledetection.fr
016
017
018 This library is free software; you can redistribute it and/or
019 modify it under the terms of the GNU Lesser General Public
020 License as published by the Free Software Foundation; either
021 version 2.1 of the License, or (at your option) any later version.
022
023 This library is distributed in the hope that it will be useful,
024 but WITHOUT ANY WARRANTY; without even the implied warranty of
025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
026 Lesser General Public License for more details.
027
028 You should have received a copy of the GNU Lesser General Public
029 License along with this library; if not, write to the Free Software
030 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
031
032 Contact:
033
034 Andreas Poth
035 lat/lon GmbH
036 Aennchenstr. 19
037 53115 Bonn
038 Germany
039 E-Mail: poth@lat-lon.de
040
041 Klaus Greve
042 Department of Geography
043 University of Bonn
044 Meckenheimer Allee 166
045 53115 Bonn
046 Germany
047 E-Mail: klaus.greve@uni-bonn.de
048
049
050 ---------------------------------------------------------------------------*/
051 package org.deegree.model.csct.resources;
052
053 // Standard set of Java objects.
054 import java.util.Date;
055
056 /**
057 * Transforme un objet d'une classe vers une autre. Cette classe sert principalement � convertir en
058 * {@link Number} des objets d'une autre classe, par exemple {@link Date}. Une m�thode statique,
059 * {@link #toNumber}, se charge d'effectuer ce genre de conversion en prenant en compte toutes les
060 * classes qui auront �t� d�clar�es � <code>ClassChanger</code>. <br>
061 * <br>
062 * Pour d�clarer une nouvelle classe, on peut proc�der comme suit. L'exemple ci-dessous inscrit une
063 * classe qui convertira des objets {@link Date} en objets {@link Long}. Notez qu'il ne s'agit que
064 * d'un exemple. Ce convertisseur n'a pas besoin d'�tre d�clar� car <code>ClassChanger</code>
065 * comprend d�j� les objets {@link Date} par d�faut.
066 * </p>
067 *
068 * <blockquote>
069 *
070 * <pre>
071 * ClassChanger.register( new ClassChanger( Date.class, Long.class ) {
072 * protected Number convert( final Comparable o ) {
073 * return new Long( ( (Date) o ).getTime() );
074 * }
075 *
076 * protected Comparable inverseConvert( final Number number ) {
077 * return new Date( number.longValue() );
078 * }
079 * } );
080 * </pre>
081 *
082 * </blockquote>
083 *
084 * @version 1.0
085 * @author Martin Desruisseaux
086 */
087 public abstract class ClassChanger {
088 /**
089 * Liste des classes d'objets pouvant �tre convertis en nombre. Cette liste contiendra par
090 * d�faut quelques instances de {@link ClassChanger} pour quelques classes standards du Java,
091 * telle que {@link Date}. Toutefois, d'autres objets pourront �tre ajout�s par la suite. Cette
092 * liste est <u>ordonn�e</u>. Les classe le plus hautes dans la hierarchie (les classes
093 * parentes) doivent appara�tre � la fin.
094 */
095 private static ClassChanger[] list = new ClassChanger[] { new ClassChanger( Date.class, Long.class ) {
096 protected Number convert( final Comparable object ) {
097 return new Long( ( (Date) object ).getTime() );
098 }
099
100 protected Comparable inverseConvert( final Number value ) {
101 return new Date( value.longValue() );
102 }
103 } };
104
105 /**
106 * Parent class for {@link #convert}'s input objects.
107 */
108 private final Class source;
109
110 /**
111 * Parent class for {@link #convert}'s output objects.
112 */
113 private final Class target;
114
115 /**
116 * Construct a new class changer.
117 *
118 * @param source
119 * Parent class for {@link #convert}'s input objects.
120 * @param target
121 * Parent class for {@link #convert}'s output objects.
122 */
123 protected ClassChanger( final Class source, final Class target ) {
124 this.source = source;
125 this.target = target;
126 if ( !Comparable.class.isAssignableFrom( source ) ) {
127 throw new IllegalArgumentException( String.valueOf( source ) );
128 }
129 if ( !Number.class.isAssignableFrom( target ) ) {
130 throw new IllegalArgumentException( String.valueOf( target ) );
131 }
132 }
133
134 /**
135 * Returns the numerical value for an object.
136 *
137 * @param object
138 * Object to convert (may be null).
139 * @return The object's numerical value.
140 * @throws ClassCastException
141 * if <code>object</code> is not of the expected class.
142 */
143 protected abstract Number convert( final Comparable object )
144 throws ClassCastException;
145
146 /**
147 * Returns an instance of the converted classe from a numerical value.
148 *
149 * @param value
150 * The value to wrap.
151 * @return An instance of the source classe.
152 */
153 protected abstract Comparable inverseConvert( final Number value );
154
155 /**
156 * Returns a string representation for this class changer.
157 */
158 public String toString() {
159 return "ClassChanger[" + source.getName() + "\u00A0\u21E8\u00A0" + target.getName() + ']';
160 }
161
162 /**
163 * Inscrit un nouvel objet <code>ClassChanger</code>. Les objets <code>ClassChanger</code>
164 * inscrits ici seront pris en compte par la m�thode {@link #toNumber}. Si un objet
165 * <code>ClassChanger</code> existait d�j� pour une m�me classe, une exception sera lanc�e.
166 * Cette sp�cification est justifi�e par le fait qu'on enregistre souvent un objet
167 * <code>ClassChanger</code> lors de l'initialisation d'une classe qui vient d'�tre charg�e
168 * pour la premi�re fois. En interdisant tout changements aux objets <code>ClassChanger</code>
169 * apr�s l'initialisation d'une classe, on �vite que la fa�on de convertir des objets en nombres
170 * r�els ne change au cours d'une ex�cution de la machine virtuelle. Notez que si
171 * <code>converter</code> ne peut pas prendre en charge une m�me classe que celle d'un autre
172 * objet <code>ClassChanger</code>, il peut toutefois prendre en charge une classe parente ou
173 * une classe fille.
174 *
175 * @param converter
176 * Convertisseur � ajouter � la liste des convertisseurs d�j� existants.
177 * @throws IllegalStateException
178 * si un autre objet <code>ClassChanger</code> prennait d�j� en charge la m�me
179 * classe (l'argument <code>classe</code> d�clar� au constructeur) que
180 * <code>converter</code>.
181 */
182 public static synchronized void register( final ClassChanger converter )
183 throws IllegalStateException {
184 int i;
185 for ( i = 0; i < list.length; i++ ) {
186 if ( list[i].source.isAssignableFrom( converter.source ) ) {
187 /*
188 * On a trouv� un convertisseur qui utilisait une classe parente. Le nouveau
189 * convertisseur devra s'ins�rer avant son parent. Mais on va d'abord s'assurer
190 * qu'il n'existait pas d�j� un convertisseur pour cette classe.
191 */
192 for ( int j = i; j < list.length; j++ ) {
193 if ( list[j].source.equals( converter.source ) ) {
194 throw new IllegalStateException( list[j].toString() );
195 }
196 }
197 break;
198 }
199 }
200 list = (ClassChanger[]) XArray.insert( list, i, 1 );
201 list[i] = converter;
202 }
203
204 /**
205 * Returns the class changer for the specified classe.
206 *
207 * @throws ClassNotFoundException
208 * if <code>source</code> is not a registered class.
209 */
210 private static synchronized ClassChanger getClassChanger( final Class source )
211 throws ClassNotFoundException {
212 for ( int i = 0; i < list.length; i++ )
213 if ( list[i].source.isAssignableFrom( source ) )
214 return list[i];
215 throw new ClassNotFoundException( source.getName() );
216 }
217
218 /**
219 * Returns the target class for the specified source class, if a suitable transformation is
220 * known. The source class is a {@link Comparable} subclass that will be specified as input to
221 * {@link #convert}. The target class is a {@link Number} subclass that wimm be returned as
222 * output by {@link #convert}. If no suitable mapping is found, then <code>source</code> is
223 * returned.
224 */
225 public static Class getTransformedClass( final Class source ) {
226 if ( source != null )
227 for ( int i = 0; i < list.length; i++ )
228 if ( list[i].source.isAssignableFrom( source ) )
229 return list[i].target;
230 return source;
231 }
232
233 /**
234 * Returns the numeric value for the specified object. For example the code
235 * <code>toNumber(new Date())</code> returns the {@link Date#getTime()} value of the
236 * specified date object as a {@link Long}.
237 *
238 * @param object
239 * Object to convert (may be null).
240 * @return <code>null</code> if <code>object</code> was null; otherwise <code>object</code>
241 * if the supplied object is already an instance of {@link Number}; otherwise a new
242 * number with the numerical value.
243 * @throws ClassNotFoundException
244 * if <code>object</code> is not an instance of a registered class.
245 */
246 public static Number toNumber( final Comparable object )
247 throws ClassNotFoundException {
248 if ( object != null ) {
249 if ( object instanceof Number ) {
250 return (Number) object;
251 }
252 return getClassChanger( object.getClass() ).convert( object );
253 }
254 return null;
255 }
256
257 /**
258 * Wrap the specified number as an instance of the specified classe. For example
259 * <code>toComparable(Date.class, new Long(time))</code> is equivalent to
260 * <code>new Date(time)</code>. There is of course no point to use this method if the
261 * destination class is know at compile time. This method is useful for creating instance of
262 * classes choosen dynamically at run time.
263 *
264 * @param value
265 * The numerical value (may be null).
266 * @param classe
267 * The desired classe for return value.
268 * @throws ClassNotFoundException
269 * if <code>classe</code> is not a registered class.
270 */
271 public static Comparable toComparable( final Number value, final Class classe )
272 throws ClassNotFoundException {
273 if ( value != null ) {
274 if ( Number.class.isAssignableFrom( classe ) ) {
275 return (Comparable) value;
276 }
277 return getClassChanger( classe ).inverseConvert( value );
278 }
279 return null;
280 }
281 }