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

org.apache.openjpa.persistence.criteria.SubqueryImpl Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openjpa.persistence.criteria;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.persistence.criteria.AbstractQuery;
import javax.persistence.criteria.CollectionJoin;
import javax.persistence.criteria.CommonAbstractCriteria;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import javax.persistence.criteria.Subquery;
import javax.persistence.metamodel.EntityType;

import org.apache.openjpa.kernel.exps.Context;
import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.QueryExpressions;
import org.apache.openjpa.kernel.exps.Value;
import org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder;
import org.apache.openjpa.lib.util.OrderedMap;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.persistence.meta.AbstractManagedType;
import org.apache.openjpa.persistence.meta.MetamodelImpl;
import org.apache.openjpa.persistence.meta.Types;

/**
 * Subquery is an expression which itself is a query and always appears in the
 * context of a parent query. A subquery delegates to a captive query for most
 * of the operations but also maintains its own joins and correlated joins.
 *
 * @param  the type selected by this subquery.
 *
 * @author Pinaki Poddar
 * @author Fay Wang
 *
 * @since 2.0.0
 */
class SubqueryImpl extends ExpressionImpl implements Subquery {
    private final AbstractQuery _parent;
    private final CriteriaQueryImpl _delegate;
    private final MetamodelImpl  _model;
    private org.apache.openjpa.kernel.exps.Subquery _subq;
    private List> _corrJoins = null;

    /**
     * Construct a subquery always in the context of a parent query.
     *
     * @param cls the result type of this subquery
     * @param parent the non-null parent query which itself can be a subquery.
     */
    SubqueryImpl(Class cls, AbstractQuery parent) {
        super(cls);
        _parent = parent;
        OrderedMap> params;
        if (parent instanceof CriteriaQueryImpl) {
            _model = ((CriteriaQueryImpl)parent).getMetamodel();
            params = ((CriteriaQueryImpl)parent).getParameterTypes();
        } else if (parent instanceof SubqueryImpl) {
            _model = ((SubqueryImpl)parent).getMetamodel();
            params = ((SubqueryImpl)parent).getInnermostParent().getParameterTypes();
        } else {
            _model = null;
            params = null;
        }
        _delegate = new CriteriaQueryImpl<>(_model, this, params);
    }

    /**
     * Gets the parent query of this subquery.
     * Can be a query or another subquery.
     */
    @Override
    public AbstractQuery getParent() {
        return _parent;
    }

    @Override
    public CommonAbstractCriteria getContainingQuery() {
        return getParent();
    }

    /**
     * Gets the captive query to which this subquery delegates.
     */
    CriteriaQueryImpl getDelegate() {
        return _delegate;
    }

    public MetamodelImpl getMetamodel() {
        return _model;
    }

    Stack getContexts() {
        return getInnermostParent().getContexts();
    }

    /**
     * Gets the 'root' query for this subquery.
     */
    public CriteriaQueryImpl getInnermostParent() {
        return (CriteriaQueryImpl)(((_parent instanceof CriteriaQueryImpl)) ?
            _parent : ((SubqueryImpl)_parent).getInnermostParent());
    }

    @Override
    public Subquery select(Expression expression) {
        _delegate.select(expression);
        return this;
    }

    @Override
    public Expression getSelection() {
        return (Expression)_delegate.getSelection();
    }

    @Override
    public  Root from(EntityType entity) {
        return _delegate.from(entity);
    }

    @Override
    public  Root from(Class entityClass) {
        return _delegate.from(entityClass);
    }

    @Override
    public Set> getRoots() {
        return _delegate.getRoots();
    }

    public Root getRoot() {
        return _delegate.getRoot(false);
    }

    @Override
    public Subquery where(Expression restriction) {
        _delegate.where(restriction);
        return this;
    }

    @Override
    public Subquery where(Predicate... restrictions) {
        _delegate.where(restrictions);
        return this;
    }

    @Override
    public Subquery groupBy(Expression... grouping) {
        _delegate.groupBy(grouping);
        return this;
    }

    @Override
    public Subquery groupBy(List> grouping) {
        _delegate.groupBy(grouping);
        return this;
    }

    @Override
    public Subquery having(Expression restriction) {
        _delegate.having(restriction);
        return this;
    }

    @Override
    public Subquery having(Predicate... restrictions) {
        _delegate.having(restrictions);
        return this;
    }

    @Override
    public Subquery distinct(boolean distinct) {
        _delegate.distinct(distinct);
        return this;
    }

    @Override
    public List> getGroupList() {
        return _delegate.getGroupList();
    }

    @Override
    public Predicate getRestriction() {
        return _delegate.getRestriction();
    }

    @Override
    public Predicate getGroupRestriction() {
        return _delegate.getGroupRestriction();
    }

    @Override
    public boolean isDistinct() {
        return _delegate.isDistinct();
    }

    @Override
    public  Subquery subquery(Class type) {
        return new SubqueryImpl<>(type, this);
    }

    /**
     * Correlate this subquery with the given root.
     */
    @Override
    public  Root correlate(Root root) {
        Types.Entity entity = (Types.Entity)root.getModel();
        RootImpl corrRoot = new RootImpl<>(entity);
        corrRoot.setCorrelatedPath((RootImpl)root);
        _delegate.addRoot(corrRoot);
        return corrRoot;
    }

    @Override
    public Set> getCorrelatedJoins() {
        return _corrJoins == null ? Collections.emptySet() : new CopyOnWriteArraySet(_corrJoins);
    }

    /**
     * Correlate this subquery with the given join.
     */
    @Override
    public  Join correlate(Join parentJoin) {
        Join corrJoin = Joins.clone(parentJoin);
        ((PathImpl)corrJoin).setCorrelatedPath((PathImpl)parentJoin);
        if (_corrJoins == null)
            _corrJoins = new ArrayList<>();
        _corrJoins.add(corrJoin);
        return (Join)corrJoin;
    }

    /**
     * Affirms if this is a correlated subquery.
     */
    public boolean isCorrelated() {
        return _corrJoins != null;
    }

    @Override
    public  CollectionJoin correlate(CollectionJoin parentJoin) {
        Join corrJoin = Joins.clone((Joins.Collection)parentJoin);
        ((PathImpl)corrJoin).setCorrelatedPath((PathImpl)parentJoin);
        if (_corrJoins == null)
            _corrJoins = new ArrayList<>();
        _corrJoins.add(corrJoin);
        return (CollectionJoin)corrJoin;
    }

    @Override
    public  SetJoin correlate(SetJoin parentJoin) {
        Join corrJoin = Joins.clone((Joins.Set)parentJoin);
        ((PathImpl)corrJoin).setCorrelatedPath((PathImpl)parentJoin);
        if (_corrJoins == null)
            _corrJoins = new ArrayList<>();
        _corrJoins.add(corrJoin);
        return (SetJoin)corrJoin;
    }

    @Override
    public  ListJoin correlate(ListJoin parentJoin) {
        Join corrJoin = Joins.clone((Joins.List)parentJoin);
        ((PathImpl)corrJoin).setCorrelatedPath((PathImpl)parentJoin);
        if (_corrJoins == null)
            _corrJoins = new ArrayList<>();
        _corrJoins.add(corrJoin);
        return (ListJoin)corrJoin;
    }

    @Override
    public  MapJoin correlate(MapJoin parentJoin) {
        Join corrJoin = Joins.clone((Joins.Map)parentJoin);
        ((PathImpl)corrJoin).setCorrelatedPath((PathImpl)parentJoin);
        if (_corrJoins == null)
            _corrJoins = new ArrayList<>();
        _corrJoins.add(corrJoin);
        return (MapJoin)corrJoin;
    }

    org.apache.openjpa.kernel.exps.Subquery getSubQ() {
        return _subq;
    }

    /**
     * Convert this path to a kernel path value.
     */
    @Override
    public Value toValue(ExpressionFactory factory, CriteriaQueryImpl q) {
        final boolean subclasses = true;
        CriteriaExpressionBuilder exprBuilder = new CriteriaExpressionBuilder();
        String alias = q.getAlias(this);
        ClassMetaData candidate = getCandidate();
        _subq = factory.newSubquery(candidate, subclasses, alias);
        _subq.setMetaData(candidate);
        Stack contexts = getContexts();
        Context context = new Context(null, _subq, contexts.peek());
        contexts.push(context);
        _delegate.setContexts(contexts);
        QueryExpressions subexp = exprBuilder.getQueryExpressions(factory, _delegate);
        _subq.setQueryExpressions(subexp);
        if (subexp.projections.length > 0)
            JPQLExpressionBuilder.checkEmbeddable(subexp.projections[0], null);
        contexts.pop();
        return _subq;
    }

    // if we are in a subquery against a collection from a
    // correlated parent, the candidate of the subquery
    // should be the class metadata of the collection element
    private ClassMetaData getCandidate() {
        if (getRoots().isEmpty() && _corrJoins != null) {
            FromImpl corrJoin = (FromImpl) _corrJoins.get(0);
            if (corrJoin.getJoins() != null) {
                FromImpl join = (FromImpl)corrJoin.getJoins().iterator().next();
                return getInnermostCandidate(join);
            }
        }

        RootImpl root = (RootImpl)getRoot();
        if (root != null && root.getCorrelatedPath() != null && !root.getJoins().isEmpty()) {
            FromImpl join = (FromImpl) root.getJoins().iterator().next();
            return getInnermostCandidate(join);
        }

        return ((AbstractManagedType)root.getModel()).meta;
    }

    private ClassMetaData getInnermostCandidate(FromImpl from) {
        if (!from.getJoins().isEmpty()) {
            from = (FromImpl) from.getJoins().iterator().next();
            return getInnermostCandidate(from);
        }
        return getCandidate(from);
    }


    private ClassMetaData getCandidate(FromImpl from) {
        return getFieldType(from._member.fmd);
    }

    private static ClassMetaData getFieldType(FieldMetaData fmd) {
        if (fmd == null)
            return null;

        ClassMetaData cmd = null;
        ValueMetaData vmd;

        if ((vmd = fmd.getElement()) != null)
            cmd = vmd.getDeclaredTypeMetaData();
        else if ((vmd = fmd.getKey()) != null)
            cmd = vmd.getDeclaredTypeMetaData();
        else if ((vmd = fmd.getValue()) != null)
            cmd = vmd.getDeclaredTypeMetaData();

        if (cmd == null || cmd.getDescribedType() == Object.class)
            cmd = fmd.getDeclaredTypeMetaData();
        if (cmd == null && fmd.isElementCollection())
            cmd = fmd.getDefiningMetaData();

        return cmd;
    }


    @Override
    public Class getResultType() {
        return getJavaType();
    }

    @Override
    public StringBuilder asValue(AliasContext q) {
        StringBuilder buffer = new StringBuilder();
        _delegate.render(buffer, _delegate.getRoots(), _corrJoins);
        return buffer;
    }

    @Override
    public StringBuilder asVariable(AliasContext q) {
        return asValue(q);
    }
}