/** * * Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); You may not * use this file except in compliance with the License. You may obtain a copy of * the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.speedment.runtime.core.internal.db; import static com.speedment.common.invariant.NullUtil.requireNonNulls; import com.speedment.runtime.core.db.JavaTypeMap; import com.speedment.runtime.core.db.metadata.ColumnMetaData; import com.speedment.runtime.core.db.metadata.TypeInfoMetaData; import com.speedment.runtime.core.exception.SpeedmentException; import com.speedment.runtime.core.internal.component.resultset.StandardJavaTypeMapping; import static com.speedment.runtime.core.internal.util.CaseInsensitiveMaps.newCaseInsensitiveMap; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Blob; import java.sql.Clob; import java.sql.Time; import java.sql.Timestamp; import java.util.*; import static java.util.Objects.requireNonNull; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; /** * * @author Emil Forslund * @since 3.0.0 */ public class JavaTypeMapImpl implements JavaTypeMap { private final List<Rule> rules; private final Map<String, Class<?>> inner; public JavaTypeMapImpl() { this(map -> {}); } /** * Sets up the java type map for this database type * * @param installer the installer * @see <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html">Official Mappings</a> */ protected JavaTypeMapImpl(Consumer<Map<String, Class<?>>> installer) { rules = new CopyOnWriteArrayList<>(); inner = newCaseInsensitiveMap(); inner.put("CHAR", String.class); inner.put("VARCHAR", String.class); inner.put("LONGVARCHAR", String.class); inner.put("LONGVARCHAR", String.class); inner.put("NUMERIC", BigDecimal.class); inner.put("DECIMAL", BigDecimal.class); inner.put("BIT", Integer.class); /// inner.put("TINYINT", Byte.class); inner.put("TINYINT UNSIGNED", Short.class); inner.put("SMALLINT", Short.class); inner.put("SMALLINT UNSIGNED", Integer.class); inner.put("INTEGER", Integer.class); inner.put("INTEGER UNSIGNED", Long.class); inner.put("BIGINT", Long.class); inner.put("BIGINT UNSIGNED", BigInteger.class); inner.put("REAL", Float.class); inner.put("FLOAT", Double.class); inner.put("DOUBLE", Double.class); inner.put("DATE", java.sql.Date.class); inner.put("TIME", Time.class); inner.put("TIMESTAMP", Timestamp.class); inner.put("CLOB", Clob.class); inner.put("BLOB", Blob.class); inner.put("BOOLEAN", Boolean.class); inner.put("BOOL", Boolean.class); //MySQL Specific mappings inner.put("YEAR", Integer.class); inner.put("INT UNSIGNED", Long.class); //PostgreSQL specific mappings inner.put("UUID", UUID.class); installer.accept(inner); assertJavaTypesKnown(); } @Override public final void addRule(Rule rule) { rules.add(requireNonNull(rule)); } @Override public final Class<?> findJdbcType(Map<String, Class<?>> sqlTypeMapping, ColumnMetaData md) { // Firstly, check if we have any rule for this type. final Optional<Class<?>> ruled = rules.stream() .map(r -> r.findJdbcType(sqlTypeMapping, md)) .filter(Optional::isPresent) .map(Optional::get) .findFirst(); if (ruled.isPresent()) { return ruled.get(); } else { // Secondly, try md.getTypeName() Class<?> result = sqlTypeMapping.get(md.getTypeName()); if (result == null) { // Type (int) according to java.sql.Types (e.g. 4) that // we got from the ColumnMetaData final int type = md.getDataType(); // Variable name (String) according to java.sql.Types (e.g. INTEGER) final Optional<String> oTypeName = TypeInfoMetaData.lookupJavaSqlType(type); if (oTypeName.isPresent()) { final String typeName = oTypeName.get(); // Thirdly, try the corresponding name using md.getDataType() // and then lookup java.sql.Types name result = sqlTypeMapping.get(typeName); } } return result; } } @Override public final void put(String key, Class<?> clazz) { requireNonNulls(key, clazz); inner.put(key, clazz); } @Override public final Class<?> get(String key) { requireNonNull(key); return inner.get(key); } private void assertJavaTypesKnown() { final Map<String, Class<?>> unmapped = new LinkedHashMap<>(); inner.entrySet().forEach((entry) -> { final String key = entry.getKey(); final Class<?> clazz = entry.getValue(); if (!StandardJavaTypeMapping.stream().anyMatch(jtm -> jtm.getJavaClass().equals(clazz))) { unmapped.put(key, clazz); } }); if (!unmapped.isEmpty()) { throw new SpeedmentException( "There are mappings that have no " + StandardJavaTypeMapping.class.getSimpleName() + " " + unmapped ); } } }