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&nbsp;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,&nbsp;new&nbsp;Long(time))</code> is equivalent to
260         * <code>new&nbsp;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    }