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