package me.prettyprint.hom; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.persistence.DiscriminatorType; import javax.persistence.Entity; import javax.persistence.InheritanceType; import me.prettyprint.hector.api.Serializer; import me.prettyprint.hom.cache.HectorObjectMapperException; import com.google.common.collect.Sets; /** * Holder for the mapping between a Class annotated with {@link Entity} and the * Cassandra column family name. * * @author Todd Burruss * * @param <T> */ public class CFMappingDef<T> { private Class<T> realClass; private Class<T> effectiveClass; private CFMappingDef<? super T> cfBaseMapDef; private CFMappingDef<? super T> cfSuperMapDef; private String colFamName; private InheritanceType inheritanceType; private String discColumn; private DiscriminatorType discType; private Object discValue; // this can be a variety of types private Method anonymousPropertyAddHandler; private Method anonymousPropertyGetHandler; private Class<?> anonymousValueType; @SuppressWarnings("rawtypes") private Serializer anonymousValueSerializer; private String[] sliceColumnNameArr; private KeyDefinition keyDef; private Map<Object, CFMappingDef<? extends T>> derivedClassMap = new HashMap<Object, CFMappingDef<? extends T>>(); private Collection<PropertyMappingDefinition> allMappedProps; // provide caching by class object for the class' mapping definition private Map<String, PropertyMappingDefinition> propertyCacheByPropName = new HashMap<String, PropertyMappingDefinition>(); // provide caching by class object for the class' mapping definition private Map<String, PropertyMappingDefinition> propertyCacheByColName = new HashMap<String, PropertyMappingDefinition>(); public CFMappingDef(Class<T> clazz) { setDefaults(clazz); } /** * Setup mapping with defaults for the given class. Does not parse all * annotations. * * @param realClass */ @SuppressWarnings("unchecked") public void setDefaults(Class<T> realClass) { this.realClass = realClass; this.keyDef = new KeyDefinition(); // find the "effective" class - skipping up the hierarchy over inner classes effectiveClass = realClass; boolean entityFound; while (!(entityFound = (null != effectiveClass.getAnnotation(Entity.class)))) { // TODO:BTB this might should be isSynthetic if (!effectiveClass.isAnonymousClass()) { break; } else { effectiveClass = (Class<T>) effectiveClass.getSuperclass(); } } // if class is missing @Entity, then proceed no further if (!entityFound) { throw new HomMissingEntityAnnotationException("class, " + realClass.getName() + ", not annotated with @" + Entity.class.getSimpleName()); } } public boolean isColumnSliceRequired() { return !isAnonymousHandlerAvailable() && !isAnyCollections() && !isAbstract() && !isDerivedEntity(); } public boolean isAnyCollections() { if (null == getAllProperties()) { return false; } for (PropertyMappingDefinition md : getAllProperties()) { if (md.isCollectionType()) { return true; } } return false; } public void addDerivedClassMap(CFMappingDef<? extends T> cfDerivedMapDef) { derivedClassMap.put(cfDerivedMapDef.getDiscValue(), cfDerivedMapDef); } public PropertyMappingDefinition getPropMapByPropName(String propName) { return propertyCacheByPropName.get(propName); } public PropertyMappingDefinition getPropMapByColumnName(String colName) { PropertyMappingDefinition md = propertyCacheByColName.get(colName); if (null != md) { return md; } else if (null != cfSuperMapDef) { return cfSuperMapDef.getPropMapByColumnName(colName); } else { return null; } } public void addPropertyDefinition(PropertyMappingDefinition propDef) { propertyCacheByColName.put(propDef.getColName(), propDef); propertyCacheByPropName.put(propDef.getPropDesc().getName(), propDef); } public String getColFamName() { return colFamName; } public String getEffectiveColFamName() { if (null != colFamName) { return colFamName; } else if (null != cfBaseMapDef) { return cfBaseMapDef.getColFamName(); } else { throw new HectorObjectMapperException( "trying to get ColumnFamily name, but is missing for mapping, " + this.toString()); } } public Class<T> getEffectiveClass() { return effectiveClass; } public InheritanceType getInheritanceType() { return inheritanceType; } public void setInheritanceType(InheritanceType inheritanceType) { this.inheritanceType = inheritanceType; } public String getDiscColumn() { if (null == cfBaseMapDef) { return discColumn; } else { return cfBaseMapDef.getDiscColumn(); } } public void setDiscColumn(String discColumn) { this.discColumn = discColumn; } public DiscriminatorType getDiscType() { if (null == cfBaseMapDef) { return discType; } else { return cfBaseMapDef.getDiscType(); } } public void setDiscType(DiscriminatorType discType) { this.discType = discType; } public Object getDiscValue() { return discValue; } public void setDiscValue(Object discValue) { this.discValue = discValue; } public KeyDefinition getKeyDef() { if (null == cfBaseMapDef) { return keyDef; } else { return cfBaseMapDef.getKeyDef(); } } @SuppressWarnings({ "unchecked", "rawtypes" }) public Collection<PropertyMappingDefinition> getAllProperties() { if (null == allMappedProps) { Set<PropertyMappingDefinition> propSet = new HashSet<PropertyMappingDefinition>(); for (PropertyMappingDefinition propMapDef : propertyCacheByColName.values()) { propSet.add(propMapDef); } if (null == cfSuperMapDef) { allMappedProps = propSet; } else { allMappedProps = Sets.union(propSet, (Set) cfSuperMapDef.getAllProperties()); } } return allMappedProps; } public CFMappingDef<? super T> getCfBaseMapDef() { return cfBaseMapDef; } public void setCfBaseMapDef(CFMappingDef<? super T> cfBaseMapDef) { this.cfBaseMapDef = cfBaseMapDef; } public void setColFamName(String colFamName) { this.colFamName = colFamName; } public Map<Object, CFMappingDef<? extends T>> getDerivedClassMap() { return derivedClassMap; } public CFMappingDef<? super T> getCfSuperMapDef() { return cfSuperMapDef; } public void setCfSuperMapDef(CFMappingDef<? super T> cfSuperMapDef) { this.cfSuperMapDef = cfSuperMapDef; } public Class<T> getRealClass() { return realClass; } @Override public String toString() { return "CFMappingDef [colFamName=" + colFamName + ", realClass=" + realClass + ", effectiveClass=" + effectiveClass + "]"; } public boolean isAbstract() { return Modifier.isAbstract(effectiveClass.getModifiers()); } public boolean isBaseEntity() { return null != inheritanceType; } public boolean isPersistableEntity() { return !isAbstract(); } public boolean isPersistableDerivedEntity() { return !isBaseEntity() && null != getDiscValue() && !isAbstract(); } public boolean isNonPersistableDerivedEntity() { return !isBaseEntity() && !isPersistableDerivedEntity() && isAbstract(); } public boolean isDerivedEntity() { return isPersistableDerivedEntity() || isNonPersistableDerivedEntity(); } public String[] getSliceColumnNameArr() { return sliceColumnNameArr; } public void setSliceColumnNameArr(String[] sliceColumnNameArr) { this.sliceColumnNameArr = sliceColumnNameArr; } public Method getAnonymousPropertyAddHandler() { if (null != anonymousPropertyAddHandler) { return anonymousPropertyAddHandler; } else if (null != cfSuperMapDef) { return cfSuperMapDef.getAnonymousPropertyAddHandler(); } else { return null; } } public Method getAnonymousPropertyGetHandler() { if (null != anonymousPropertyGetHandler) { return anonymousPropertyGetHandler; } else if (null != cfSuperMapDef) { return cfSuperMapDef.getAnonymousPropertyGetHandler(); } else { return null; } } public Class<?> getAnonymousValueType() { if (null != anonymousValueType) { return anonymousValueType; } else if (null != cfSuperMapDef) { return cfSuperMapDef.getAnonymousValueType(); } else { return null; } } @SuppressWarnings("rawtypes") public Serializer getAnonymousValueSerializer() { if (null != anonymousValueSerializer) { return anonymousValueSerializer; } else if (null != cfSuperMapDef) { return cfSuperMapDef.getAnonymousValueSerializer(); } else { return null; } } public void setAnonymousValueType(Class<?> anonymousValueType) { this.anonymousValueType = anonymousValueType; } public void setAnonymousValueSerializer( @SuppressWarnings("rawtypes") Serializer anonymousValueSerializer) { this.anonymousValueSerializer = anonymousValueSerializer; } public void setAnonymousPropertyAddHandler(Method anonymousPropertyAddHandler) { this.anonymousPropertyAddHandler = anonymousPropertyAddHandler; } public boolean isAnonymousHandlerAvailable() { return null != anonymousValueSerializer; } // // public boolean isSliceColumnArrayRequired() { // return null != sliceColumnNameArr && 0 < sliceColumnNameArr.length; // } public Collection<PropertyMappingDefinition> getCollectionProperties() { Set<PropertyMappingDefinition> collSet = new HashSet<PropertyMappingDefinition>(); for (PropertyMappingDefinition md : getAllProperties()) { if (md.isCollectionType()) { collSet.add(md); } } return collSet; } public void setAnonymousPropertyGetHandler(Method anonymousPropertyGetHandler) { this.anonymousPropertyGetHandler = anonymousPropertyGetHandler; } }