001package org.andromda.utils;
002
003import java.text.MessageFormat;
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.List;
007
008import org.apache.commons.lang.StringUtils;
009
010/**
011 * A utility object used by the code generator when it needs to convert an object
012 * from one data type to another.
013 *
014 * @author Joel Kozikowski
015 * @author Bob Fields
016 */
017public class JavaTypeConverter
018{
019    private List<String> javaTypeConversionIgnoreList = new ArrayList<String>();
020
021    /**
022     * Specifies a list of one or more fully qualified java types that should be ignored
023     * whenever a type conversion is done.  See Spring namespace property "javaTypeConversionIgnoreList"
024     * @param commaSeparatedIgnoreList comma separated list of types to be ignored
025     */
026    public void setJavaTypeConversionIgnoreList(String commaSeparatedIgnoreList)
027    {
028        javaTypeConversionIgnoreList = new ArrayList<String>();
029        if (commaSeparatedIgnoreList != null)
030        {
031            final String[] typeList = commaSeparatedIgnoreList.split("\\s*,\\s*");
032            javaTypeConversionIgnoreList.addAll(Arrays.asList(typeList));
033        }
034    }
035
036    private static class ConversionEntry
037    {
038        // - not private to increase performance of
039        //   inner class access
040        final String sourceType;
041        final String targetType;
042        final String conversionPattern;
043
044        public ConversionEntry(
045            final String sourceType,
046            final String targetType,
047            final String conversionPattern)
048        {
049            this.sourceType = sourceType;
050            this.targetType = targetType;
051            this.conversionPattern = conversionPattern;
052        }
053    }
054
055    private static ConversionEntry[] conversionTable =
056        {
057            new ConversionEntry("int", "java.lang.Integer", "new java.lang.Integer({0})"),
058            new ConversionEntry("int", "java.lang.String", "java.lang.String.valueOf({0})"),
059            new ConversionEntry("int", "long", "(long){0}"),
060            new ConversionEntry("int", "double", "(double){0}"),
061            new ConversionEntry("int", "float", "(float){0}"),
062            new ConversionEntry("int", "boolean", "({0} != 0)"),
063            new ConversionEntry("java.lang.Integer", "int", "({0} == null ? 0 : {0}.intValue())"),
064            new ConversionEntry("java.lang.Integer", "java.lang.String", "({0} == null ? null : {0}.toString())"),
065            new ConversionEntry("java.lang.String", "int", "({0} == null ? 0 : Integer.valueOf({0}).intValue())"),
066            new ConversionEntry("java.lang.String", "java.lang.Integer", "({0} == null ? null : Integer.valueOf({0}))"),
067
068            new ConversionEntry("long", "java.lang.Long", "new java.lang.Long({0})"),
069            new ConversionEntry("long", "java.lang.String", "java.lang.String.valueOf({0})"),
070            new ConversionEntry("long", "int", "(int){0}"),
071            new ConversionEntry("long", "double", "(double){0}"),
072            new ConversionEntry("long", "float", "(float){0}"),
073            new ConversionEntry("long", "boolean", "({0} != 0)"),
074            new ConversionEntry("java.lang.Long", "long", "({0} == null ? 0 : {0}.longValue())"),
075            new ConversionEntry("java.lang.Long", "java.lang.String", "({0} == null ? null : {0}.toString())"),
076            new ConversionEntry("java.lang.String", "long", "({0} == null ? 0 : Long.valueOf({0}).longValue())"),
077            new ConversionEntry("java.lang.String", "java.lang.Long", "({0} == null ? null : Long.valueOf({0}))"),
078
079            new ConversionEntry("double", "java.lang.Double", "new java.lang.Double({0})"),
080            new ConversionEntry("double", "java.lang.String", "java.lang.String.valueOf({0})"),
081            new ConversionEntry("double", "int", "(int){0}"),
082            new ConversionEntry("double", "long", "(long){0}"),
083            new ConversionEntry("double", "float", "(float){0}"),
084            new ConversionEntry("double", "boolean", "({0} != 0.0)"),
085            new ConversionEntry("java.lang.Double", "double", "({0} == null ? 0.0 : {0}.doubleValue())"),
086            new ConversionEntry("java.lang.Double", "java.lang.String", "({0} == null ? null : {0}.toString())"),
087            new ConversionEntry("java.lang.String", "double", "({0} == null ? 0.0 : Double.valueOf({0}).doubleValue())"),
088            new ConversionEntry("java.lang.String", "java.lang.Double", "({0} == null ? null : Double.valueOf({0}))"),
089
090            new ConversionEntry("float", "java.lang.Float", "new java.lang.Float({0})"),
091            new ConversionEntry("float", "java.lang.String", "java.lang.String.valueOf({0})"),
092            new ConversionEntry("float", "int", "(int){0}"),
093            new ConversionEntry("float", "long", "(long){0}"),
094            new ConversionEntry("float", "double", "(double){0}"),
095            new ConversionEntry("float", "boolean", "({0} != 0.0f)"),
096            new ConversionEntry("java.lang.Float", "float", "({0} == null ? 0.0f : {0}.floatValue())"),
097            new ConversionEntry("java.lang.Float", "java.lang.String", "({0} == null ? null : {0}.toString())"),
098            new ConversionEntry("java.lang.String", "float", "({0} == null ? 0.0f : Float.valueOf({0}).floatValue())"),
099            new ConversionEntry("java.lang.String", "java.lang.Float", "({0} == null ? null : Float.valueOf({0}))"),
100
101            new ConversionEntry("int", "java.math.BigInteger", "java.math.BigInteger.valueOf((long){0})"),
102            new ConversionEntry("java.math.BigInteger", "int", "({0} == null ? 0 : {0}.intValue())"),
103            new ConversionEntry("long", "java.math.BigInteger", "java.math.BigInteger.valueOf({0})"),
104            new ConversionEntry("java.math.BigInteger", "long", "({0} == null ? 0 : {0}.longValue())"),
105            new ConversionEntry("java.math.BigInteger", "float", "({0} == null ? 0.0f : {0}.floatValue())"),
106            new ConversionEntry("float", "java.math.BigInteger", "new java.math.BigDecimal((double){0}).toBigInteger()"),
107            new ConversionEntry("java.math.BigInteger", "double", "({0} == null ? 0.0 : {0}.doubleValue())"),
108            new ConversionEntry("double", "java.math.BigInteger", "new java.math.BigDecimal({0}).toBigInteger()"),
109            new ConversionEntry("java.lang.String", "java.math.BigInteger", "new java.math.BigInteger({0})"),
110            new ConversionEntry("java.math.BigInteger", "java.lang.String", "({0} == null ? null : {0}.toString())"),
111            new ConversionEntry("java.math.BigDecimal", "java.math.BigInteger", "({0} == null ? null : {0}.toBigInteger())"),
112            new ConversionEntry("java.lang.Integer", "java.math.BigInteger", "({0} == null ? null : java.math.BigInteger.valueOf((long){0}.intValue()))"),
113            new ConversionEntry("java.math.BigInteger", "java.lang.Integer", "({0} == null ? null : new java.lang.Integer({0}.intValue()))"),
114            new ConversionEntry("java.lang.Long", "java.math.BigInteger", "({0} == null ? null : java.math.BigInteger.valueOf({0}.longValue()))"),
115            new ConversionEntry("java.math.BigInteger", "java.lang.Long", "({0} == null ? null : new java.lang.Long({0}.longValue()))"),
116            new ConversionEntry("java.lang.Float", "java.math.BigInteger", "({0} == null ? null : new java.math.BigDecimal((double){0}.floatValue()).toBigInteger())"),
117            new ConversionEntry("java.lang.Double", "java.math.BigInteger", "({0} == null ? null : new java.math.BigDecimal({0}.doubleValue()).toBigInteger())"),
118
119            new ConversionEntry("int", "java.math.BigDecimal", "java.math.BigDecimal.valueOf((long){0})"),
120            new ConversionEntry("java.math.BigDecimal", "int", "({0} == null ? 0 : {0}.intValue())"),
121            new ConversionEntry("long", "java.math.BigDecimal", "java.math.BigDecimal.valueOf({0})"),
122            new ConversionEntry("java.math.BigDecimal", "long", "({0} == null ? 0 : {0}.longValue())"),
123            new ConversionEntry("java.math.BigDecimal", "float", "({0} == null ? 0.0f : {0}.floatValue())"),
124            new ConversionEntry("float", "java.math.BigDecimal", "new java.math.BigDecimal((double){0})"),
125            new ConversionEntry("java.math.BigDecimal", "double", "({0} == null ? 0.0 : {0}.doubleValue())"),
126            new ConversionEntry("double", "java.math.BigDecimal", "new java.math.BigDecimal({0})"),
127            new ConversionEntry("java.lang.String", "java.math.BigDecimal", "new java.math.BigDecimal({0})"),
128            new ConversionEntry("java.math.BigDecimal", "java.lang.String", "({0} == null ? null : {0}.toString())"),
129            new ConversionEntry("java.math.BigInteger", "java.math.BigDecimal", "new java.math.BigDecimal({0})"),
130            new ConversionEntry("java.lang.Integer", "java.math.BigDecimal", "({0} == null ? null : java.math.BigDecimal.valueOf((long){0}.intValue()))"),
131            new ConversionEntry("java.math.BigDecimal", "java.lang.Integer", "({0} == null ? null : new java.lang.Integer({0}.intValue()))"),
132            new ConversionEntry("java.lang.Long", "java.math.BigDecimal", "({0} == null ? null : java.math.BigDecimal.valueOf({0}.longValue()))"),
133            new ConversionEntry("java.math.BigDecimal", "java.lang.Long", "({0} == null ? null : new java.lang.Long({0}.longValue()))"),
134            new ConversionEntry("java.lang.Float", "java.math.BigDecimal", "({0} == null ? null : new java.math.BigDecimal((double){0}.floatValue()))"),
135            new ConversionEntry("java.lang.Double", "java.math.BigDecimal", "({0} == null ? null : new java.math.BigDecimal({0}.doubleValue()))"),
136
137            new ConversionEntry("boolean", "java.lang.Boolean", "new java.lang.Boolean({0})"),
138            new ConversionEntry("boolean", "java.lang.String", "java.lang.String.valueOf({0})"),
139            new ConversionEntry("boolean", "int", "({0} ? 1 : 0)"),
140            new ConversionEntry("boolean", "long", "({0} ? 1 : 0)"),
141            new ConversionEntry("boolean", "double", "({0} ? 1.0 : 0.0)"),
142            new ConversionEntry("boolean", "float", "({0} ? 1.0f : 0.0f)"),
143            new ConversionEntry("java.lang.Boolean", "boolean", "({0} == null ? false : {0}.booleanValue())"),
144            new ConversionEntry("java.lang.Boolean", "java.lang.String", "({0} == null ? null : {0}.toString())"),
145            new ConversionEntry("java.lang.String", "boolean", "({0} == null ? false : Boolean.valueOf({0}).booleanValue())"),
146            new ConversionEntry("java.lang.String", "java.lang.Boolean", "({0} == null ? null : Boolean.valueOf({0}))"),
147
148            new ConversionEntry("string", "java.lang.String", "{0}"),
149            new ConversionEntry("java.lang.String", "string", "{0}"),
150
151            new ConversionEntry("java.util.Date", "java.lang.String",
152                "({0} == null ? null : new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ssZ\").format({0}))"),
153            new ConversionEntry("java.sql.Timestamp", "java.lang.String",
154                "({0} == null ? null : new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ssZ\").format({0}))"),
155            new ConversionEntry("java.sql.Time", "java.lang.String",
156                    "({0} == null ? null : new java.text.SimpleDateFormat(\"HH:mm:ssZ\").format({0}))"),
157            new ConversionEntry("java.sql.Date", "java.lang.String",
158                "({0} == null ? null : new java.text.SimpleDateFormat(\"yyyy-MM-dd\").format({0}))"),
159            new ConversionEntry("java.lang.String", "java.util.Date",
160                "({0} == null ? null : new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ssZ\").parse({0}))"),
161            new ConversionEntry("java.lang.String", "java.sql.Timestamp",
162                "({0} == null ? null : new java.sql.Timestamp(new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ssZ\").parse({0}).getTime()))"),
163            new ConversionEntry("java.lang.String", "java.sql.Time",
164                "({0} == null ? null : new java.sql.Time(new java.text.SimpleDateFormat(\"HH:mm:ssZ\").parse({0}).getTime()))"),
165            new ConversionEntry("java.lang.String", "java.sql.Date",
166                "({0} == null ? null : new java.sql.Date(new java.text.SimpleDateFormat(\"yyyy-MM-dd\").parse({0}).getTime()))"),
167            new ConversionEntry("java.sql.Timestamp", "java.util.Date", "{0}"),
168            new ConversionEntry("java.sql.Timestamp", "java.sql.Time", "({0} == null ? null : new java.sql.Time({0}.getTime()))"),
169            new ConversionEntry("java.sql.Timestamp", "java.sql.Date", "({0} == null ? null : new java.sql.Date({0}.getTime()))"),
170            new ConversionEntry("java.sql.Time", "java.util.Date", "{0}"),
171            new ConversionEntry("java.sql.Time", "java.sql.Timestamp", "({0} == null ? null : new java.sql.Timestamp({0}.getTime()))"),
172            new ConversionEntry("java.sql.Date", "java.util.Date", "{0}"),
173            new ConversionEntry("java.sql.Date", "java.sql.Timestamp", "({0} == null ? null : new java.sql.Timestamp({0}.getTime()))"),
174            new ConversionEntry("java.util.Date", "java.sql.Timestamp", "({0} == null ? null : new java.sql.Timestamp({0}.getTime()))"),
175            new ConversionEntry("java.util.Date", "java.sql.Time", "({0} == null ? null : new java.sql.Time({0}.getTime()))"),
176            new ConversionEntry("java.util.Date", "java.sql.Date", "({0} == null ? null : new java.sql.Date({0}.getTime()))"),
177            new ConversionEntry("java.util.Date", "long", "({0} == null ? 0 : {0}.getTime())"),
178            new ConversionEntry("java.sql.Timestamp", "long", "({0} == null ? 0 : {0}.getTime())"),
179            new ConversionEntry("java.sql.Time", "long", "({0} == null ? 0 : {0}.getTime())"),
180            new ConversionEntry("java.sql.Date", "long", "({0} == null ? 0 : {0}.getTime())"),
181            new ConversionEntry("long", "java.sql.Date", "new java.sql.Date({0})"),
182            new ConversionEntry("long", "java.sql.Time", "new java.sql.Time({0})"),
183            new ConversionEntry("long", "java.sql.Timestamp", "new java.sql.Timestamp({0})"),
184            new ConversionEntry("long", "java.util.Date", "new java.util.Date({0})"),
185
186            new ConversionEntry("java.lang.Character", "java.lang.String", "({0} == null ? null : {0}.toString())"),
187            new ConversionEntry("java.lang.String", "java.lang.Character", "({0} == null || {0}.length() == 0 ? null : java.lang.Character.valueOf({0}.charAt(0)))"),
188
189            new ConversionEntry("java.lang.Object", "java.lang.String", "{0} == null ? null : {0}.toString()"),
190            new ConversionEntry("java.lang.Object", "java.lang.Integer", "{0} == null ? null : {0}.toString()"),
191            new ConversionEntry("java.lang.Object", "java.lang.Long", "{0} == null ? null : {0}.toString()"),
192            new ConversionEntry("java.lang.Object", "java.lang.Float", "{0} == null ? null : {0}.toString()"),
193            new ConversionEntry("java.lang.Object", "java.lang.Double", "{0} == null ? null : {0}.toString()"),
194            new ConversionEntry("java.lang.Object", "java.lang.Character", "({0} == null || {0}.toString().length() == 0 ? null : java.lang.Character.valueOf({0}.toString().charAr(0)))"),
195            new ConversionEntry("java.lang.String", "java.lang.Object", "{0}"),
196            new ConversionEntry("java.lang.Integer", "java.lang.Object", "{0}"),
197            new ConversionEntry("java.lang.Long", "java.lang.Object", "{0}"),
198            new ConversionEntry("java.lang.Float", "java.lang.Object", "{0}"),
199            new ConversionEntry("java.lang.Double", "java.lang.Object", "{0}"),
200            new ConversionEntry("java.lang.Character", "java.lang.Object", "{0}"),
201            new ConversionEntry("java.lang.Character", "char", "({0} == null ? 0 : {0}.charValue())"),
202            new ConversionEntry("char", "java.lang.Character", "java.lang.Character.valueOf({0})"),
203        };
204
205    /**
206     * Attempts to code a type conversion from the specified source value to the target
207     * type that can be used on the right hand side of an assignment. If such a conversion
208     * exists, the Java code fragment to do that will be returned. If no such conversion exists,
209     * null is returned.
210     * @param sourceType The fully qualified data type of the source value
211     * @param sourceValue The actual source value (usually a variable name)
212     * @param targetType The fully qualified data type of the target value
213     * @return The converted value, or null if no conversion can be done.
214     */
215    public String typeConvert(
216        final String sourceType,
217        final String sourceValue,
218        final String targetType)
219    {
220        String convertedValue;
221
222        if (StringUtils.isBlank(sourceType) || StringUtils.isBlank(targetType) ||
223            javaTypeConversionIgnoreList.contains(sourceType) || javaTypeConversionIgnoreList.contains(targetType))
224        {
225            convertedValue = null;
226        }
227        else if (sourceType.equals(targetType))
228        {
229            convertedValue = sourceValue;
230        }
231        else
232        {
233            convertedValue = null;
234
235            for (int i = 0; i < conversionTable.length && convertedValue == null; i++)
236            {
237                if (conversionTable[i].sourceType.equals(sourceType) &&
238                    conversionTable[i].targetType.equals(targetType))
239                {
240                    convertedValue =
241                        MessageFormat.format(
242                            conversionTable[i].conversionPattern,
243                                sourceValue);
244                } // if
245            } // for
246
247            if (convertedValue == null && sourceType.startsWith("java.lang.") && !sourceType.endsWith("[]"))
248            {
249                // If source is a primitive wrapper, try to convert
250                // to the base primitive first...
251                String primitiveSource = sourceType.substring(10).toLowerCase();
252                if ("integer".equals(primitiveSource))
253                {
254                    primitiveSource = "int";
255                }
256                else if ("character".equals(primitiveSource)) {
257                    primitiveSource = "char";
258                }
259
260                String interimValue = typeConvert(
261                        sourceType,
262                        sourceValue,
263                        primitiveSource);
264
265                if (interimValue != null)
266                {
267                    convertedValue = typeConvert(
268                            primitiveSource,
269                            interimValue,
270                            targetType);
271                }
272            }
273
274            if (convertedValue == null && targetType.startsWith("java.lang.") && !targetType.endsWith("[]"))
275            {
276                // One last try - if target is a primitive wrapper, try to convert
277                // to the base primitive first...
278                String primitiveTarget = targetType.substring(10).toLowerCase();
279                if ("integer".equals(primitiveTarget))
280                {
281                    primitiveTarget = "int";
282                }
283                else if ("character".equals(primitiveTarget))
284                {
285                    primitiveTarget = "char";
286                }
287
288                String interimValue = typeConvert(
289                        sourceType,
290                        sourceValue,
291                        primitiveTarget);
292                if (interimValue != null)
293                {
294                    convertedValue = typeConvert(
295                            primitiveTarget,
296                            interimValue,
297                            targetType);
298                }
299            }
300        }
301
302        return convertedValue;
303    }
304
305    private static final List<String> javaLangList = new ArrayList<String>();
306    /**
307     * Creates a java.lang. fully qualified name from the given <code>name</code>,
308     * inserting 'java.lang.' in front if needed. Many EJB3 and Hibernate configuration files
309     * require java.lang. in the type name, where Java code does not (gives compiler warning).
310     * This allows UML Primitive types to be modeled and mapped without java.lang. implementation.
311     *
312     * @param name the name of the model element.
313     * @return the java.lang. fully qualified name, if needed.
314     */
315    public static String getJavaLangTypeName(String name)
316    {
317        if (StringUtils.isBlank(name))
318        {
319            return name;
320        }
321        synchronized (javaLangList)
322        {
323            if (javaLangList.isEmpty())
324            {
325                javaLangList.add("String");
326                javaLangList.add("Boolean");
327                javaLangList.add("Integer");
328                javaLangList.add("Long");
329                javaLangList.add("Double");
330                javaLangList.add("Object");
331                javaLangList.add("Short");
332                javaLangList.add("Character");
333                javaLangList.add("Byte");
334                javaLangList.add("Float");
335                javaLangList.add("Number");
336                javaLangList.add("Math");
337                javaLangList.add("Class");
338                javaLangList.add("Exception");
339                javaLangList.add("Enum");
340            }
341        }
342        // If type has no package and is not the same as lowercase (i.e. primitive type)...
343        if (!(name.indexOf('.')>-1) && !(name.equals(name.toLowerCase())))
344        {
345            if (javaLangList.contains(name)
346                || (name.endsWith("[]") && javaLangList.contains(name.substring(0, name.length()-2))))
347            {
348                name = "java.lang." + name;
349            }
350        }
351        return name;
352    }
353}