Java tutorial
/* * Copyright 2014, Luca Rosellini. * * 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 kina.entity; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.*; import com.google.common.collect.ImmutableMap; import org.apache.commons.collections.CollectionUtils; import com.datastax.driver.core.DataType; import kina.exceptions.GenericException; import kina.rdd.CassandraRDDUtils; import kina.utils.AnnotationUtils; import kina.utils.CassandraUtils; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.db.marshal.*; import static java.lang.Class.forName; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableCollection; /** * Defines a serializable CellValidator. <br/> * <p/> * This object wraps the complexity of obtaining the Cassandra's AbstractType * associated to a {@link Cell}. * <p/> * In the case of collection types, a simple cassandra marshaller qualified name * is not enough to fully generate an AbstractType, we also need the type(s) the * collection holds. */ public class CellValidator implements Serializable { private static final String DEFAULT_VALIDATOR_CLASSNAME = "org.apache.cassandra.db.marshal.UTF8Type"; private static final Map<Class, CQL3Type.Native> MAP_JAVA_TYPE_TO_CQL_TYPE = ImmutableMap.<Class, CQL3Type.Native>builder() .put(String.class, CQL3Type.Native.TEXT).put(Integer.class, CQL3Type.Native.INT) .put(Boolean.class, CQL3Type.Native.BOOLEAN).put(Date.class, CQL3Type.Native.TIMESTAMP) .put(BigDecimal.class, CQL3Type.Native.DECIMAL).put(Long.class, CQL3Type.Native.BIGINT) .put(Double.class, CQL3Type.Native.DOUBLE).put(Float.class, CQL3Type.Native.FLOAT) .put(InetAddress.class, CQL3Type.Native.INET).put(Inet4Address.class, CQL3Type.Native.INET) .put(Inet6Address.class, CQL3Type.Native.INET).put(BigInteger.class, CQL3Type.Native.VARINT) .put(UUID.class, CQL3Type.Native.UUID).build(); private static final Map<String, DataType.Name> MAP_JAVA_TYPE_TO_DATA_TYPE_NAME = ImmutableMap.<String, DataType.Name>builder() .put(UTF8Type.class.getCanonicalName(), DataType.Name.TEXT) .put(Int32Type.class.getCanonicalName(), DataType.Name.INT) .put(BooleanType.class.getCanonicalName(), DataType.Name.BOOLEAN) .put(TimestampType.class.getCanonicalName(), DataType.Name.TIMESTAMP) .put(DateType.class.getCanonicalName(), DataType.Name.TIMESTAMP) .put(DecimalType.class.getCanonicalName(), DataType.Name.DECIMAL) .put(LongType.class.getCanonicalName(), DataType.Name.BIGINT) .put(DoubleType.class.getCanonicalName(), DataType.Name.DOUBLE) .put(FloatType.class.getCanonicalName(), DataType.Name.FLOAT) .put(InetAddressType.class.getCanonicalName(), DataType.Name.INET) .put(IntegerType.class.getCanonicalName(), DataType.Name.VARINT) .put(UUIDType.class.getCanonicalName(), DataType.Name.UUID) .put(TimeUUIDType.class.getCanonicalName(), DataType.Name.TIMEUUID) .put(MapType.class.getCanonicalName(), DataType.Name.MAP) .put(SetType.class.getCanonicalName(), DataType.Name.SET) .put(ListType.class.getCanonicalName(), DataType.Name.LIST) .put(BytesType.class.getCanonicalName(), DataType.Name.BLOB).build(); /** * Possible types of CellValidator. */ public static enum Kind { MAP, SET, LIST, NOT_A_COLLECTION; /** * Returns the Kind associated to the provided Cassandra validator class. * <p/> * <p> * To be more specific, returns: * <ul> * <li>NOT_A_COLLECTION: when the provided type is not a set a list or a map</li> * <li>MAP: when the provided validator is MapType.class</li> * <li>LIST: when the provided validator is ListType.class</li> * <li>SET: when the provided validator is SetType.class</li> * </ul> * </p> * * @param type the marshaller Class object. * @return the Kind associated to the provided marhaller Class object. */ public static Kind validatorClassToKind(Class<? extends AbstractType> type) { if (type == null) { return NOT_A_COLLECTION; } if (type.equals(SetType.class)) { return SET; } else if (type.equals(ListType.class)) { return LIST; } else if (type.equals(MapType.class)) { return MAP; } else { return NOT_A_COLLECTION; } } /** * Returns the Kind associated to the provided object instance. * <p/> * <p>To be more specific, returns: * <ul> * <li>{@link CellValidator.Kind#SET}: when the provided instance object is an * instance of {@link java.util.Set}</li> * <li>{@link CellValidator.Kind#LIST}: when the provided instance object is an * instance of {@link java.util.List}</li> * <li>{@link CellValidator.Kind#MAP}: when the provided instance object is an * instance of {@link java.util.Map}</li> * <li>{@link CellValidator.Kind#NOT_A_COLLECTION}: otherwise.</li> * </ul> * </p> * * @param object an object instance. * @param <T> the generic type of the provided object instance. * @return the Kind associated to the provided object. */ public static <T> Kind objectToKind(T object) { if (object == null) { return NOT_A_COLLECTION; } Kind res; if (Set.class.isAssignableFrom(object.getClass())) { res = SET; } else if (List.class.isAssignableFrom(object.getClass())) { res = LIST; } else if (Map.class.isAssignableFrom(object.getClass())) { res = MAP; } else { res = NOT_A_COLLECTION; } return res; } } /** * Cassandra's validatorClassName class name for the current cell.<br/> * The provided type must extends {@link org.apache.cassandra.db.marshal.AbstractType} */ private String validatorClassName = DEFAULT_VALIDATOR_CLASSNAME; /** * Only applies to collection types (validatorKind != Kind.NOT_A_COLLECTION), contains the list of * types the collection holds. At most, this collection will contain two elements, in the case * of validatorKind == Kind.MAP. */ private Collection<String> validatorTypes; /** * Holds the type of the current validator. */ private Kind validatorKind = Kind.NOT_A_COLLECTION; private transient AbstractType<?> abstractType; private DataType.Name cqlTypeName; /** * Factory method that builds a CellValidator from an KinaType field. * * @param field the KinaType field. * @return a new CellValidator associated to the provided object. */ public static CellValidator cellValidator(java.lang.reflect.Field field) { return new CellValidator(field); } /** * Factory method that builds a CellValidator from a DataType object. * * @param type the data type coming from the driver. * @return a new CellValidator associated to the provided object. */ public static CellValidator cellValidator(DataType type) { return new CellValidator(type); } /** * Generates a CellValidator for a generic instance of an object. * We need the actual instance in order to differentiate between an UUID and a TimeUUID. * * @param obj an instance to use to build the new CellValidator. * @param <T> the generic type of the provided object instance. * @return a new CellValidator associated to the provided object. */ public static <T> CellValidator cellValidator(T obj) { if (obj == null) { return null; } Kind kind = Kind.objectToKind(obj); AbstractType<?> tAbstractType = CassandraRDDUtils.marshallerInstance(obj); String validatorClassName = tAbstractType.getClass().getCanonicalName(); Collection<String> validatorTypes = null; DataType.Name cqlTypeName = MAP_JAVA_TYPE_TO_DATA_TYPE_NAME.get(validatorClassName);// tAbstractType.get return new CellValidator(validatorClassName, kind, validatorTypes, cqlTypeName); } /** * private constructor. */ private CellValidator(String validatorClassName, Kind validatorKind, Collection<String> validatorTypes, DataType.Name cqlTypeName) { this.validatorClassName = validatorClassName != null ? validatorClassName : DEFAULT_VALIDATOR_CLASSNAME; this.validatorKind = validatorKind; this.validatorTypes = validatorTypes; this.cqlTypeName = cqlTypeName; } /** * private constructor. */ private static String getCollectionInnerType(Class<?> type) { CQL3Type.Native nativeType = MAP_JAVA_TYPE_TO_CQL_TYPE.get(type); return nativeType.name().toLowerCase(); } /** * private constructor. */ private CellValidator(java.lang.reflect.Field field) { Class<?>[] types = AnnotationUtils.getGenericTypes(field); Class<? extends AbstractType> clazz = CassandraUtils.validationClass(field); this.validatorClassName = clazz.getCanonicalName(); this.validatorKind = Kind.validatorClassToKind(clazz); cqlTypeName = MAP_JAVA_TYPE_TO_DATA_TYPE_NAME.get(this.validatorClassName); switch (this.validatorKind) { case SET: case LIST: this.validatorTypes = asList(getCollectionInnerType(types[0])); break; case MAP: this.validatorTypes = asList(getCollectionInnerType(types[0]), getCollectionInnerType(types[1])); break; default: } } /** * private constructor. * * @param type a {@link com.datastax.driver.core.DataType} coming from the underlying Java driver. */ private CellValidator(DataType type) { if (type == null) { throw new kina.exceptions.InstantiationException("input DataType cannot be null"); } cqlTypeName = type.getName(); if (!type.isCollection()) { validatorClassName = AnnotationUtils.MAP_JAVA_TYPE_TO_ABSTRACT_TYPE.get(type.asJavaClass()).getClass() .getName(); } else { validatorTypes = new ArrayList<>(); for (DataType dataType : type.getTypeArguments()) { validatorTypes.add(dataType.toString()); } switch (type.getName()) { case SET: validatorKind = Kind.SET; validatorClassName = SetType.class.getName(); break; case LIST: validatorKind = Kind.LIST; validatorClassName = ListType.class.getName(); break; case MAP: validatorKind = Kind.MAP; validatorClassName = MapType.class.getName(); break; default: throw new GenericException("Cannot determine collection type for " + type.getName()); } validatorTypes = unmodifiableCollection(validatorTypes); } } /** * Generates the cassandra marshaller ({@link org.apache.cassandra.db.marshal.AbstractType}) for this CellValidator. * * @return an instance of the cassandra marshaller for this CellValidator. */ public AbstractType<?> getAbstractType() { if (abstractType != null) { return abstractType; } try { if (validatorKind == Kind.NOT_A_COLLECTION) { abstractType = AnnotationUtils.MAP_ABSTRACT_TYPE_CLASS_TO_ABSTRACT_TYPE .get(forName(validatorClassName)); } else { Iterator<String> types = validatorTypes.iterator(); switch (validatorKind) { case SET: CQL3Type cql3Type = CQL3Type.Native.valueOf(types.next().toUpperCase()); abstractType = SetType.getInstance(cql3Type.getType()); break; case LIST: cql3Type = CQL3Type.Native.valueOf(types.next().toUpperCase()); abstractType = ListType.getInstance(cql3Type.getType()); break; case MAP: cql3Type = CQL3Type.Native.valueOf(types.next().toUpperCase()); CQL3Type cql3Type2 = CQL3Type.Native.valueOf(types.next().toUpperCase()); abstractType = MapType.getInstance(cql3Type.getType(), cql3Type2.getType()); break; default: throw new GenericException("Cannot determine collection kind for " + validatorKind); } } } catch (ClassNotFoundException e) { throw new GenericException(e); } return abstractType; } /** * Getter for validatorClassName. * * @return the cassandra marshaller class name. */ public String getValidatorClassName() { return validatorClassName; } /** * Getter for validatorTypes. * * @return a collection of validator type names. */ public Collection<String> getValidatorTypes() { return validatorTypes; } /** * Getter for validatorKind. * * @return the Kind associated to this CellValidator. */ public Kind validatorKind() { return validatorKind; } /** * {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CellValidator that = (CellValidator) o; if (validatorKind != that.validatorKind) { return false; } if (!validatorClassName.equals(that.validatorClassName)) { return false; } if (validatorKind != Kind.NOT_A_COLLECTION && !CollectionUtils.isEqualCollection(validatorTypes, that.validatorTypes)) { return false; } return true; } /** * {@inheritDoc} */ @Override public int hashCode() { int result = validatorClassName.hashCode(); result = 31 * result + (validatorTypes != null ? validatorTypes.hashCode() : 0); result = 31 * result + validatorKind.hashCode(); return result; } /** * {@inheritDoc} */ @Override public String toString() { return "CellValidator{" + "validatorClassName='" + validatorClassName + '\'' + ", validatorTypes=" + (validatorTypes != null ? validatorTypes : "") + ", validatorKind=" + validatorKind + ", abstractType=" + abstractType + '}'; } /** * @return the original CQL3 type name (if known, null otherwise) */ public DataType.Name getCqlTypeName() { return cqlTypeName; } }