/*
* Copyright (c) 2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.common;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.impl.ColumnField;
import com.emc.storageos.db.client.model.Cf;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.NamedRelationIndex;
import com.emc.storageos.db.client.model.RelationIndex;
public class DependencyInterceptor {
private static final Logger log = LoggerFactory.getLogger(DependencyInterceptor.class);
private Collection<Class<?>> modelClasses;
public DependencyInterceptor(Collection<Class<?>> modelClasses) {
super();
this.modelClasses = modelClasses;
}
/**
* Check the clazz if it has Cf annotation
*
* @param clazz the class which to check against
* @return true if has Cf annotation
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public boolean isConcretModelClass(Class clazz) {
return clazz.getAnnotation(Cf.class) != null;
}
/**
* handle the dependency if type attribute set to abstract class in RelationIndex/NamedRelationIndex
*
* @param tracker dependency tracker
* @param sourceClazz the DataObject Type
* @param field the property has Relation/NamedRelationIndex annotation in sourceClazz
* @return
*/
@SuppressWarnings({ "rawtypes" })
public void handleDependency(DependencyTracker tracker, Class sourceClazz, ColumnField field) {
log.info("process dependency of class {} field {}", sourceClazz.getSimpleName(), field.getName());
if (field.getIndexRefType().equals(DataObject.class)) {
if (hasMutipleDependencies(field)) {
addMutilpleDependencies(tracker, sourceClazz, field);
}
} else {
addConcretDependencies(tracker, sourceClazz, field);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addConcretDependencies(DependencyTracker tracker, Class sourceClazz, ColumnField field) {
Class targetClazz = field.getIndexRefType();
for (Class clazz : this.modelClasses) {
if (targetClazz.isAssignableFrom(clazz) && isConcretModelClass(clazz)) {
log.info("{} depends on {}:"+field.getName(), clazz.getSimpleName(), sourceClazz.getSimpleName());
tracker.addDependency(clazz, sourceClazz, field);
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addMutilpleDependencies(DependencyTracker tracker, Class sourceClazz, ColumnField field) {
Annotation a = this.getRelationAnnotation(field);
Class<? extends DataObject>[] refTypes;
if (a instanceof RelationIndex) {
refTypes = ((RelationIndex) a).types();
} else {
refTypes = ((NamedRelationIndex) a).types();
}
for (Class<? extends DataObject> type : refTypes) {
log.info("{} depends on {}:"+field.getName(), type.getSimpleName(), sourceClazz.getSimpleName());
tracker.addDependency(type, sourceClazz, field);
}
}
private boolean hasMutipleDependencies(ColumnField field) {
Annotation a = this.getRelationAnnotation(field);
if (a instanceof RelationIndex) {
return ((RelationIndex) a).types().length > 0;
}
if (a instanceof NamedRelationIndex) {
return ((NamedRelationIndex) a).types().length > 0;
}
return false;
}
private Annotation getRelationAnnotation(ColumnField field) {
PropertyDescriptor property = field.getPropertyDescriptor();
Method method = property.getReadMethod();
for (Annotation annotation : method.getAnnotations()) {
if (annotation instanceof RelationIndex) {
return annotation;
} else if (annotation instanceof NamedRelationIndex) {
return annotation;
}
}
throw new IllegalStateException("field:" + field.getName() + " should have RelationIndex annotation");
}
}