| /* |
| * 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 javax.persistence.criteria.Expression; |
| import javax.persistence.criteria.Join; |
| import javax.persistence.criteria.JoinType; |
| import javax.persistence.criteria.Path; |
| import javax.persistence.metamodel.Bindable; |
| import javax.persistence.metamodel.ManagedType; |
| import javax.persistence.metamodel.MapAttribute; |
| import javax.persistence.metamodel.PluralAttribute; |
| import javax.persistence.metamodel.SingularAttribute; |
| import javax.persistence.metamodel.Type; |
| import javax.persistence.metamodel.Type.PersistenceType; |
| |
| 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.persistence.meta.Members; |
| import org.apache.openjpa.persistence.meta.Members.Member; |
| |
| /** |
| * Path is an expression representing a persistent attribute traversed from a parent path. |
| * The type of the path is the type of the persistent attribute. |
| * If the persistent attribute is bindable, then further path can be traversed from this path. |
| * |
| * @author Pinaki Poddar |
| * @author Fay Wang |
| * |
| * @param <Z> the type of the parent path |
| * @param <X> the type of this path |
| */ |
| class PathImpl<Z,X> extends ExpressionImpl<X> implements Path<X> { |
| protected final PathImpl<?,Z> _parent; |
| protected final Members.Member<? super Z,?> _member; |
| private boolean isEmbedded = false; |
| private PathImpl<?,?> _correlatedPath; |
| |
| /** |
| * Protected constructor use by root path which neither represent a member nor has a parent. |
| */ |
| protected PathImpl(Class<X> cls) { |
| super(cls); |
| _parent = null; |
| _member = null; |
| } |
| |
| /** |
| * Create a path from the given parent representing the given member. |
| * |
| * @param parent the path from which this path needs to be constructed. Must not be null. |
| * @param member the persistent property that represents this path. |
| * @param cls denotes the type expressed by this path. |
| */ |
| public PathImpl(PathImpl<?,Z> parent, Members.Member<? super Z, ?> member, Class<X> cls) { |
| super(cls); |
| _parent = parent; |
| if (_parent.isEmbedded) { |
| FieldMetaData fmd = getEmbeddedFieldMetaData(member.fmd); |
| _member = new Members.SingularAttributeImpl(member.owner, fmd); |
| } else { |
| _member = member; |
| } |
| isEmbedded = _member.fmd.isElementCollection() ? _member.fmd.getElement().isEmbedded() : |
| _member.fmd.isEmbedded(); |
| } |
| |
| /** |
| * Gets the bindable object that corresponds to the path expression. |
| * |
| * @throws IllegalArgumentException if this path is not bindable |
| */ |
| public Bindable<X> getModel() { |
| if (_member instanceof Bindable<?> == false) { |
| throw new IllegalArgumentException(this + " represents a basic path and not a bindable"); |
| } |
| return (Bindable<X>)_member; |
| } |
| |
| /** |
| * Gets the parent of this path or null if this path is the root. |
| */ |
| public final Path<Z> getParentPath() { |
| return _parent; |
| } |
| |
| /** |
| * Gets the path that originates this traversal. Can be itself if this path is the root. |
| */ |
| public PathImpl<?,?> getInnermostParentPath() { |
| return (_parent == null) ? this : _parent.getInnermostParentPath(); |
| } |
| |
| /** |
| * Gets the field that may have been embedded inside the given field. |
| * For example, a given primary key field which is using an embedded class as a complex primary key. |
| * @param fmd a given field |
| * @return the embedded field or the given field itself |
| */ |
| protected FieldMetaData getEmbeddedFieldMetaData(FieldMetaData fmd) { |
| Members.Member<?,?> member = getInnermostMember(_parent,_member); |
| |
| ClassMetaData embeddedMeta = member.fmd.isElementCollection() |
| ? member.fmd.getElement().getEmbeddedMetaData() |
| : member.fmd.getEmbeddedMetaData(); |
| |
| return (embeddedMeta != null) ? embeddedMeta.getField(fmd.getName()) : fmd; |
| } |
| |
| protected Members.Member<?,?> getInnermostMember(PathImpl<?,?> parent, Members.Member<?,?> member) { |
| return member != null ? member : getInnermostMember(parent._parent, parent._member); |
| } |
| |
| /** |
| * Makes this path correlated to the given path. |
| */ |
| public void setCorrelatedPath(PathImpl<?,?> correlatedPath) { |
| _correlatedPath = correlatedPath; |
| } |
| |
| /** |
| * Gets the path correlated to this path, if any. |
| */ |
| public PathImpl<?,?> getCorrelatedPath() { |
| return _correlatedPath; |
| } |
| |
| /** |
| * Affirms if this path is correlated to another path. |
| */ |
| public boolean isCorrelated() { |
| return _correlatedPath != null; |
| } |
| |
| /** |
| * Convert this path to a kernel path. |
| */ |
| @Override |
| public Value toValue(ExpressionFactory factory, CriteriaQueryImpl<?> q) { |
| if (q.isRegistered(this)) |
| return q.getRegisteredValue(this); |
| org.apache.openjpa.kernel.exps.Path path = null; |
| SubqueryImpl<?> subquery = q.getDelegator(); |
| boolean allowNull = _parent == null ? false : _parent instanceof Join |
| && ((Join<?,?>)_parent).getJoinType() != JoinType.INNER; |
| PathImpl<?,?> corrJoin = getCorrelatedJoin(this); |
| PathImpl<?,?> corrRoot = getCorrelatedRoot(subquery); |
| if (_parent != null && q.isRegistered(_parent)) { |
| path = factory.newPath(q.getRegisteredVariable(_parent)); |
| path.setSchemaAlias(q.getAlias(_parent)); |
| path.get(_member.fmd, allowNull); |
| } else if (_parent != null && _parent._correlatedPath != null && q.isRegistered(_parent._correlatedPath)){ |
| path = factory.newPath(q.getRegisteredVariable(_parent._correlatedPath)); |
| path.setSchemaAlias(q.getAlias(_parent._correlatedPath)); |
| path.get(_member.fmd, allowNull); |
| } else if (corrJoin != null || corrRoot != null) { |
| org.apache.openjpa.kernel.exps.Subquery subQ = subquery.getSubQ(); |
| path = factory.newPath(subQ); |
| path.setMetaData(subQ.getMetaData()); |
| path.setSchemaAlias(q.getAlias(_parent)); |
| traversePath(_parent, path, _member.fmd); |
| } else if (_parent != null) { |
| Value val = _parent.toValue(factory, q); |
| if (val instanceof org.apache.openjpa.kernel.exps.Path) { |
| path = (org.apache.openjpa.kernel.exps.Path)val; |
| path.get(_member.fmd, allowNull); |
| } else { |
| val.setAlias(q.getAlias(this)); |
| return val; |
| } |
| } else if (_parent == null) { |
| path = factory.newPath(); |
| path.setMetaData(q.getMetamodel().getRepository().getCachedMetaData(getJavaType())); |
| } |
| if (_member != null && !_member.isCollection()) { |
| path.setImplicitType(getJavaType()); |
| } |
| path.setAlias(q.getAlias(this)); |
| return path; |
| } |
| |
| public PathImpl<?,?> getCorrelatedRoot(SubqueryImpl<?> subquery) { |
| if (subquery == null) |
| return null; |
| PathImpl<?,?> root = getInnermostParentPath(); |
| if (subquery.getRoots() != null && subquery.getRoots().contains(this)) |
| return root; |
| return null; |
| } |
| |
| |
| public PathImpl<?,?> getCorrelatedJoin(PathImpl<?,?> path) { |
| if (path._correlatedPath != null) |
| return path._correlatedPath; |
| if (path._parent == null) |
| return null; |
| return getCorrelatedJoin(path._parent); |
| } |
| |
| /** |
| * Affirms if this receiver occurs in the roots of the given subquery. |
| */ |
| public boolean inSubquery(SubqueryImpl<?> subquery) { |
| return subquery != null && (subquery.getRoots() == null ? false : subquery.getRoots().contains(this)); |
| } |
| |
| protected void traversePath(PathImpl<?,?> parent, org.apache.openjpa.kernel.exps.Path path, FieldMetaData fmd) { |
| boolean allowNull = parent == null ? false : parent instanceof Join |
| && ((Join<?,?>)parent).getJoinType() != JoinType.INNER; |
| FieldMetaData fmd1 = parent._member == null ? null : parent._member.fmd; |
| PathImpl<?,?> parent1 = parent._parent; |
| if (parent1 == null || parent1.getCorrelatedPath() != null) { |
| if (fmd != null) |
| path.get(fmd, allowNull); |
| return; |
| } |
| traversePath(parent1, path, fmd1); |
| if (fmd != null) |
| path.get(fmd, allowNull); |
| } |
| |
| /** |
| * Gets a new path that represents the given single-valued attribute from this path. |
| */ |
| public <Y> Path<Y> get(SingularAttribute<? super X, Y> attr) { |
| if (getType() != attr.getDeclaringType()) { |
| attr = (SingularAttribute)((ManagedType)getType()).getAttribute(attr.getName()); |
| } |
| return new PathImpl<X,Y>(this, (Members.SingularAttributeImpl<? super X, Y>)attr, attr.getJavaType()); |
| } |
| |
| /** |
| * Gets a new path that represents the given multi-valued attribute from this path. |
| */ |
| public <E, C extends java.util.Collection<E>> Expression<C> get(PluralAttribute<X, C, E> coll) { |
| if (getType() != coll.getDeclaringType()) { |
| coll = (PluralAttribute)((ManagedType)getType()).getAttribute(coll.getName()); |
| } |
| return new PathImpl<X,C>(this, (Members.PluralAttributeImpl<? super X, C, E>)coll, coll.getJavaType()); |
| } |
| |
| /** |
| * Gets a new path that represents the given map-valued attribute from this path. |
| */ |
| public <K, V, M extends java.util.Map<K, V>> Expression<M> get(MapAttribute<X, K, V> map) { |
| if (getType() != map.getDeclaringType()) { |
| map = (MapAttribute)((ManagedType)getType()).getAttribute(map.getName()); |
| } |
| return new PathImpl<X,M>(this, (Members.MapAttributeImpl<? super X,K,V>)map, (Class<M>)map.getJavaType()); |
| } |
| |
| /** |
| * Gets a new path that represents the attribute of the given name from this path. |
| * |
| * @exception IllegalArgumentException if this path represents a basic attribute that is can not be traversed |
| * further. |
| */ |
| public <Y> Path<Y> get(String attName) { |
| Type<?> type = this.getType(); |
| if (type.getPersistenceType() == PersistenceType.BASIC) { |
| throw new IllegalArgumentException(this + " is a basic path and can not be navigated to " + attName); |
| } |
| |
| Members.Member<? super X, Y> next = (Members.Member<? super X, Y>) |
| ((ManagedType<? super X>)type).getAttribute(attName); |
| return new PathImpl<X,Y>(this, next, next.getJavaType()); |
| } |
| |
| public Type<?> getType() { |
| return _member.getType(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Member<? extends Z, X> getMember() { |
| return (Member<? extends Z, X>) _member; |
| } |
| |
| /** |
| * Get the type() expression corresponding to this path. |
| */ |
| public Expression<Class<? extends X>> type() { |
| return new Expressions.Type<Class<? extends X>>(this); |
| } |
| |
| public StringBuilder asValue(AliasContext q) { |
| StringBuilder buffer = new StringBuilder(); |
| if (_parent != null) { |
| Value var = q.getRegisteredVariable(_parent); |
| buffer.append(var != null ? var.getName() : _parent.asValue(q)).append("."); |
| } |
| if (_member != null) { |
| buffer.append(_member.fmd.getName()); |
| } |
| return buffer; |
| } |
| |
| public StringBuilder asVariable(AliasContext q) { |
| Value var = q.getRegisteredVariable(this); |
| return asValue(q).append(" ").append(var == null ? "?" : var.getName()); |
| } |
| } |