/* * Copyright 2013-2017 the original author or authors * * 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 org.springframework.data.cassandra.mapping; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.DataType; import com.datastax.driver.core.DataType.Name; import com.datastax.driver.core.Row; import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.UDTValue; import com.google.common.reflect.TypeToken; /** * Simple constant holder for a {@link SimpleTypeHolder} enriched with Cassandra specific simple types. * * @author Alex Shvid * @author Matthew T. Adams * @author Mark Paluch * @author Antoine Toulme */ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { public static final Set<Class<?>> CASSANDRA_SIMPLE_TYPES; private static final Map<Class<?>, DataType> classToDataType; private static final Map<DataType.Name, DataType> nameToDataType; static { CodecRegistry codecRegistry = CodecRegistry.DEFAULT_INSTANCE; Map<Class<?>, Class<?>> primitiveWrappers = new HashMap<>(8); primitiveWrappers.put(Boolean.class, boolean.class); primitiveWrappers.put(Byte.class, byte.class); primitiveWrappers.put(Character.class, char.class); primitiveWrappers.put(Double.class, double.class); primitiveWrappers.put(Float.class, float.class); primitiveWrappers.put(Integer.class, int.class); primitiveWrappers.put(Long.class, long.class); primitiveWrappers.put(Short.class, short.class); Set<Class<?>> simpleTypes = getCassandraPrimitiveTypes(codecRegistry); simpleTypes.add(Number.class); simpleTypes.add(Row.class); simpleTypes.add(UDTValue.class); classToDataType = Collections.unmodifiableMap(classToDataType(codecRegistry, primitiveWrappers)); nameToDataType = Collections.unmodifiableMap(nameToDataType()); CASSANDRA_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes); } public static final SimpleTypeHolder HOLDER = new CassandraSimpleTypeHolder(); /** * @return the map between {@link Name} and {@link DataType}. */ private static Map<Name, DataType> nameToDataType() { Map<Name, DataType> nameToDataType = new HashMap<>(16); DataType.allPrimitiveTypes().forEach(dataType -> { nameToDataType.put(dataType.getName(), dataType); }); return nameToDataType; } /** * @return the map between {@link Class} and {@link DataType}. * @param codecRegistry the Cassandra codec registry * @param primitiveWrappers map of primitive to wrapper type */ private static Map<Class<?>, DataType> classToDataType(CodecRegistry codecRegistry, Map<Class<?>, Class<?>> primitiveWrappers) { Map<Class<?>, DataType> classToDataType = new HashMap<>(16); DataType.allPrimitiveTypes().forEach(dataType -> { Class<?> javaType = codecRegistry.codecFor(dataType).getJavaType().getRawType(); classToDataType.put(javaType, dataType); Optional.ofNullable(primitiveWrappers.get(javaType)) .ifPresent(primitiveType -> classToDataType.put(primitiveType, dataType)); }); // override String to text datatype as String is used multiple times classToDataType.put(String.class, DataType.text()); // map Long to bigint as counter columns (last type aver multiple overrides) are a special use case // so map it to a more common type by default classToDataType.put(Long.class, DataType.bigint()); classToDataType.put(long.class, DataType.bigint()); return classToDataType; } /** * Returns a {@link Set} containing all Cassandra primitive types. * * @param codecRegistry the Cassandra codec registry * @return the set of Cassandra primitive types. */ private static Set<Class<?>> getCassandraPrimitiveTypes(CodecRegistry codecRegistry) { return DataType.allPrimitiveTypes().stream() .map(codecRegistry::codecFor) .map(TypeCodec::getJavaType) .map(TypeToken::getRawType) .collect(Collectors.toSet()); } /** * Returns the {@link DataType} for a {@link DataType.Name}. * * @param name * @return */ public static DataType getDataTypeFor(DataType.Name name) { return nameToDataType.get(name); } /** * Returns the default {@link DataType} for a {@link Class}. * * @param javaClass * @return */ public static DataType getDataTypeFor(Class<?> javaClass) { if (javaClass.isEnum()) { return DataType.varchar(); } return classToDataType.get(javaClass); } public static DataType.Name[] getDataTypeNamesFrom(List<TypeInformation<?>> arguments) { DataType.Name[] array = new DataType.Name[arguments.size()]; for (int i = 0; i != array.length; i++) { TypeInformation<?> typeInfo = arguments.get(i); DataType dataType = getDataTypeFor(typeInfo.getType()); if (dataType == null) { throw new InvalidDataAccessApiUsageException( String.format("Did not find appropriate primitive DataType for type '%s'", typeInfo.getType())); } array[i] = dataType.getName(); } return array; } public CassandraSimpleTypeHolder() { super(CASSANDRA_SIMPLE_TYPES, true); } }