lundi 4 février 2019

Type checking of a JPA Criteria Path

I am writing an API that generates Predicates corresponding to Criterion classes I've created.

This is the base criterion class:

public abstract class AbstractCriterion<O extends SearchOperator, V> {

    protected O operator;
    protected String pathExpression;
    protected V value;
    protected SearchCriteria<?> searchCriteria;

    public AbstractCriterion(SearchCriteria<?> searchCriteria, O operator, String pathExpression, V value) {
        ...
    }

    public abstract Predicate getPredicate(CriteriaBuilder criteriaBuilder, Root<?> root);

    ...
}

Here is the CollectionCriterion class:

public class CollectionCriterion<E> extends AbstractCriterion<CollectionSearchOperator, Object> {

    public CollectionCriterion(SearchCriteria<?> searchCriteria, CollectionSearchOperator operator, String pathExpression, Object value) {
        ...
    }

    public Predicate getPredicate(CriteriaBuilder criteriaBuilder, Root<?> root) {
        ...
        //here I retrieve the field corresponding to the path expression
        //for instance I retrieve the field country corresponding to the
        //path "person.address.country"
        Field field = JpaUtils.navigateTo(searchCriteria.getManagedClass(), pathExpression);

        //here I got to do somme validation and check that the retrieved 
        //field is a Collection and ideally check if it's a Collection
        //containing only E elements (Collection<E>)
        if(!Collection.class.isAssignableFrom(field.getType())) {
            throw new Exception(...);
        }
        else {
            //if it's a collection check if it contains only
            //instances of E
        }

        ...

        String[] pathArray = pathExpression.split(".");

        //I iterate after splitting the string path expression to get
        //the last element's Path object. This is the main reason why
        //I am doing validation
        Path<Object> tempPath = root.get(pathArray[0]);

        for(int i = 1; i < pathArray.length; i++) {

            if(i != pathArray.length - 1) {
                tempPath = tempPath.get(pathArray[i]);
            }
        }

        Path<Collection<E>> path = tempPath.<Collection<E>> get(pathArray[lastIndex]);

        switch(operator) {
            ...
        }
    }

1- My first question is about finding a way for checking that the field is a collection of E elements, I am aware of the type erasure at runtime (here from stackoverflow), but I found the following tools that can retrieve generic type parameters: Typetools and Classmate. Is it possible to use those libraries to solve this problem?

2- Is it possible to do this check through a simple cast of the value of the field?

Object instance = field.getDeclaringClass().getConstructor().newInstance();

try {
    Collection<E> collection = (Collection<E>) field.getValue(instance);
}
catch(ClassCastException e) {
    throw new Exception(...);
}

3- If not possible at all, how does the generic method Path.get() behave when passing an argument type like Collection<E>? Does it throws an exception if the string argument is not a Collection<E>?

Path<Collection<E>> path = tempPath.<Collection<E>> get(pathArray[lastIndex]);

Thanks for your time.





Aucun commentaire:

Enregistrer un commentaire