-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor: RolesAnnotationChecker split into parts (used Dependency
Injection): RolesAnnotationChecker + KnowledgePathChecker + TypeComparer + ParameterKnowledgePathExctractor. Updated tests for RolesAnnotationChecker (refactor + split). Added skeleton for ComponentProcessChecker.
- Loading branch information
Showing
18 changed files
with
1,430 additions
and
618 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
jdeeco-core/src/cz/cuni/mff/d3s/deeco/annotations/processor/ComponentProcessChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
*/ |
60 changes: 60 additions & 0 deletions
60
jdeeco-core/src/cz/cuni/mff/d3s/deeco/annotations/processor/GenericTypeComparer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
jdeeco-core/src/cz/cuni/mff/d3s/deeco/annotations/processor/KnowledgePathCheckException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
jdeeco-core/src/cz/cuni/mff/d3s/deeco/annotations/processor/KnowledgePathChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
144 changes: 144 additions & 0 deletions
144
jdeeco-core/src/cz/cuni/mff/d3s/deeco/annotations/processor/KnowledgePathCheckerImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
|
||
} |
Oops, something went wrong.