All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.buschmais.xo.impl.query.XOQueryImpl Maven / Gradle / Ivy

The newest version!
package com.buschmais.xo.impl.query;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.*;

import com.buschmais.xo.api.Query;
import com.buschmais.xo.api.ResultIterator;
import com.buschmais.xo.api.XOException;
import com.buschmais.xo.api.XOTransaction;
import com.buschmais.xo.api.annotation.Flush;
import com.buschmais.xo.api.metadata.type.DatastoreEntityMetadata;
import com.buschmais.xo.api.metadata.type.DatastoreRelationMetadata;
import com.buschmais.xo.impl.SessionContext;
import com.buschmais.xo.impl.plugin.QueryLanguagePluginRepository;
import com.buschmais.xo.impl.transaction.TransactionalResultIterator;
import com.buschmais.xo.spi.datastore.DatastoreQuery;
import com.buschmais.xo.spi.datastore.DatastoreSession;
import com.buschmais.xo.spi.plugin.QueryLanguagePlugin;
import com.buschmais.xo.spi.session.InstanceManager;

import static java.util.Comparator.comparing;

/**
 * Implementation of a {@link com.buschmais.xo.api.Query}.
 *
 * @param 
 *     The result type.
 * @param 
 *     The query language type.
 * @param 
 *     The query expression type.
 * @param 
 *     The entity type.
 * @param 
 *     The relation type.
 */
public class XOQueryImpl implements Query {

    private Class queryLanguage = null;
    private Boolean flush = null;
    private final QE expression;
    private final SessionContext sessionContext;
    private final QueryLanguagePluginRepository queryLanguagePluginManager;
    private final Class returnType;
    private final Collection> returnTypes;
    private final InstanceManager entityInstanceManager;
    private final InstanceManager relationInstanceManager;
    private Map parameters = null;

    public XOQueryImpl(SessionContext sessionContext, QE expression, Class returnType,
        Collection> returnTypes) {
        this.sessionContext = sessionContext;
        this.entityInstanceManager = sessionContext.getEntityInstanceManager();
        this.relationInstanceManager = sessionContext.getRelationInstanceManager();
        this.queryLanguagePluginManager = sessionContext.getPluginRepositoryManager()
            .getPluginManager(QueryLanguagePlugin.class);
        this.expression = expression;
        this.returnType = returnType;
        this.returnTypes = returnTypes;
    }

    public XOQueryImpl(SessionContext sessionContext, QE expression) {
        this(sessionContext, expression, null, Collections.emptyList());
    }

    public XOQueryImpl(SessionContext sessionContext, QE expression, Class returnType) {
        this(sessionContext, expression, returnType, Collections.emptyList());
    }

    @Override
    public Query using(Class queryLanguage) {
        this.queryLanguage = queryLanguage;
        return sessionContext.getInterceptorFactory()
            .addInterceptor(this, Query.class);
    }

    @Override
    public Query withParameter(String name, Object value) {
        if (parameters == null) {
            parameters = new HashMap<>();
        }
        Object oldValue = parameters.put(name, convertParameter(value));
        if (oldValue != null) {
            throw new XOException("Parameter '" + name + "' has already been assigned to value '" + value + "'.");
        }
        return sessionContext.getInterceptorFactory()
            .addInterceptor(this, Query.class);
    }

    @Override
    public Query withParameters(Map parameters) {
        if (parameters == null) {
            throw new XOException("Parameters must not be null.");
        }
        if (this.parameters != null) {
            throw new XOException("Parameters have already been assigned: " + parameters);
        }
        Map convertedParameters = new HashMap<>(parameters.size(), 1);
        for (Map.Entry parameterEntry : parameters.entrySet()) {
            String name = parameterEntry.getKey();
            Object value = parameterEntry.getValue();
            value = convertParameter(value);
            convertedParameters.put(name, value);
        }
        this.parameters = convertedParameters;
        return sessionContext.getInterceptorFactory()
            .addInterceptor(this, Query.class);
    }

    @Override
    public Query flush(boolean flush) {
        this.flush = flush;
        return this;
    }

    @Override
    public Result execute() {
        DatastoreSession, ?, ?, Relation, ? extends DatastoreRelationMetadata, ?, ?> datastoreSession = sessionContext.getDatastoreSession();
        if (queryLanguage == null) {
            queryLanguage = datastoreSession.getDefaultQueryLanguage();
        }
        DatastoreQuery query;
        QueryLanguagePlugin queryLanguagePlugin = (QueryLanguagePlugin) queryLanguagePluginManager.get(queryLanguage);
        if (queryLanguagePlugin != null) {
            query = queryLanguagePlugin.createQuery(sessionContext.getDatastoreSession());
        } else {
            query = (DatastoreQuery) sessionContext.getDatastoreSession()
                .createQuery(queryLanguage);
        }
        Map effectiveParameters = parameters != null ? parameters : Collections.emptyMap();
        ResultIterator> iterator;
        if (expression instanceof String) {
            flush(null);
            iterator = query.execute((String) expression, effectiveParameters);
        } else if (expression instanceof AnnotatedElement) {
            AnnotatedElement annotatedElement = (AnnotatedElement) expression;
            QL queryAnnotation = sessionContext.getMetadataProvider()
                .getQuery(annotatedElement);
            if (queryAnnotation == null) {
                throw new XOException("Cannot find query annotation on element " + expression.toString());
            }
            flush(annotatedElement);
            iterator = query.execute(queryAnnotation, effectiveParameters);
        } else {
            throw new XOException("Expression type is not supported: " + expression);
        }
        SortedSet> resultTypes = getResultTypes();
        XOTransaction xoTransaction = sessionContext.getXOTransaction();
        return sessionContext.getInterceptorFactory()
            .addInterceptor(
                new QueryResultIterableImpl(sessionContext, xoTransaction != null ? new TransactionalResultIterator<>(iterator, xoTransaction) : iterator,
                    resultTypes), Result.class);
    }

    private void flush(AnnotatedElement annotatedElement) {
        boolean doFlush;
        if (this.flush != null) {
            doFlush = this.flush;
        } else {
            Flush autoFlushAnnotation = annotatedElement != null ? annotatedElement.getAnnotation(Flush.class) : null;
            doFlush = autoFlushAnnotation == null || autoFlushAnnotation.value();
        }
        if (doFlush) {
            sessionContext.getCacheSynchronizationService()
                .flush();
        }
    }

    /**
     * Converts the given parameter value to instances which can be passed to the
     * datastore.
     *
     * @param value
     *     The value.
     * @return The converted value.
     */
    private Object convertParameter(Object value) {
        if (entityInstanceManager.isInstance(value)) {
            return entityInstanceManager.getDatastoreType(value);
        } else if (relationInstanceManager.isInstance(value)) {
            return relationInstanceManager.getDatastoreType(value);
        } else if (value instanceof Collection) {
            Collection collection = (Collection) value;
            List values = new ArrayList<>();
            for (Object o : collection) {
                values.add(convertParameter(o));
            }
            return values;
        }
        return value;
    }

    private SortedSet> getResultTypes() {
        SortedSet> resultTypes = new TreeSet<>(comparing(Class::getName));
        if (returnType != null) {
            resultTypes.add(returnType);
        }
        resultTypes.addAll(returnTypes);
        return resultTypes;
    }

}