001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.jdbc; 017 018import java.math.BigDecimal; 019import java.sql.ResultSetMetaData; 020import java.sql.SQLException; 021import java.sql.Timestamp; 022import java.sql.Types; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.UUID; 027import javax.jcr.PropertyType; 028import javax.jcr.RepositoryException; 029import javax.jcr.Value; 030import javax.jcr.ValueFormatException; 031import org.modeshape.jdbc.types.BlobTransform; 032import org.modeshape.jdbc.types.BooleanTransform; 033import org.modeshape.jdbc.types.DateTransform; 034import org.modeshape.jdbc.types.DecimalTransform; 035import org.modeshape.jdbc.types.DoubleTransform; 036import org.modeshape.jdbc.types.LongTransform; 037import org.modeshape.jdbc.types.StringTransform; 038import org.modeshape.jdbc.types.UUIDTransform; 039 040/** 041 * Provides functionality to convert from JCR {@link PropertyType}s and JDBC types. 042 */ 043public final class JcrType { 044 045 private static final int UUID_LENGTH = UUID.randomUUID().toString().length(); 046 private static final Map<String, JcrType> TYPE_INFO; 047 048 public static final class DefaultDataTypes { 049 public static final String STRING = PropertyType.TYPENAME_STRING; 050 public static final String BOOLEAN = PropertyType.TYPENAME_BOOLEAN; 051 public static final String LONG = PropertyType.TYPENAME_LONG; 052 public static final String DOUBLE = PropertyType.TYPENAME_DOUBLE; 053 public static final String DECIMAL = PropertyType.TYPENAME_DECIMAL; 054 public static final String DATE = PropertyType.TYPENAME_DATE; 055 public static final String URI = PropertyType.TYPENAME_URI; 056 public static final String WEAK_REF = PropertyType.TYPENAME_WEAKREFERENCE; 057 public static final String UNDEFINED = PropertyType.TYPENAME_UNDEFINED; 058 public static final String BINARY = PropertyType.TYPENAME_BINARY; 059 public static final String REFERENCE = PropertyType.TYPENAME_REFERENCE; 060 public static final String PATH = PropertyType.TYPENAME_STRING; 061 public static final String NAME = PropertyType.TYPENAME_STRING; 062 } 063 064 static { 065 Map<String, JcrType> types = new HashMap<String, JcrType>(); 066 register(types, PropertyType.BINARY, Types.BLOB, "BLOB", JcrBlob.class, 30, Integer.MAX_VALUE, new BlobTransform()); // assumed 067 register(types, PropertyType.BOOLEAN, Types.BOOLEAN, "BOOLEAN", Boolean.class, 5, 1, new BooleanTransform()); // 'true' or 068 // 'false' 069 register(types, PropertyType.DATE, Types.TIMESTAMP, "TIMESTAMP", Timestamp.class, 30, 10, new DateTransform()); // yyyy-MM-dd'T'HH:mm:ss.SSS+HH:mm 070 register(types, PropertyType.DOUBLE, Types.DOUBLE, "DOUBLE", Double.class, 20, 20, new DoubleTransform()); // assumed 071 register(types, PropertyType.DECIMAL, Types.DECIMAL, "BIGDECIMAL", BigDecimal.class, 20, 20, new DecimalTransform()); // assumed 072 register(types, PropertyType.LONG, Types.BIGINT, "LONG", Long.class, 20, 19, new LongTransform()); // assumed 073 register(types, PropertyType.NAME, Types.VARCHAR, "STRING", String.class, 20, Integer.MAX_VALUE, new StringTransform()); // assumed 074 register(types, PropertyType.PATH, Types.VARCHAR, "STRING", String.class, 50, Integer.MAX_VALUE, new StringTransform()); // assumed 075 register(types, 076 PropertyType.REFERENCE, 077 Types.VARCHAR, 078 "STRING", 079 UUID.class, 080 UUID_LENGTH, 081 UUID_LENGTH, 082 new UUIDTransform()); 083 register(types, 084 PropertyType.WEAKREFERENCE, 085 Types.VARCHAR, 086 "STRING", 087 UUID.class, 088 UUID_LENGTH, 089 UUID_LENGTH, 090 new UUIDTransform()); 091 register(types, PropertyType.URI, Types.VARCHAR, "STRING", String.class, 50, Integer.MAX_VALUE, new StringTransform()); // assumed 092 register(types, PropertyType.STRING, Types.VARCHAR, "STRING", String.class, 50, Integer.MAX_VALUE, new StringTransform()); // assumed 093 register(types, 094 PropertyType.UNDEFINED, 095 Types.VARCHAR, 096 "STRING", 097 String.class, 098 50, 099 Integer.MAX_VALUE, 100 new StringTransform()); // same 101 // as 102 // string 103 TYPE_INFO = Collections.unmodifiableMap(types); 104 } 105 106 private static void register( Map<String, JcrType> types, 107 int jcrType, 108 int jdbcType, 109 String typeName, 110 Class<?> clazz, 111 int displaySize, 112 int precision, 113 Transform transform ) { 114 JcrType type = new JcrType(jcrType, jdbcType, typeName, clazz, displaySize, precision, transform); 115 types.put(type.getJcrName(), type); 116 } 117 118 private final int jcrType; 119 private final String jcrName; 120 private final Class<?> clazz; 121 private final int jdbcType; 122 private final String typeName; 123 private final int displaySize; 124 private final int precision; 125 private final Transform transform; 126 127 protected JcrType( int jcrType, 128 int jdbcType, 129 String typeName, 130 Class<?> clazz, 131 int displaySize, 132 int precision, 133 Transform transform ) { 134 this.jcrType = jcrType; 135 this.jcrName = PropertyType.nameFromValue(jcrType).toUpperCase(); 136 this.clazz = clazz; 137 this.displaySize = displaySize; 138 this.jdbcType = jdbcType; 139 this.typeName = typeName; 140 this.precision = precision; 141 this.transform = transform; 142 assert this.jcrName != null; 143 assert this.clazz != null; 144 assert this.displaySize > 0; 145 assert this.transform != null; 146 } 147 148 /** 149 * Get the name of the JCR type. 150 * 151 * @return the JCR type name; never null 152 */ 153 public String getJcrName() { 154 return jcrName; 155 } 156 157 /** 158 * Get the JCR {@link PropertyType} value. 159 * 160 * @return the JCR property type; never null 161 */ 162 public int getJcrType() { 163 return jcrType; 164 } 165 166 /** 167 * Get the JDBC {@link Types} value. 168 * 169 * @return the JDBC type; never null 170 */ 171 public int getJdbcType() { 172 return jdbcType; 173 } 174 175 /** 176 * Get the native type name associated with the JDBC {@link Types} value. 177 * 178 * @return the native JDBC type name; never null 179 */ 180 public String getJdbcTypeName() { 181 return this.typeName; 182 } 183 184 /** 185 * Get the default precision used for this JcrType 186 * 187 * @return the Integer form of the precision 188 */ 189 public Integer getDefaultPrecision() { 190 return new Integer(precision); 191 } 192 193 /** 194 * Return the {@link Transform} object to use to transform the {@link Value} to the correct data type. 195 * 196 * @return Transform 197 */ 198 protected Transform getTransform() { 199 return this.transform; 200 } 201 202 /** 203 * Get the indicator if the value is case sensitive 204 * 205 * @return boolean indicating if the value is case sensitive 206 */ 207 public boolean isCaseSensitive() { 208 switch (getJcrType()) { 209 case PropertyType.DOUBLE: 210 case PropertyType.LONG: 211 case PropertyType.DECIMAL: 212 case PropertyType.WEAKREFERENCE: 213 case PropertyType.REFERENCE: // conversion is case-insensitive 214 case PropertyType.BOOLEAN: // conversion is case-insensitive 215 return false; 216 } 217 return true; 218 } 219 220 /** 221 * Get the indicator if the value is considered a signed value. 222 * 223 * @return boolean indicating if value is signed. 224 */ 225 public boolean isSigned() { 226 switch (getJcrType()) { 227 case PropertyType.DOUBLE: 228 case PropertyType.LONG: 229 case PropertyType.DECIMAL: 230 case PropertyType.DATE: 231 return true; 232 } 233 return false; 234 } 235 236 /** 237 * Get the Java class used to represent values for this type. 238 * 239 * @return the representation class; never null 240 */ 241 public Class<?> getRepresentationClass() { 242 return clazz; 243 } 244 245 /** 246 * Get the nominal display size for the given type. This may not be large enough for certain string and binary values. 247 * 248 * @return the nominal display size; always positive 249 * @see ResultSetMetaData#getColumnDisplaySize(int) 250 */ 251 public int getNominalDisplaySize() { 252 return displaySize; 253 } 254 255 public Object translateValue( Value value ) throws SQLException { 256 if (value == null) return null; 257 try { 258 return this.getTransform().transform(value); 259 260 } catch (ValueFormatException ve) { 261 throw new SQLException(ve.getLocalizedMessage(), ve); 262 } catch (IllegalStateException ie) { 263 throw new SQLException(ie.getLocalizedMessage(), ie); 264 } catch (RepositoryException e) { 265 throw new SQLException(e.getLocalizedMessage(), e); 266 } 267 268 } 269 270 public static Object translateValueToJDBC( Value value ) throws SQLException { 271 String jcrName = PropertyType.nameFromValue(value.getType()); 272 JcrType jcrtype = typeInfo(jcrName); 273 return jcrtype.translateValue(value); 274 } 275 276 public static JcrType typeInfo( String jcrTypeName ) { 277 return TYPE_INFO.get(jcrTypeName.toUpperCase()); 278 } 279 280 public static JcrType typeInfo( int jcrType ) { 281 return typeInfo(PropertyType.nameFromValue(jcrType)); 282 } 283 284 public static String jdbcType( String jcrTypeName ) { 285 JcrType t = typeInfo(jcrTypeName); 286 return t.getJdbcTypeName(); 287 } 288 289}