View Javadoc

1   // Copyright 2011 Leo Przybylski. All rights reserved.
2   //
3   // Redistribution and use in source and binary forms, with or without modification, are
4   // permitted provided that the following conditions are met:
5   //
6   //    1. Redistributions of source code must retain the above copyright notice, this list of
7   //       conditions and the following disclaimer.
8   //
9   //    2. Redistributions in binary form must reproduce the above copyright notice, this list
10  //       of conditions and the following disclaimer in the documentation and/or other materials
11  //       provided with the distribution.
12  //
13  // THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED
14  // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
15  // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
16  // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17  // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19  // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20  // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
21  // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22  //
23  // The views and conclusions contained in the software and documentation are those of the
24  // authors and should not be interpreted as representing official policies, either expressed
25  // or implied, of Leo Przybylski.
26  package liquibase.database.typeconversion.ext;
27  
28  import liquibase.change.ColumnConfig;
29  import liquibase.database.Database;
30  import liquibase.database.core.*;
31  import liquibase.database.structure.Column;
32  import liquibase.database.structure.type.*;
33  import liquibase.database.typeconversion.TypeConverter;
34  import liquibase.exception.DateParseException;
35  import liquibase.exception.UnexpectedLiquibaseException;
36  import liquibase.logging.LogFactory;
37  import liquibase.statement.DatabaseFunction;
38  import liquibase.util.StringUtils;
39  
40  import java.math.BigDecimal;
41  import java.math.BigInteger;
42  import java.lang.reflect.Field;
43  import java.sql.Types;
44  import java.text.ParseException;
45  import java.util.Arrays;
46  import java.util.List;
47  
48  import static liquibase.ext.Constants.EXTENSION_PRIORITY;
49  
50  /**
51   * @author Leo Przybylski
52   */
53  public abstract class AbstractTypeConverter extends liquibase.database.typeconversion.core.AbstractTypeConverter implements liquibase.database.typeconversion.TypeConverter {
54      protected static final List<Integer> oneParam = Arrays.asList(
55          Types.CHAR,
56          -15, // Types.NCHAR in java 1.6,
57          Types.VARCHAR,
58          -9, //Types.NVARCHAR in java 1.6,
59          Types.VARBINARY,
60          Types.DOUBLE,
61          Types.FLOAT
62          );
63          
64      protected static final List<Integer> twoParams = Arrays.asList(
65          Types.DECIMAL,
66          Types.NUMERIC,
67          Types.REAL
68          );
69  
70      public int getPriority() {
71          return EXTENSION_PRIORITY;
72      }
73  
74      @Override
75      public Object convertDatabaseValueToObject(Object value, int databaseDataType, int firstParameter, int secondParameter, Database database) throws ParseException {
76          if (value == null) {
77              return null;
78          } else if (value instanceof String) {
79              return convertToCorrectObjectType(((String) value).trim().replaceFirst("^'", "").replaceFirst("'$", ""), databaseDataType, firstParameter, secondParameter, database);
80          } else {
81              return value;
82          }
83      }
84  
85      @Override
86      public String convertToDatabaseTypeString(Column referenceColumn, Database database) {        
87          final StringBuilder retval = new StringBuilder();
88          try {
89              retval.append(getSqlTypeName(referenceColumn.getDataType()));
90          }
91          catch (Exception e) {
92              retval.append(referenceColumn.getTypeName());
93          }
94  
95          
96          final boolean hasOneParam  = oneParam.contains(referenceColumn.getDataType());
97          final boolean hasTwoParams = twoParams.contains(referenceColumn.getDataType());
98          
99  
100         if (hasOneParam || hasTwoParams) {
101             retval.append("(").append(referenceColumn.getColumnSize());
102             if (hasTwoParams) {
103                 retval.append(",").append(referenceColumn.getDecimalDigits());
104             }
105             retval.append(")");
106         }
107 
108         
109         return retval.toString();
110     }
111 
112     /**
113      * Convert the type value gotten from the metadata which is a value from {@link Types} to a {@link String} value
114      * that can be used in an SQL statement. Example output:
115      * <ul>
116      *   <li>java.sql.Types.DECIMAL(25,0)</li>
117      *   <li>java.sql.Types.BIGINT</li>
118      *   <li>java.sql.Types.VARCHAR(255)</li>
119      * </ul>
120      *
121      * @param type int value found in {@linK Types}
122      * @return String value including package of the type name.
123      */
124     protected String getSqlTypeName(final int type) throws Exception {
125         for (final Field field : Types.class.getFields()) {
126             final int sql_type = field.getInt(null);
127             if (type == sql_type) {
128                 return "java.sql.Types." + field.getName();
129             }
130         }
131         return null;
132     }
133 
134     @Override
135     protected DataType getDataType(String columnTypeString, Boolean autoIncrement, String dataTypeName, String precision, String additionalInformation) {
136         // Translate type to database-specific type, if possible
137         DataType returnTypeName = null;
138         if (dataTypeName.equalsIgnoreCase("BIGINT")) {
139             returnTypeName = getBigIntType();
140         } else if (dataTypeName.equalsIgnoreCase("NUMBER") 
141                    || dataTypeName.equalsIgnoreCase("DECIMAL")
142                    || dataTypeName.equalsIgnoreCase("NUMERIC")) {
143             returnTypeName = getNumberType();
144         } else if (dataTypeName.equalsIgnoreCase("BLOB")) {
145             returnTypeName = getBlobType();
146         } else if (dataTypeName.equalsIgnoreCase("BOOLEAN")) {
147             returnTypeName = getBooleanType();
148         } else if (dataTypeName.equalsIgnoreCase("CHAR")) {
149             returnTypeName = getCharType();
150         } else if (dataTypeName.equalsIgnoreCase("CLOB")) {
151             returnTypeName = getClobType();
152         } else if (dataTypeName.equalsIgnoreCase("CURRENCY")) {
153             returnTypeName = getCurrencyType();
154         } else if (dataTypeName.equalsIgnoreCase("DATE") || dataTypeName.equalsIgnoreCase(getDateType().getDataTypeName())) {
155             returnTypeName = getDateType();
156         } else if (dataTypeName.equalsIgnoreCase("DATETIME") || dataTypeName.equalsIgnoreCase(getDateTimeType().getDataTypeName())) {
157             returnTypeName = getDateTimeType();
158         } else if (dataTypeName.equalsIgnoreCase("DOUBLE")) {
159             returnTypeName = getDoubleType();
160         } else if (dataTypeName.equalsIgnoreCase("FLOAT")) {
161             returnTypeName = getFloatType();
162         } else if (dataTypeName.equalsIgnoreCase("INT")) {
163             returnTypeName = getIntType();
164         } else if (dataTypeName.equalsIgnoreCase("INTEGER")) {
165             returnTypeName = getIntType();
166         } else if (dataTypeName.equalsIgnoreCase("LONGBLOB")) {
167             returnTypeName = getLongBlobType();
168         } else if (dataTypeName.equalsIgnoreCase("LONGVARBINARY")) {
169             returnTypeName = getBlobType();
170         } else if (dataTypeName.equalsIgnoreCase("LONGVARCHAR")) {
171             returnTypeName = getClobType();
172         } else if (dataTypeName.equalsIgnoreCase("SMALLINT")) {
173             returnTypeName = getSmallIntType();
174         } else if (dataTypeName.equalsIgnoreCase("TEXT")) {
175             returnTypeName = getClobType();
176         } else if (dataTypeName.equalsIgnoreCase("TIME") || dataTypeName.equalsIgnoreCase(getTimeType().getDataTypeName())) {
177             returnTypeName = getTimeType();
178         } else if (dataTypeName.toUpperCase().contains("TIMESTAMP")) {
179             returnTypeName = getDateTimeType();
180         } else if (dataTypeName.equalsIgnoreCase("TINYINT")) {
181             returnTypeName = getTinyIntType();
182         } else if (dataTypeName.equalsIgnoreCase("UUID")) {
183             returnTypeName = getUUIDType();
184         } else if (dataTypeName.equalsIgnoreCase("VARCHAR")) {
185             returnTypeName = getVarcharType();
186         } else if (dataTypeName.equalsIgnoreCase("NVARCHAR")) {
187             returnTypeName = getNVarcharType();
188         } else {
189             return new CustomType(columnTypeString,0,2);
190         }
191 
192         if (returnTypeName == null) {
193             throw new UnexpectedLiquibaseException("Could not determine " + dataTypeName + " for " + this.getClass().getName());
194         }
195         addPrecisionToType(precision, returnTypeName);
196         returnTypeName.setAdditionalInformation(additionalInformation);
197 
198          return returnTypeName;
199     }
200 }