root/trunk/src/java/org/jcoderz/phoenix/cmpgen2/TypeMapping.java

Revision 1011, 11.3 kB (checked in by amandel, 4 years ago)

Aligned svn keyword settings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.phoenix.cmpgen2;
34
35import java.math.BigDecimal;
36import java.sql.Date;
37import java.sql.Timestamp;
38import java.util.Arrays;
39import java.util.HashSet;
40import java.util.List;
41import java.util.Set;
42
43import org.jcoderz.commons.util.Constants;
44import org.jcoderz.phoenix.sqlparser.ColumnSpec;
45import org.jcoderz.phoenix.sqlparser.NumericAttribute;
46
47
48/**
49 * @author Albrecht Messner
50 */
51public final class TypeMapping
52{
53   private static final String[] JAVA_PRIMITIVE_TYPES = {
54      Byte.TYPE.getName(),
55      Short.TYPE.getName(),
56      Integer.TYPE.getName(),
57      Long.TYPE.getName(),
58      Float.TYPE.getName(),
59      Double.TYPE.getName(),
60      Character.TYPE.getName(),
61      Boolean.TYPE.getName(),
62      "byte[]"
63   };
64   
65   private static final String[] PRIMITIVE_TYPE_WRAPPERS = {
66      Byte.class.getName(),
67      Short.class.getName(),
68      Integer.class.getName(),
69      Long.class.getName(),
70      Float.class.getName(),
71      Double.class.getName(),
72      Character.class.getName(),
73      Boolean.class.getName(),
74      "byte[]"
75   };
76   
77   // these get mapped to java.lang.String
78   private static final String[] STRING_TYPES = {
79      "CHAR",
80      "CHAR2",
81      "NCHAR",
82      "NCHAR2",
83      "VARCHAR",
84      "VARCHAR2",
85      "NVARCHAR",
86      "NVARCHAR2"
87      };
88
89   private static final String[] TIMESTAMP_TYPES = {
90      "TIMESTAMP"
91      };
92   
93   private static final String[] DATE_TYPES = {
94      "DATE"
95      };
96   
97   // these get mapped to numeric types
98   private static final String[] NUMERIC_TYPES = {
99      "NUMBER",
100      "NUMERIC",
101      "DECIMAL",
102      "INTEGER",
103      "INT",
104      "FLOAT",
105      "REAL"
106      };
107     
108   private static final String[] FLOAT_TYPES = {
109      "FLOAT",
110      "REAL"
111   };
112   
113   private static final int INTEGER_PRECISION_LIMIT = 10;
114   private static final int LONG_PRECISION_LIMIT = 19;
115
116
117   // put all arrays into hash sets for faster lookup
118   private static final Set STRING_TYPE_SET = new HashSet();
119   private static final Set NUMERIC_TYPE_SET = new HashSet();
120   private static final Set TIMESTAMP_TYPE_SET = new HashSet();
121   private static final Set DATE_TYPE_SET = new HashSet();
122   private static final Set FLOAT_TYPE_SET = new HashSet();
123   private static final Set JAVA_PRIMITIVE_TYPE_SET = new HashSet();
124
125   static
126   {
127      STRING_TYPE_SET.addAll(Arrays.asList(STRING_TYPES));
128      NUMERIC_TYPE_SET.addAll(Arrays.asList(NUMERIC_TYPES));
129      TIMESTAMP_TYPE_SET.addAll(Arrays.asList(TIMESTAMP_TYPES));
130      DATE_TYPE_SET.addAll(Arrays.asList(DATE_TYPES));
131      FLOAT_TYPE_SET.addAll(Arrays.asList(FLOAT_TYPES));
132      JAVA_PRIMITIVE_TYPE_SET.addAll(Arrays.asList(JAVA_PRIMITIVE_TYPES));
133   }
134   
135   /**
136    * private constructor to avoid instantiation
137    */
138   private TypeMapping ()
139   {
140   }
141   
142   /**
143    * Finds the appropriate type mapping from a given column spec.
144    *
145    * Note that the type returned here is the "simple" type as it is stored
146    * in the database, not the "complex" type.
147    *
148    * @param column the column specification
149    * @param fullyQualified whether the type should contain the package
150    *        name or not
151    * @return the simple java type used to store the column in the db
152    * @throws CmpGeneratorException if no type mapping can be found
153    */
154   public static String getJavaType (
155         ColumnSpec column,
156         boolean fullyQualified)
157         throws CmpGeneratorException
158   {
159      String javaType = column.getJavaType();
160      if (javaType == null)
161      {
162         // this gets a mapping for character data types
163         javaType = getTypeMapping(column.getColumnType());
164         
165         // no character type, try to find a numeric type
166         if (javaType == null)
167         {
168            final List sqlTypeAttributes = column.getDatatypeAttributes();
169            int att1 = 0, att2 = 0;
170            if (sqlTypeAttributes.size() > 0)
171            {
172               att1 = ((NumericAttribute) sqlTypeAttributes.get(0)).getNumber();
173               if (sqlTypeAttributes.size() > 1)
174               {
175                  att2 = ((NumericAttribute) sqlTypeAttributes.get(1))
176                      .getNumber();
177               }
178            }
179            javaType =
180               getNumberTypeMapping(
181                  column.getColumnType(),
182                  att1,
183                  att2);
184         }
185      }
186      else
187      {
188         // a java type has been specified
189         final String loadMethod = column.getLoadMethod();
190         if (loadMethod != null)
191         {
192            // hey! it's a complex type
193            // the "simple" java type can be found in the signature of the
194            // load method
195            final int openParen = loadMethod.indexOf('(');
196            final int closeParen = loadMethod.indexOf(')');
197            if (openParen == -1
198               || closeParen == -1
199               || closeParen <= openParen)
200            {
201               throw new CmpGeneratorException(
202                  loadMethod + " is an invalid load method signature");
203            }
204            javaType = loadMethod.substring(openParen + 1, closeParen).trim();
205            if (javaType.length() <= 0)
206            {
207               throw new CmpGeneratorException(
208                  loadMethod + " is an invalid load method signature");
209            }
210         }
211      }
212     
213      if (javaType == null)
214      {
215         throw new CmpGeneratorException("No type mapping found for column "
216            + column);
217      }
218
219      // ok, now we've got a java type. if it is a primitive type
220      // and the column is nullable, then we must use the wrapper object
221      // instead
222      if (isPrimitiveType(javaType) && !column.isNotNull())
223      {
224         javaType = primitiveToObject(javaType);
225      }
226
227      // check if we should return the type fully qualified
228      if (!fullyQualified)
229      {
230         javaType = unqualifyType(javaType);
231      }
232
233      return javaType;
234   }
235
236   /**
237    * Returns everything after the last dot in a type name.
238    * @param typeName a java type name
239    * @return the unqualified type name, or the argument if
240    *         it was not a qualified java type name
241    */
242   public static String unqualifyType (String typeName)
243   {
244      final int dotIndex = typeName.lastIndexOf('.');
245      return typeName.substring(dotIndex + 1);
246   }
247
248   /**
249    * Returns the fully qualified java type to which an SQL type is mapped.
250    *
251    * @param sqlType the name of the sql type
252    * @return the FQ type name of the java type, or null if this method can not
253    *         find a type mapping
254    */
255   public static String getTypeMapping (String sqlType)
256   {
257      String javaType;
258     
259      final String s = sqlType.toUpperCase(Constants.SYSTEM_LOCALE);
260      if (STRING_TYPE_SET.contains(s))
261      {
262         javaType = String.class.getName();
263      }
264      else if (TIMESTAMP_TYPE_SET.contains(s))
265      {
266         javaType = Timestamp.class.getName();
267      }
268      else if (DATE_TYPE_SET.contains(s))
269      {
270         javaType = Date.class.getName();
271      }
272      else if (NUMERIC_TYPE_SET.contains(s))
273      {
274         javaType = null;
275      }
276      else
277      {
278         javaType = Object.class.getName();
279      }
280     
281      return javaType;
282   }
283   
284   /**
285    * Returns a type mapping for numeric types.
286    *
287    * @param sqlType the name of the sql type
288    * @param precision the precision of the sql type
289    * @param scale the scale of the sql type, or 0 if no scale given
290    * @return the name of the appropriate java type
291    */
292   public static String getNumberTypeMapping (
293      String sqlType,
294      int precision,
295      int scale)
296   {
297      String javaType;
298      final String s = sqlType.toUpperCase(Constants.SYSTEM_LOCALE);
299
300      if (precision < 0 || scale < 0)
301      {
302         throw new IllegalArgumentException(
303                 "Scale and precision must be non-negative");
304      }
305
306      if (NUMERIC_TYPE_SET.contains(s))
307      {
308         if (scale > 0 || FLOAT_TYPE_SET.contains(s))
309         {
310            return BigDecimal.class.getName();
311         }
312         else
313         {
314            if (precision < INTEGER_PRECISION_LIMIT)
315            {
316               javaType = Integer.TYPE.getName();
317            }
318            else if (precision < LONG_PRECISION_LIMIT)
319            {
320               javaType = Long.TYPE.getName();
321            }
322            else
323            {
324               javaType = BigDecimal.class.getName();
325            }
326         }
327      }
328      else
329      {
330         throw new IllegalArgumentException(s + " is not a numeric SQL type");
331      }
332     
333      return javaType;
334   }
335   
336   /**
337    * Determines whether a given type is primitive.
338    *
339    * @param type the name of a java type
340    * @return true if the type is primitive, false otherwise
341    */
342   public static boolean isPrimitiveType (String type)
343   {
344      boolean result = false;
345      if (JAVA_PRIMITIVE_TYPE_SET.contains(type))
346      {
347         result = true;
348      }
349      return result;
350   }
351
352   /**
353    * Maps a java primitive type to its corresponding wrapper object.
354    * @param primitiveType the name of a primitive type
355    * @return the corresponding wrapper object
356    * @throws IllegalArgumentException if primitiveType is not a primitive type
357    */
358   public static String primitiveToObject (String primitiveType)
359   {
360      if (!isPrimitiveType(primitiveType))
361      {
362         throw new IllegalArgumentException(
363               "Can't map "
364               + primitiveType
365               + " to its Object wrapper because it is not a primitive type");
366      }
367      String objectWrapperName = null;
368      for (int i = 0; i < JAVA_PRIMITIVE_TYPES.length; i++)
369      {
370         if (JAVA_PRIMITIVE_TYPES[i].equals(primitiveType))
371         {
372            objectWrapperName = PRIMITIVE_TYPE_WRAPPERS[i];
373            break;
374         }
375      }
376      return objectWrapperName;
377   }
378
379}
Note: See TracBrowser for help on using the browser.