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

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

There is a newer version: 4.0.0
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.List;

import javax.persistence.criteria.AbstractQuery;
import javax.persistence.criteria.CollectionJoin;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.PluralJoin;
import javax.persistence.criteria.SetJoin;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.CollectionAttribute;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.Type;

import org.apache.openjpa.kernel.exps.AbstractExpressionBuilder;
import org.apache.openjpa.kernel.exps.ExpressionFactory;
import org.apache.openjpa.kernel.exps.Value;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.persistence.meta.AbstractManagedType;
import org.apache.openjpa.persistence.meta.Members;
import org.apache.openjpa.persistence.meta.Members.KeyAttributeImpl;
import org.apache.openjpa.persistence.meta.Members.MapAttributeImpl;
import org.apache.openjpa.persistence.meta.Members.Member;

/**
 * Implements strongly-typed Join expressions via singular and plural attributes.
 * 
 * @author Fay Wang
 * @author Pinaki Poddar
 * 
 * @since 2.0.0
 * 
 */
abstract class Joins {

    static Join clone(Join join) {
        java.util.List members = new ArrayList();
        java.util.List jts = new ArrayList();
        FromImpl root = getMembers((PathImpl)join, members, jts);
        Members.Member member = members.get(0);
        JoinType jt = jts.get(0);
        Join join1 = makeJoin(root, member, jt);
        for (int i = 1; i < members.size(); i++)
            join1 = makeJoin((FromImpl) join1, members.get(i), jts.get(i));

        return join1;
    }

    static Join makeJoin(FromImpl from, Members.Member member,
            JoinType jt) {
        if (member instanceof Members.SingularAttributeImpl)
            return new Joins.SingularJoin(from,
                    (Members.SingularAttributeImpl) member, jt);
        else if (member instanceof Members.CollectionAttributeImpl)
            return new Joins.Collection(from,
                    (Members.CollectionAttributeImpl) member, jt);
        else if (member instanceof Members.ListAttributeImpl)
            return new Joins.List(from, (Members.ListAttributeImpl) member, jt);
        else if (member instanceof Members.SetAttributeImpl)
            return new Joins.Set(from, (Members.SetAttributeImpl) member, jt);
        else if (member instanceof Members.MapAttributeImpl)
            return new Joins.Map(from, (Members.MapAttributeImpl) member, jt);
        return null;
    }

    static FromImpl getMembers(PathImpl join,
            java.util.List members,
            java.util.List jts) {
        PathImpl parent = (PathImpl) join.getParentPath();
        Members.Member member = join.getMember();
        JoinType jt = ((Join) join).getJoinType();
        FromImpl from = null;
        if (parent instanceof RootImpl) {
            members.add(member);
            jts.add(jt);
            return (FromImpl) parent;
        } else {
            from = getMembers(parent, members, jts);
        }
        members.add(member);
        jts.add(jt);
        return from;
    }

    /**
     * Join a single-valued attribute.
     * 
     *
     * @param  type from which joining
     * @param  type of the attribute being joined
     */
    static class SingularJoin extends FromImpl implements Join {
        private final JoinType joinType;
        private boolean allowNull = false;
        
        public SingularJoin(FromImpl from, Members.SingularAttributeImpl member, JoinType jt) {
            super(from, member, member.getJavaType());
            joinType = jt;
            allowNull = joinType != JoinType.INNER;
        }
        
        public JoinType getJoinType() {
            return joinType;
        }

        public FromImpl getParent() {
            return (FromImpl) _parent;
        }
        
        public Member getMember() {
            return (Member) _member;
        }
        
        /**
         * Return the metamodel attribute corresponding to the join.
         * @return metamodel attribute type corresponding to the join
         */
        public Attribute getAttribute() {
            return  (Attribute )_member;
        }
        
        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl c) {
            ClassMetaData meta = _member.fmd.getDeclaredTypeMetaData();
            org.apache.openjpa.kernel.exps.Path path = null;
            SubqueryImpl subquery = c.getDelegator();
            PathImpl parent = getInnermostParentPath();
            Value val = c.getRegisteredValue(this);
            if (val != null)
                return val;
            else if (parent.inSubquery(subquery)) {
                org.apache.openjpa.kernel.exps.Subquery subQ = subquery.getSubQ();
                path = factory.newPath(subQ);
                path.setMetaData(subQ.getMetaData());
                path.setSchemaAlias(c.getAlias(this));
                path.get(_member.fmd, allowNull); 
            } else {
                path = (org.apache.openjpa.kernel.exps.Path) _parent.toValue(factory, c);
                path.get(_member.fmd, allowNull);
                path.setMetaData(meta);
                path.setImplicitType(meta.getDescribedType());
            }
            return path;
        }
        
        @Override
        public org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, 
            CriteriaQueryImpl c) {
            ClassMetaData meta = _member.fmd.getDeclaredTypeMetaData();
            org.apache.openjpa.kernel.exps.Path path = null;
            SubqueryImpl subquery = c.getDelegator();
            PathImpl parent = getInnermostParentPath();
            org.apache.openjpa.kernel.exps.Expression filter = null;
            PathImpl correlatedParentPath = null;
            boolean bind = true;
            java.util.Set> corrJoins = null;
            org.apache.openjpa.kernel.exps.Expression join = null;
            if (!isCorrelated()) {
                if (subquery != null) {
                    corrJoins = subquery.getCorrelatedJoins();
                    org.apache.openjpa.kernel.exps.Subquery subQ = subquery.getSubQ();
                    if ((!corrJoins.isEmpty() && corrJoins.contains(_parent)) || 
                        (corrJoins.isEmpty() && parent.inSubquery(subquery) && _parent.getCorrelatedPath() != null)) { 
                        path = factory.newPath(subQ);
                        correlatedParentPath = _parent.getCorrelatedPath();
                        bind = false;
                    } else { 
                        if (c.isRegistered(_parent)) { 
                            Value var = c.getRegisteredVariable(_parent);
                            path = factory.newPath(var);
                        } else 
                            path = factory.newPath(subQ);
                        path.setMetaData(meta);
                        path.get(_member.fmd, false);
                        path.setSchemaAlias(c.getAlias(_parent));
                    } 
                } else if (c.isRegistered(_parent)) {
                    Value var = c.getRegisteredVariable(_parent);
                    path = factory.newPath(var);
                    path.setMetaData(meta);
                    path.get(_member.fmd, false);
                } else            
                    path = (org.apache.openjpa.kernel.exps.Path)toValue(factory, c);
                
                Class type = meta == null ? AbstractExpressionBuilder.TYPE_OBJECT : meta.getDescribedType();
                Value var = null;
                if (bind) {
                    var = factory.newBoundVariable(c.getAlias(this),type);
                    join = factory.bindVariable(var, path);
                    c.registerVariable(this, var, path);
                }
                
                if (!_member.fmd.isTypePC()) { // multi-valued relation
                    setImplicitContainsTypes(path, var, AbstractExpressionBuilder.CONTAINS_TYPE_ELEMENT);
                    join = factory.contains(path, var);
                }                
            }
            if (getJoins() != null) {
                for (Join join1 : getJoins()) {
                    filter = Expressions.and(factory, 
                             ((FromImpl)join1).toKernelExpression(factory, c), filter);
                }
            }
            org.apache.openjpa.kernel.exps.Expression expr = Expressions.and(factory, join, filter);
            
            if (correlatedParentPath == null) {
                return expr;
            } else {
                org.apache.openjpa.kernel.exps.Path parentPath = null;
                if (corrJoins != null && corrJoins.contains(_parent)) {
                    Value var = getVariableForCorrPath(subquery, correlatedParentPath);
                    parentPath = factory.newPath(var);
                } else {
                    parentPath = (org.apache.openjpa.kernel.exps.Path)correlatedParentPath.toValue(factory, c);
                }
                parentPath.get(_member.fmd, allowNull);
                parentPath.setSchemaAlias(c.getAlias(correlatedParentPath));
                if (c.ctx().getParent() != null && c.ctx().getVariable(parentPath.getSchemaAlias()) == null) 
                    parentPath.setSubqueryContext(c.ctx(), parentPath.getSchemaAlias());
                
                path.setMetaData(meta);
                //filter = bindVariableForKeyPath(path, alias, filter);
                filter = factory.equal(parentPath, path);
                return Expressions.and(factory, expr, filter);
            }
        }
        
        private Value getVariableForCorrPath(SubqueryImpl subquery, PathImpl path) {
            AbstractQuery parent = subquery.getParent();
            if (parent instanceof CriteriaQueryImpl) {
                return ((CriteriaQueryImpl)parent).getRegisteredVariable(path);
            }
            Value var = ((SubqueryImpl)parent).getDelegate().getRegisteredVariable(path); 
            if (var != null)
                return var;
            return getVariableForCorrPath((SubqueryImpl)parent, path);
        }
        
        /**
         * Set the implicit types of the given values based on the fact that
         * the first is supposed to contain the second.
         */
        public void setImplicitContainsTypes(Value val1, Value val2, int op) {
            if (val1.getType() == AbstractExpressionBuilder.TYPE_OBJECT) {
                if (op == AbstractExpressionBuilder.CONTAINS_TYPE_ELEMENT)
                    val1.setImplicitType(Collection.class);
                else
                    val1.setImplicitType(Map.class);
            }

            if (val2.getType() == AbstractExpressionBuilder.TYPE_OBJECT && val1 instanceof Path) {
                FieldMetaData fmd = ((org.apache.openjpa.kernel.exps.Path) val1).last();
                ClassMetaData meta;
                if (fmd != null) {
                    if (op == AbstractExpressionBuilder.CONTAINS_TYPE_ELEMENT || 
                        op == AbstractExpressionBuilder.CONTAINS_TYPE_VALUE) {
                        val2.setImplicitType(fmd.getElement().getDeclaredType());
                        meta = fmd.getElement().getDeclaredTypeMetaData();
                        if (meta != null) {
                            val2.setMetaData(meta);
                        }
                    } else {
                        val2.setImplicitType(fmd.getKey().getDeclaredType());
                        meta = fmd.getKey().getDeclaredTypeMetaData();
                        if (meta != null) {
                            val2.setMetaData(meta);
                        }
                    }
                }
            }
        }
        
        @Override
        public StringBuilder asVariable(AliasContext q) {
            return new StringBuilder(" " + joinType + " JOIN ").append(super.asVariable(q));
        }
    }
    
    /**
     * Join a plural attribute.
     * 
     * @param Z type being joined from
     * @param C Java collection type of the container
     * @param E type of the element being joined to
     * 
     */
    static abstract class AbstractCollection extends FromImpl implements PluralJoin {
        private final JoinType joinType;
        private boolean allowNull = false;
        
        public AbstractCollection(FromImpl from, Members.PluralAttributeImpl member, 
            JoinType jt) {
            super(from, member, member.getBindableJavaType());
            joinType = jt;
            allowNull = joinType != JoinType.INNER;
        }
        
        public final JoinType getJoinType() {
            return joinType;
        }

        /**
         * Gets the parent of this join.
         */
        public final FromImpl getParent() {
            return (FromImpl) _parent;
        }
        
        public Attribute getAttribute() {
            return (Member)_member;
        }
        
        public PluralAttribute getModel() {
            return (PluralAttribute) _member.getType();
        }
        
        public ClassMetaData getMemberClassMetaData() {
            return _member.fmd.isElementCollection() 
                ? _member.fmd.getElement().getEmbeddedMetaData()
                : _member.fmd.getElement().getDeclaredTypeMetaData();
        }

        /**
         * Convert this path to a kernel path (value).
         */
        @Override
        public Value toValue(ExpressionFactory factory, CriteriaQueryImpl c) {
            org.apache.openjpa.kernel.exps.Path path = null;
            SubqueryImpl subquery = c.getDelegator();
            PathImpl parent = getInnermostParentPath();
            
            Value var = c.getRegisteredVariable(this);
            if (var != null) {
                 path = factory.newPath(var);
            } else if (parent.inSubquery(subquery)) {
                org.apache.openjpa.kernel.exps.Subquery subQ = subquery.getSubQ();
                path = factory.newPath(subQ);
                path.setMetaData(subQ.getMetaData());
                path.setSchemaAlias(c.getAlias(this));
            } else {
                path = (org.apache.openjpa.kernel.exps.Path) _parent.toValue(factory, c);
                path.get(_member.fmd, allowNull);
            }
            return path;
        }

        /**
         * Convert this path to a join expression.
         * 
         */
        @Override
        public org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, 
            CriteriaQueryImpl c) {
            ClassMetaData meta = getMemberClassMetaData(); 
            org.apache.openjpa.kernel.exps.Path path = null;
            SubqueryImpl subquery = c.getDelegator();
            org.apache.openjpa.kernel.exps.Expression filter = null;
            java.util.Set> corrJoins = null;
            boolean bind = true;
            org.apache.openjpa.kernel.exps.Expression join = null;
            PathImpl corrJoin = getCorrelatedJoin(this);
            PathImpl corrRoot = getCorrelatedRoot(subquery);

            PathImpl correlatedParentPath = null;
            if (!isCorrelated()) {
                if (subquery != null) {
                    corrJoins = subquery.getCorrelatedJoins();
                    org.apache.openjpa.kernel.exps.Subquery subQ = subquery.getSubQ();
                    path = factory.newPath(subQ); 
                    if ((corrJoin != null || corrRoot != null) && _parent.getCorrelatedPath() != null) { 
                        subQ.setSubqAlias(c.getAlias(this));
                        path = factory.newPath(subQ);
                        correlatedParentPath = _parent.getCorrelatedPath();
                        bind = false;
                    } else {    
                        if (c.isRegistered(_parent)) { 
                            Value var = c.getRegisteredVariable(_parent);
                            path = factory.newPath(var);
                        } else {
                            path = factory.newPath(subQ);
                        }
                        path.setMetaData(meta);
                        path.get(_member.fmd, false);
                        path.setSchemaAlias(c.getAlias(_parent));
                    } 
                } else if (c.isRegistered(_parent)) {
                    Value var = c.getRegisteredVariable(_parent);
                    path = factory.newPath(var);
                    path.setMetaData(meta);
                    path.get(_member.fmd, false);
                } else {           
                    path = (org.apache.openjpa.kernel.exps.Path)toValue(factory, c);
                }
                Class type = meta == null ? AbstractExpressionBuilder.TYPE_OBJECT : meta.getDescribedType(); 
                if (bind) {
                    Value var = factory.newBoundVariable(c.getAlias(this), type);
                    join = factory.bindVariable(var, path);
                    c.registerVariable(this, var, path);
                }
            }
            if (getJoins() != null) {
                for (Join join1 : getJoins()) {
                    filter = Expressions.and(factory, 
                        ((FromImpl)join1).toKernelExpression(factory, c), filter);
                }
            }
            org.apache.openjpa.kernel.exps.Expression expr = Expressions.and(factory, join, filter);
            if (correlatedParentPath == null) {
                return expr;
            } else {
                org.apache.openjpa.kernel.exps.Path parentPath = null;
                if (!corrJoins.isEmpty() && corrJoins.contains(_parent)) {
                    Value var = getVariableForCorrPath(subquery, correlatedParentPath);
                    parentPath = factory.newPath(var);
                } else {
                    parentPath = (org.apache.openjpa.kernel.exps.Path) correlatedParentPath.toValue(factory, c);
                }
                parentPath.get(_member.fmd, allowNull);
                parentPath.setSchemaAlias(c.getAlias(correlatedParentPath));
                if (c.ctx().getParent() != null && c.ctx().getVariable(parentPath.getSchemaAlias()) == null) 
                    parentPath.setSubqueryContext(c.ctx(), parentPath.getSchemaAlias());
                
                path.setSchemaAlias(c.getAlias(correlatedParentPath));
                path.setMetaData(meta);
                Class type = meta == null ? AbstractExpressionBuilder.TYPE_OBJECT : meta.getDescribedType(); 
                Value var = factory.newBoundVariable(c.getAlias(this), type);
                join = factory.bindVariable(var, parentPath);
                
                if (_member.fmd.getDeclaredTypeCode() == JavaTypes.MAP)
                    c.registerVariable(this, var, parentPath);
                
                if (_member.fmd.isElementCollection()) {
                    filter = Expressions.and(factory, join, filter);
                } else { 
                    filter = factory.equal(parentPath, path);
                }
                return Expressions.and(factory, expr, filter);
            }
        }
        
        private Value getVariableForCorrPath(SubqueryImpl subquery, PathImpl path) {
            AbstractQuery parent = subquery.getParent();
            if (parent instanceof CriteriaQueryImpl) {
                return ((CriteriaQueryImpl)parent).getRegisteredVariable(path);
            }
            Value var = ((SubqueryImpl)parent).getDelegate().getRegisteredVariable(path); 
            if (var != null)
                return var;
            return getVariableForCorrPath((SubqueryImpl)parent, path);
        }
        
        @Override
        public StringBuilder asVariable(AliasContext q) {
            return new StringBuilder(" " + joinType + " JOIN ").append(super.asVariable(q));
        }
    }
    
    /**
     * Join a java.util.Collection<E> type attribute.
     *
     * @param  the type from which being joined
     * @param  the type of the the collection attribute elements
     */
    static class Collection extends AbstractCollection,E> 
        implements CollectionJoin {
        public Collection(FromImpl parent, Members.CollectionAttributeImpl member, JoinType jt) {
            super(parent, member, jt);
        }
        
        public CollectionAttribute getModel() {
            return (CollectionAttribute)_member;
        }
    }
    
    /**
     * Join a java.util.Set<E> type attribute.
     *
     * @param  the type from which being joined
     * @param  the type of the the set attribute elements
     */
    static class Set extends AbstractCollection,E> 
        implements SetJoin {
        public Set(FromImpl parent, Members.SetAttributeImpl member, JoinType jt) {
            super(parent, member, jt);
        }
        
        public SetAttribute getModel() {
            return (SetAttribute)_member;
        }
    }
    
    /**
     * Join a java.util.List<E> type attribute.
     *
     * @param  the type from which being joined
     * @param  the type of the the list attribute elements
     */
    
    static class List extends AbstractCollection,E> 
        implements ListJoin {
        
        public List(FromImpl parent, Members.ListAttributeImpl member, JoinType jt) {
            super(parent, member, jt);
        }
        
        public ListAttribute getModel() {
            return (ListAttribute)_member;
        }
        
        public Expression index() {
            return new Expressions.Index(this);
        }
    }
    
    /**
     * Join a java.util.Map<K,V> type attribute.
     *
     * @param  the type from which being joined
     * @param  the type of the the map attribute keys
     * @param  the type of the the map attribute values
     */
    
    static class Map extends AbstractCollection,V> 
        implements MapJoin {
        private KeyJoin _keyJoin;
        
        public Map(FromImpl parent, Members.MapAttributeImpl member, JoinType jt) {
            super(parent, member, jt);
        }
        
        public MapAttribute getModel() {
            return (MapAttribute) _member;
        }
        
        public Join, K> joinKey() {
            return joinKey(JoinType.INNER);
        }
        
        /**
         * Create a pseudo-attribute of a pseudo-managed type for java.util.Map<K,V> 
         * to represent its keys of type java.util.Set<V>.
         */
        public Join, K> joinKey(JoinType jt) {
            AbstractManagedType> pseudoOwner = (AbstractManagedType>)
               _member.owner.model.getType(getModel().getJavaType());
            KeyAttributeImpl, K> keyAttr = 
              new Members.KeyAttributeImpl, K>(pseudoOwner, _member.fmd);
            _keyJoin = new KeyJoin((FromImpl>)this, keyAttr, jt);
            return _keyJoin;
        }
        
        public Expression> entry() {
            return new MapEntry(this);
        }
        
        public Path key() {
            return new MapKey(this);
        }
        
        public Path value() {
            return this;
        }
                
        @Override
        public org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, 
            CriteriaQueryImpl c) {
            return (_keyJoin == null) 
                ? super.toKernelExpression(factory, c)
                : _keyJoin.toKernelExpression(factory, c);
        }
    }
    
       
   static class MapKey extends PathImpl {
       private final Map map;
       private final MapAttributeImpl attr;
       
       public MapKey(Map joinMap){
           super(((MapAttribute)joinMap.getAttribute()).getKeyJavaType());
           attr = ((MapAttributeImpl)joinMap.getAttribute());
           this.map = joinMap;
       }
       
       @Override
       public Type getType() {
           return attr.getKeyType();
       }
       
       /**
        * Convert this path to a join expression.
        * 
        */
       @Override
       public Value toValue(ExpressionFactory factory, CriteriaQueryImpl c) {
           Value val = c.getRegisteredVariable(map);
           org.apache.openjpa.kernel.exps.Path path = factory.newPath(val);
           return factory.getKey(path);
       }
       
       @Override
       public StringBuilder asValue(AliasContext q) {
           StringBuilder buffer = new StringBuilder("KEY(");
           Value var = q.getRegisteredVariable(map);
           buffer.append(var != null ? var.getName() : map.asValue(q)).append(")");
           return buffer;
       }
   }
       
   static class MapEntry extends ExpressionImpl> {
       private final Map map;
       
       public MapEntry(Map joinMap){
           super(((MapAttribute)joinMap.getAttribute()).getJavaType());
           this.map = joinMap;
       }
       
       /**
        * Convert this path to a join expression.
        * 
        */
       @Override
       public Value toValue(ExpressionFactory factory, CriteriaQueryImpl c) {
           Value val = c.getRegisteredVariable(map);
           org.apache.openjpa.kernel.exps.Path path = factory.newPath(val);
           org.apache.openjpa.kernel.exps.Path var = factory.newPath(val);
           return factory.mapEntry(path, var);
       }
       
       @Override
       public StringBuilder asValue(AliasContext q) {
           StringBuilder buffer = new StringBuilder("ENTRY(");
           Value var = q.getRegisteredVariable(map);
           buffer.append(var != null ? var.getName() : map.asValue(q)).append(")");
           return buffer;
       }
   }
   
   /**
    * A specialized join via key of a java.util.Map<K,V> attribute.
    * Treats the map key as a pseudo-attribute of type java.util.Set<K> of a pseduo-managed type corresponding
    * to java.util.Map<K,V>. 
    *  
    * @param  the type of the key of the original java.util.Map attribute 
    * @param  the type of the value of the original java.util.Map attribute
    */
   static class KeyJoin extends Joins.Set, K> {
    public KeyJoin(FromImpl> parent, KeyAttributeImpl, K> member, 
            JoinType jt) {
        super(parent, member, jt);
    }
    
    @Override
    public Value toValue(ExpressionFactory factory, CriteriaQueryImpl c) {
        return factory.getKey(getParent().toValue(factory, c));
    }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy