/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.ignite.cache.store.cassandra.common; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Row; import java.beans.PropertyDescriptor; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.beanutils.PropertyUtils; import org.apache.ignite.cache.store.cassandra.serializer.Serializer; /** * Helper class providing bunch of methods to discover fields of POJO objects and * map builtin Java types to appropriate Cassandra types. */ public class PropertyMappingHelper { /** Bytes array Class type. */ private static final Class BYTES_ARRAY_CLASS = (new byte[] {}).getClass(); /** Mapping from Java to Cassandra types. */ private static final Map<Class, DataType.Name> JAVA_TO_CASSANDRA_MAPPING = new HashMap<Class, DataType.Name>() {{ put(String.class, DataType.Name.TEXT); put(Integer.class, DataType.Name.INT); put(int.class, DataType.Name.INT); put(Short.class, DataType.Name.INT); put(short.class, DataType.Name.INT); put(Long.class, DataType.Name.BIGINT); put(long.class, DataType.Name.BIGINT); put(Double.class, DataType.Name.DOUBLE); put(double.class, DataType.Name.DOUBLE); put(Boolean.class, DataType.Name.BOOLEAN); put(boolean.class, DataType.Name.BOOLEAN); put(Float.class, DataType.Name.FLOAT); put(float.class, DataType.Name.FLOAT); put(ByteBuffer.class, DataType.Name.BLOB); put(BYTES_ARRAY_CLASS, DataType.Name.BLOB); put(BigDecimal.class, DataType.Name.DECIMAL); put(InetAddress.class, DataType.Name.INET); put(Date.class, DataType.Name.TIMESTAMP); put(UUID.class, DataType.Name.UUID); put(BigInteger.class, DataType.Name.VARINT); }}; /** * Maps Cassandra type to specified Java type. * * @param clazz java class. * * @return Cassandra type. */ public static DataType.Name getCassandraType(Class clazz) { return JAVA_TO_CASSANDRA_MAPPING.get(clazz); } /** * Returns property descriptor by class property name. * * @param clazz class from which to get property descriptor. * @param prop name of the property. * * @return property descriptor. */ public static PropertyDescriptor getPojoPropertyDescriptor(Class clazz, String prop) { List<PropertyDescriptor> descriptors = getPojoPropertyDescriptors(clazz, false); if (descriptors == null || descriptors.isEmpty()) throw new IllegalArgumentException("POJO class " + clazz.getName() + " doesn't have '" + prop + "' property"); for (PropertyDescriptor descriptor : descriptors) { if (descriptor.getName().equals(prop)) return descriptor; } throw new IllegalArgumentException("POJO class " + clazz.getName() + " doesn't have '" + prop + "' property"); } /** * Extracts all property descriptors from a class. * * @param clazz class which property descriptors should be extracted. * @param primitive boolean flag indicating that only property descriptors for primitive properties * should be extracted. * * @return list of class property descriptors */ public static List<PropertyDescriptor> getPojoPropertyDescriptors(Class clazz, boolean primitive) { PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(clazz); List<PropertyDescriptor> list = new ArrayList<>(descriptors == null ? 1 : descriptors.length); if (descriptors == null || descriptors.length == 0) return list; for (PropertyDescriptor descriptor : descriptors) { if (descriptor.getReadMethod() == null || (primitive && !isPrimitivePropertyDescriptor(descriptor))) continue; list.add(descriptor); } return list; } /** * Checks if property descriptor describes primitive property (int, boolean, long and etc.) * * @param desc property descriptor. * * @return {@code true} property is primitive */ public static boolean isPrimitivePropertyDescriptor(PropertyDescriptor desc) { return PropertyMappingHelper.JAVA_TO_CASSANDRA_MAPPING.containsKey(desc.getPropertyType()); } /** * Returns value of specific column in the row returned by CQL statement. * * @param row row returned by CQL statement. * @param col column name. * @param clazz java class to which column value should be casted. * @param serializer serializer to use if column stores BLOB otherwise could be null. * * @return row column value. */ public static Object getCassandraColumnValue(Row row, String col, Class clazz, Serializer serializer) { if (String.class.equals(clazz)) return row.getString(col); if (Integer.class.equals(clazz) || int.class.equals(clazz)) return row.getInt(col); if (Short.class.equals(clazz) || short.class.equals(clazz)) return (short)row.getInt(col); if (Long.class.equals(clazz) || long.class.equals(clazz)) return row.getLong(col); if (Double.class.equals(clazz) || double.class.equals(clazz)) return row.getDouble(col); if (Boolean.class.equals(clazz) || boolean.class.equals(clazz)) return row.getBool(col); if (Float.class.equals(clazz) || float.class.equals(clazz)) return row.getFloat(col); if (ByteBuffer.class.equals(clazz)) return row.getBytes(col); if (PropertyMappingHelper.BYTES_ARRAY_CLASS.equals(clazz)) { ByteBuffer buf = row.getBytes(col); return buf == null ? null : buf.array(); } if (BigDecimal.class.equals(clazz)) return row.getDecimal(col); if (InetAddress.class.equals(clazz)) return row.getInet(col); if (Date.class.equals(clazz)) return row.getTimestamp(col); if (UUID.class.equals(clazz)) return row.getUUID(col); if (BigInteger.class.equals(clazz)) return row.getVarint(col); if (serializer == null) { throw new IllegalStateException("Can't deserialize value from '" + col + "' Cassandra column, " + "cause there is no BLOB serializer specified"); } ByteBuffer buf = row.getBytes(col); return buf == null ? null : serializer.deserialize(buf); } }