Skip to content

Commit

Permalink
Refactor: RolesAnnotationChecker split into parts (used Dependency
Browse files Browse the repository at this point in the history
Injection): RolesAnnotationChecker + KnowledgePathChecker + TypeComparer
+ ParameterKnowledgePathExctractor.

Updated tests for RolesAnnotationChecker (refactor + split).

Added skeleton for ComponentProcessChecker.
  • Loading branch information
jiracekz committed Mar 16, 2015
1 parent 3db925d commit 3e4afeb
Show file tree
Hide file tree
Showing 18 changed files with 1,430 additions and 618 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cz.cuni.mff.d3s.deeco.annotations.processor;

import java.util.Arrays;

import cz.cuni.mff.d3s.deeco.model.runtime.api.ComponentInstance;
import cz.cuni.mff.d3s.deeco.model.runtime.api.EnsembleDefinition;

Expand All @@ -11,6 +13,15 @@
*/
public interface AnnotationChecker {

static TypeComparer typeComparer = new GenericTypeComparer();
static KnowledgePathChecker knowledgePathChecker = new KnowledgePathCheckerImpl(typeComparer);

public static AnnotationChecker[] standardCheckers = new AnnotationChecker[]
{
new RolesAnnotationChecker(knowledgePathChecker, typeComparer),
//new ComponentProcessChecker()
};

/**
* Checks that a given component instance is valid.
* @param componentObj The component instance object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ enum ParsingEvent {
*/
public AnnotationProcessor(RuntimeMetadataFactory factory, RuntimeMetadata model, KnowledgeManagerFactory knowledgeMangerFactory,
AnnotationProcessorExtensionPoint... extensions) {
this(factory, model, knowledgeMangerFactory, Arrays.asList(new RolesAnnotationChecker()), extensions);
this(factory, model, knowledgeMangerFactory, AnnotationChecker.standardCheckers, extensions);
}

/**
Expand All @@ -202,7 +202,7 @@ public AnnotationProcessor(RuntimeMetadataFactory factory, RuntimeMetadata model
* @param knowledgeMangerFactory knowledge manager factory to be used
*/
AnnotationProcessor(RuntimeMetadataFactory factory, RuntimeMetadata model, KnowledgeManagerFactory knowledgeMangerFactory,
List<AnnotationChecker> annotationCheckers, AnnotationProcessorExtensionPoint... extensions) {
AnnotationChecker[] annotationCheckers, AnnotationProcessorExtensionPoint... extensions) {
this.factory = factory;
this.model = model;
if (extensions.length == 0) {
Expand All @@ -213,7 +213,7 @@ public AnnotationProcessor(RuntimeMetadataFactory factory, RuntimeMetadata model
this.knowledgeManagerFactory = knowledgeMangerFactory;
this.cloner = new Cloner();
if (annotationCheckers != null) {
this.checkers = annotationCheckers.toArray(new AnnotationChecker[0]);
this.checkers = annotationCheckers;
} else {
this.checkers = new AnnotationChecker[0];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cz.cuni.mff.d3s.deeco.annotations.processor;

import java.lang.reflect.Type;
import java.util.Collection;

import cz.cuni.mff.d3s.deeco.knowledge.KnowledgeManager;
import cz.cuni.mff.d3s.deeco.knowledge.ReadOnlyKnowledgeManager;
import cz.cuni.mff.d3s.deeco.model.runtime.api.ComponentInstance;
import cz.cuni.mff.d3s.deeco.model.runtime.api.ComponentProcess;
import cz.cuni.mff.d3s.deeco.model.runtime.api.EnsembleDefinition;
import cz.cuni.mff.d3s.deeco.model.runtime.api.KnowledgePath;
import cz.cuni.mff.d3s.deeco.model.runtime.api.Parameter;

/**
* Checks whether all component's processes use only parameters from the component's knowledge
*
* @author Zbyněk Jiráček
*
*//*
public class ComponentProcessChecker implements AnnotationChecker {
@Override
public void validateComponent(Object componentObj, ComponentInstance componentInstance)
throws AnnotationCheckerException {
if (componentObj == null) {
throw new AnnotationCheckerException("The input component cannot be null");
}
if (componentInstance == null) {
throw new AnnotationCheckerException("The input component instance cannot be null.");
}
for (ComponentProcess process : componentInstance.getComponentProcesses()) {
for (Parameter parameter : process.getParameters()) {
checkKnowledgePath(parameter.getGenericType(), parameter.getKnowledgePath(), componentObj.getClass());
}
}
}
void checkKnowledgePath(Type type, KnowledgePath knowledgePath, Class<?> componentClass)
throws AnnotationCheckerException {
}
@Override
public void validateEnsemble(Class<?> ensembleClass, EnsembleDefinition ensembleDefinition)
throws AnnotationCheckerException {
// ensembles have no processes, ergo they're always correct
}
}
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package cz.cuni.mff.d3s.deeco.annotations.processor;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericTypeComparer implements TypeComparer {

/**
* Checks whether a given types are (or can be) equal. This method is used to check whether
* a field of a given type can implement (or reference) a field of a given type from a role
* class. The method returns true, if the types are equal, or in all cases when the types
* cannot be proven to be different (this involves mostly unresolved generic arguments).
* Note that due to technical limitations, type hierarchy is not taken into account
* (ie. for classes A extends B, A and B are not considered equal in any case).
* @param implementationType The type of the field in the class/method
* @param roleType The type of the field as declared in the role
* @return True if types are equal/may be equal, false otherwise
*/
@Override
public boolean compareTypes(Type implementationType, Type roleType) {
if (implementationType.equals(roleType)) {
return true;
}

// nonequal types can be equal, if one of them is a generic type (or generically parameterized)
if (implementationType instanceof GenericArrayType && roleType instanceof GenericArrayType) {
GenericArrayType gType1 = (GenericArrayType) implementationType;
GenericArrayType gType2 = (GenericArrayType) roleType;
return compareTypes(gType1.getGenericComponentType(), gType2.getGenericComponentType());
} else if (implementationType instanceof GenericArrayType && roleType instanceof Class) {
return ((Class<?>) roleType).isArray();
} else if (implementationType instanceof Class && roleType instanceof GenericArrayType) {
return ((Class<?>) implementationType).isArray();

} else if (implementationType instanceof ParameterizedType && roleType instanceof ParameterizedType) {
ParameterizedType pType1 = (ParameterizedType) implementationType;
ParameterizedType pType2 = (ParameterizedType) roleType;
if (!compareTypes(pType1.getRawType(), pType2.getRawType())) {
return false;
}

assert pType1.getActualTypeArguments().length == pType2.getActualTypeArguments().length;
for (int i = 0; i < pType1.getActualTypeArguments().length; i++) {
if (!compareTypes(pType1.getActualTypeArguments()[i], pType2.getActualTypeArguments()[i])) {
return false;
}
}

return true;

} else if (implementationType instanceof TypeVariable || roleType instanceof TypeVariable) {
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cz.cuni.mff.d3s.deeco.annotations.processor;

public class KnowledgePathCheckException extends Exception {

private static final long serialVersionUID = -8720858480071032369L;

public KnowledgePathCheckException() {
// TODO Auto-generated constructor stub
}

public KnowledgePathCheckException(String message) {
super(message);
// TODO Auto-generated constructor stub
}

public KnowledgePathCheckException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}

public KnowledgePathCheckException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}

public KnowledgePathCheckException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cz.cuni.mff.d3s.deeco.annotations.processor;

import java.lang.reflect.Type;
import java.util.List;

import cz.cuni.mff.d3s.deeco.model.runtime.api.PathNode;

/**
*
* The class is used for checking of ensemble membership and knowledge exchange functions,
* as well as for checking of component processes.
*
* @author Zbyněk Jiráček
*
*/
public interface KnowledgePathChecker {

/**
* Checks whether a field sequence is present in a given class. Also checks the type
* of the field, if wanted. The given path can contain only field names (not [..]). It not
* only checks whether the given role class contains the first-level field, it also checks
* whether the rest of the path is valid.
* @param type The desired type (or null if the type should not be checked)
* @param fieldNameSequence The knowledge path separated to individual path nodes
* @param clazz The class that should contain the path
* @return True if the field (of given type) is present in the role, false otherwise.
*/
boolean isFieldInClass(Type type, List<PathNode> fieldSequence, Class<?> clazz) throws KnowledgePathCheckException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package cz.cuni.mff.d3s.deeco.annotations.processor;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.stream.Collectors;

import cz.cuni.mff.d3s.deeco.model.runtime.api.PathNode;
import cz.cuni.mff.d3s.deeco.model.runtime.api.PathNodeComponentId;
import cz.cuni.mff.d3s.deeco.model.runtime.api.PathNodeField;

/**
*
* @author Zbyněk Jiráček
*
*/
public class KnowledgePathCheckerImpl implements KnowledgePathChecker {

class PathNodeCheckingException extends Exception {
private static final long serialVersionUID = 6724099053420733270L;

public PathNodeCheckingException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}

private TypeComparer typeComparer;

public KnowledgePathCheckerImpl(TypeComparer typeComparer) {
this.typeComparer = typeComparer;
}

public static String pathNodeSequenceToString(Type type, List<PathNode> pathNodes) {
String result = "'" + String.join(".",
pathNodes.stream().map(p -> p.toString()).collect(Collectors.toList())) + "'";

if (type != null) {
result += " of type " + type;
}

return result;
}

@Override
public boolean isFieldInClass(Type type, List<PathNode> fieldSequence, Class<?> clazz) throws KnowledgePathCheckException {

if (fieldSequence == null || fieldSequence.size() < 1) {
throw new KnowledgePathCheckException("The field sequence cannot be null or empty.");
}
if (clazz == null) {
throw new KnowledgePathCheckException("The role class cannot be null.");
}

try {
Type fieldType = getTypeInClass(fieldSequence, clazz);
if (fieldType == null) {
return false; // field not present in the role class
}

return type == null || typeComparer.compareTypes(type, fieldType);
} catch (PathNodeCheckingException e) {
throw new KnowledgePathCheckException("Knowledge path " + pathNodeSequenceToString(type, fieldSequence)
+ ": " + e.getMessage(), e);
}
}

/**
* Returns the type of a field sequence from a given role class. If the given field sequence
* has only one element "id", String.class is returned (implicit field present in all roles).
* When the field sequence consist of multiple elements, the type of the nested field is returned
* (ie. if the given class contains a field x of type C, class C contains a field y and the
* input field name sequence is ("x", "y"), then the result is the type of C.y).
*
* If the given field has generic type, {@link ParameterizedType} is returned, containing
* the type arguments.
*
* Sometimes generic types cannot be inferred. In that case it is possible, that an unknown
* type is returned. In this case the result is an instance of {@link TypeVariable}.
*
* @param fieldNameSequence Sequence of fields (knowledge path split by dots)
* @param roleClass The role class
* @return Type of the expression.
*/
Type getTypeInClass(List<PathNode> fieldSequence, Class<?> roleClass) throws PathNodeCheckingException {
if (fieldSequence.size() == 1 && fieldSequence.get(0) instanceof PathNodeComponentId) {
// id is always present and always of type String
return String.class;
}

return getTypeInClassNoImplicitId(fieldSequence, roleClass);
}

/**
* Returns the type of a field sequence from a given class. When the field sequence consists
* of multiple elements, the type of the nested field is returned (ie. if the given class
* contains a field x of type C, class C contains a field y and the input field name
* sequence is ("x", "y"), then the result is the type of C.y).
*
* If the given field has generic type, {@link ParameterizedType} is returned, containing
* the type arguments.
*
* Sometimes generic types cannot be inferred. In that case it is possible, that an unknown
* type is returned. In this case the result is an instance of {@link TypeVariable}.
*
* @param fieldNameSequence Sequence of fields (knowledge path split by dots)
* @param clazz The class
* @return Type of the expression.
*/
private Type getTypeInClassNoImplicitId(List<PathNode> fieldNameSequence, Class<?> clazz) throws PathNodeCheckingException {

String firstField = checkAndReadPathNodeField(fieldNameSequence.get(0));

Field field;
try {
field = clazz.getField(firstField);
} catch (NoSuchFieldException | SecurityException e) {
return null;
}

if (!RoleAnnotationsHelper.isPublicAndNonstatic(field)) {
return null;
}

if (fieldNameSequence.size() == 1) {
return field.getGenericType();
} else {
return getTypeInClassNoImplicitId(fieldNameSequence.subList(1, fieldNameSequence.size()), field.getType());
}
}

private String checkAndReadPathNodeField(PathNode pathNode) throws PathNodeCheckingException {
if (!(pathNode instanceof PathNodeField)) {
throw new PathNodeCheckingException("Invalid path node " + pathNode.toString() + ". Only "
+ PathNodeField.class.getSimpleName() + " instances are expected.");
}

return ((PathNodeField)pathNode).getName();
}

}
Loading

0 comments on commit 3e4afeb

Please sign in to comment.