View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * $Id$
44   */
45  
46  package org.exolab.castor.mapping.loader;
47  
48  import java.io.Serializable;
49  import java.lang.reflect.Method;
50  import java.util.Enumeration;
51  import java.util.NoSuchElementException;
52  import java.util.StringTokenizer;
53  import java.util.Vector;
54  
55  import org.castor.core.util.AbstractProperties;
56  import org.castor.xml.XMLProperties;
57  import org.exolab.castor.mapping.CollectionHandler;
58  import org.exolab.castor.mapping.MappingException;
59  
60  /**
61   * Utility class for obtaining collection handlers. Based on the configuration
62   * and supported classes it will return collections suitable for Java 1.1 and
63   * Java 1.2 run times.
64   * 
65   * @author <a href="arkin@intalio.com">Assaf Arkin</a>
66   * @version $Revision$ $Date: 2005-05-02 14:58:59 -0600 (Mon, 02 May
67   *          2005) $
68   * @see CollectionHandler
69   */
70  public final class CollectionHandlers {
71  
72     private static Info[] _info;
73  
74     // For JDK 1.1 compatilibity
75     private static Class _collectionClass = null;
76     private static boolean _loadedCollectionClass = false;
77  
78     static {
79        Vector<Info> allInfo = new Vector<Info>();
80        AbstractProperties properties = XMLProperties.newInstance();
81        StringTokenizer tokenizer = new StringTokenizer(properties.getString(
82              XMLProperties.COLLECTION_HANDLERS_FOR_JAVA_11_OR_12, ""), ", ");
83        while (tokenizer.hasMoreTokens()) {
84           try {
85              Class<?> infoClass;
86              if (CollectionHandlers.class.getClassLoader() != null)
87                 infoClass = CollectionHandlers.class.getClassLoader().loadClass(tokenizer.nextToken());
88              else
89                 infoClass = Class.forName(tokenizer.nextToken());
90              Method method = infoClass.getMethod("getCollectionHandlersInfo", (Class[]) null);
91              Info[] info = (Info[]) method.invoke(null, (Object[]) null);
92              for (int i = 0; i < info.length; ++i)
93                 allInfo.addElement(info[i]);
94           } catch (Exception except) {
95              // System.err.println( "CollectionHandlers: " + except.toString()
96              // );
97           }
98        }
99        _info = new Info[allInfo.size()];
100       allInfo.copyInto(_info);
101    }
102 
103    /**
104     * Returns the collection's Java class from the collection name. The
105     * collection name may be a short name (e.g. <tt>vector</tt>) or the
106     * collection Java class name (e.g. <tt>java.util.Vector</tt>). If the
107     * collection is not supported, an exception is thrown.
108     * 
109     * @param name
110     *           The collection name
111     * @return The collection Java class
112     * @throws MappingException
113     *            The named collection is not supported
114     */
115    public static Class<?> getCollectionType(String name) throws MappingException {
116       for (Info info : _info) {
117          if (info.getShortName().equalsIgnoreCase(name) || info.getJavaClass().getName().equals(name)) {
118             return info.getJavaClass();
119          }
120       }
121          
122 //      for (int i = 0; i < _info.length; ++i) {
123 //         if (_info[i].getShortName().equalsIgnoreCase(name) || _info[i].getJavaClass().getName().equals(name)) {
124 //            return _info[i].getJavaClass();
125 //            // throw new MappingException( "mapping.noCollectionHandler", name);
126 //         }
127 //      }
128 
129       // -- Fix for JDK 1.1 compatibility
130       // old code: return Collection.class;
131       if (!_loadedCollectionClass) {
132          _loadedCollectionClass = true;
133          try {
134             _collectionClass = Class.forName("java.util.Collection");
135          } catch (ClassNotFoundException cnfe) {
136             // Do nothing we are just here for JDK 1.1
137             // compatibility
138          }
139       }
140       return _collectionClass;
141    }
142 
143    /**
144     * Returns true if the given class has an associated CollectionHandler.
145     * 
146     * @param javaClass
147     *           the class to search collection handlers for
148     * @return true if the given class has an associated CollectionHandler,
149     *         otherwise false.
150     */
151    public static boolean hasHandler(Class<?> javaClass) {
152       // -- Adjust javaClass for arrays, needed for arrays of
153       // -- primitives, except for byte[] which shouldn't
154       // -- use a collection handler
155       if (javaClass.isArray()) {
156          if (javaClass.getComponentType() != Byte.TYPE)
157             javaClass = Object[].class;
158       }
159 
160       for (Info info : _info) {
161          if (info.getJavaClass().isAssignableFrom(javaClass)) {
162             return true;
163          }
164       }
165 //      for (int i = 0; i < _info.length; ++i)
166 //         if (_info[i].getJavaClass().isAssignableFrom(javaClass))
167 //            return true;
168 
169       return false;
170 
171    }
172 
173    /**
174     * Returns the associated string name for a given collection.
175     * 
176     * @param javaClass
177     *           the class to search collection handlers for
178     * @return the string name for the given collection type or null if no
179     *         association has been defined.
180     */
181    public static String getCollectionName(Class<?> javaClass) {
182       // -- Adjust javaClass for arrays, needed for arrays of
183       // -- primitives, except for byte[] which shouldn't
184       // -- use a collection handler
185       if (javaClass.isArray()) {
186          if (javaClass.getComponentType() != Byte.TYPE)
187             javaClass = Object[].class;
188       }
189 
190       // -- First check direct class equality, to provide a better match
191       // -- (for example in JDK 1.2 a Vector is also a Collection)
192       for (int i = 0; i < _info.length; ++i)
193          if (_info[i].getJavaClass().equals(javaClass))
194             return _info[i].getShortName();
195 
196       // -- handle Possible inheritance
197       for (int i = 0; i < _info.length; ++i)
198          if (_info[i].getJavaClass().isAssignableFrom(javaClass))
199             return _info[i].getShortName();
200 
201       return null;
202 
203    } // -- hasHandler
204 
205    /**
206     * Returns the collection's handler based on the Java class.
207     * 
208     * @param javaClass
209     *           The collection's Java class
210     * @return The collection handler
211     * @throws MappingException
212     *            The collection class is not supported
213     */
214    public static CollectionHandler getHandler(Class<?> javaClass) throws MappingException {
215       // -- Adjust javaClass for arrays, needed for arrays of
216       // -- primitives, except for byte[] which shouldn't
217       // -- use a collection handler
218       if (javaClass.isArray()) {
219          if (javaClass.getComponentType() != Byte.TYPE)
220             javaClass = Object[].class;
221       }
222 
223       // -- First check direct class equality, to provide a better match
224       // -- (for example in JDK 1.2 a Vector is also a Collection)
225       for (int i = 0; i < _info.length; ++i)
226          if (_info[i].getJavaClass().equals(javaClass))
227             return _info[i].handler;
228 
229       // -- handle Possible inheritence
230       for (int i = 0; i < _info.length; ++i)
231          if (_info[i].getJavaClass().isAssignableFrom(javaClass))
232             return _info[i].handler;
233 
234       throw new MappingException("mapping.noCollectionHandler", javaClass.getName());
235    }
236 
237    /**
238     * Returns true if the collection requires get/set methods.
239     * <tt>java.util</tt> collections only require a get method, but an array
240     * collection required both get and set methods.
241     * 
242     * @param javaClass
243     *           The collection's java class
244     * @return True if collection requires get/set methods, false if collection
245     *         requires only get method
246     * @throws MappingException
247     *            The collection class is not supported
248     */
249    public static boolean isGetSetCollection(Class<?> javaClass) throws MappingException {
250       for (int i = 0; i < _info.length; ++i)
251          if (_info[i].getJavaClass().equals(javaClass))
252             return _info[i].getSetCollection;
253       throw new MappingException("mapping.noCollectionHandler", javaClass.getName());
254    }
255 
256    static class Info {
257 
258       /**
259        * The short name of the collection (e.g. <tt>vector</tt>).
260        */
261       private final String shortName;
262 
263       /**
264        * The Java class of the collection (e.g. <tt>java.util.Vector</tt>).
265        */
266       private final Class<?> javaClass;
267 
268       /**
269        * The collection handler instance.
270        */
271       private final CollectionHandler handler;
272 
273       /**
274        * True for collections that require both get and set methods.
275        */
276       final boolean getSetCollection;
277 
278       Info(String shortName, Class<?> javaClass, boolean getSetCollection, CollectionHandler handler) {
279          this.shortName = shortName;
280          this.javaClass = javaClass;
281          this.handler = handler;
282          this.getSetCollection = getSetCollection;
283       }
284 
285       String getShortName() {
286          return shortName;
287       }
288 
289       Class<?> getJavaClass() {
290          return javaClass;
291       }
292 
293       CollectionHandler getHandler() {
294          return handler;
295       }
296 
297    }
298 
299    /**
300     * Enumerator for a null collection.
301     */
302    public static final class EmptyEnumerator<T> implements Enumeration<T>, Serializable {
303 
304       public boolean hasMoreElements() {
305          return false;
306       }
307 
308       public T nextElement() {
309          throw new NoSuchElementException();
310       }
311    }
312 
313 }