org.eclipse.persistence.jpa.jpql.tools.resolver.FromSubqueryResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction 774c696
/*******************************************************************************
* Copyright (c) 2012, 2014 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation
*
******************************************************************************/
package org.eclipse.persistence.jpa.jpql.tools.resolver;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.parser.AbsExpression;
import org.eclipse.persistence.jpa.jpql.parser.AbstractEclipseLinkExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AdditionExpression;
import org.eclipse.persistence.jpa.jpql.parser.AvgFunction;
import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression;
import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression;
import org.eclipse.persistence.jpa.jpql.parser.CountFunction;
import org.eclipse.persistence.jpa.jpql.parser.DivisionExpression;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.IndexExpression;
import org.eclipse.persistence.jpa.jpql.parser.LengthExpression;
import org.eclipse.persistence.jpa.jpql.parser.LocateExpression;
import org.eclipse.persistence.jpa.jpql.parser.LowerExpression;
import org.eclipse.persistence.jpa.jpql.parser.MaxFunction;
import org.eclipse.persistence.jpa.jpql.parser.MinFunction;
import org.eclipse.persistence.jpa.jpql.parser.ResultVariable;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.SizeExpression;
import org.eclipse.persistence.jpa.jpql.parser.SqrtExpression;
import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression;
import org.eclipse.persistence.jpa.jpql.parser.SubstringExpression;
import org.eclipse.persistence.jpa.jpql.parser.SubtractionExpression;
import org.eclipse.persistence.jpa.jpql.parser.SumFunction;
import org.eclipse.persistence.jpa.jpql.parser.TrimExpression;
import org.eclipse.persistence.jpa.jpql.parser.UpperExpression;
import org.eclipse.persistence.jpa.jpql.tools.JPQLQueryContext;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeVisitor;
import org.eclipse.persistence.jpa.jpql.tools.spi.IMapping;
import org.eclipse.persistence.jpa.jpql.tools.spi.IMappingType;
import org.eclipse.persistence.jpa.jpql.tools.spi.IType;
import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration;
import org.eclipse.persistence.jpa.jpql.tools.utility.iterable.SnapshotCloneIterable;
import static org.eclipse.persistence.jpa.jpql.LiteralType.*;
/**
* This {@link Resolver} wraps a subquery that is used as the "root" object in a query's declaration.
*
* Example:
*
*
SELECT e.firstName
* FROM Employee e, (SELECT count(e2), e2.firstName FROM Employee e2) e3
* WHERE e.firstName = e3.firstName
*
* @version 2.5
* @since 2.4
* @author Pascal Filion
*/
public class FromSubqueryResolver extends Resolver {
/**
* The virtual {@link IManagedType} representing the subquery.
*/
private IManagedType managedType;
/**
* The {@link JPQLQueryContext} is used to query information about the application metadata and
* cached information.
*/
private JPQLQueryContext queryContext;
/**
* The subquery being defined as a "root" object.
*/
private SimpleSelectStatement subquery;
/**
* Creates a new FromSubqueryResolver
.
*
* @param parent The parent of this resolver, which is never null
* @param queryContext The context used to query information about the application metadata and
* cached information
* @param subquery
* @exception NullPointerException If the parent is null
*/
public FromSubqueryResolver(Resolver parent,
JPQLQueryContext queryContext,
SimpleSelectStatement subquery) {
super(parent);
this.subquery = subquery;
this.queryContext = queryContext;
}
/**
* {@inheritDoc}
*/
@Override
protected IType buildType() {
return getManagedType().getType();
}
/**
* {@inheritDoc}
*/
@Override
protected ITypeDeclaration buildTypeDeclaration() {
return getType().getTypeDeclaration();
}
/**
* {@inheritDoc}
*/
@Override
public IManagedType getManagedType() {
if (managedType == null) {
managedType = new VirtualManagedType();
}
return managedType;
}
private enum MappingType {
PROPERTY,
RELATIONSHIP,
UNKNOWN
}
/**
* This {@link IManagedType} represents a virtual managed type where its content will be derived
* from the subquery.
*/
protected class VirtualManagedType implements IManagedType {
/**
* The virtual {@link IMapping mappings} representing what is selected.
*/
private Map mappings;
/**
* {@inheritDoc}
*/
public void accept(IManagedTypeVisitor visitor) {
}
/**
* {@inheritDoc}
*/
public int compareTo(IManagedType managedType) {
return getType().getName().compareTo(managedType.getType().getName());
}
/**
* {@inheritDoc}
*/
public IMapping getMappingNamed(String name) {
initializeMappings();
return mappings.get(name);
}
/**
* {@inheritDoc}
*/
public IManagedTypeProvider getProvider() {
return FromSubqueryResolver.this.getProvider();
}
/**
* {@inheritDoc}
*/
public IType getType() {
return getProvider().getTypeRepository().getType(IType.UNRESOLVABLE_TYPE);
}
private void initializeMappings() {
if (mappings == null) {
mappings = new HashMap();
// Create virtual mappings that wraps the select items
VirtualMappingBuilder builder = new VirtualMappingBuilder();
builder.parent = this;
builder.mappings = mappings;
subquery.accept(builder);
}
}
/**
* {@inheritDoc}
*/
public Iterable mappings() {
initializeMappings();
return new SnapshotCloneIterable(mappings.values());
}
}
/**
* This virtual {@link IMapping} wraps one of the select items.
*/
protected class VirtualMapping implements IMapping {
private MappingType mappingType;
private String name;
private IManagedType parent;
private Resolver resolver;
protected VirtualMapping(IManagedType parent,
String name,
Resolver resolver,
MappingType mappingType) {
super();
this.name = name;
this.parent = parent;
this.resolver = resolver;
this.mappingType = mappingType;
}
/**
* {@inheritDoc}
*/
public int compareTo(IMapping mapping) {
return getName().compareTo(mapping.getName());
}
/**
* {@inheritDoc}
*/
public int getMappingType() {
IMapping mapping = resolver.getMapping();
return (mapping != null) ? mapping.getMappingType() : IMappingType.TRANSIENT;
}
/**
* {@inheritDoc}
*/
public String getName() {
return name;
}
/**
* {@inheritDoc}
*/
public IManagedType getParent() {
return parent;
}
/**
* {@inheritDoc}
*/
public IType getType() {
return resolver.getType();
}
/**
* {@inheritDoc}
*/
public ITypeDeclaration getTypeDeclaration() {
return resolver.getTypeDeclaration();
}
/**
* {@inheritDoc}
*/
public boolean hasAnnotation(Class annotationType) {
// TODO: Can we do this???
return getType().hasAnnotation(annotationType);
}
/**
* {@inheritDoc}
*/
public boolean isCollection() {
IMapping mapping = resolver.getMapping();
return (mapping != null) ? mapping.isCollection() : false;
}
/**
* {@inheritDoc}
*/
public boolean isProperty() {
IMapping mapping = resolver.getMapping();
return (mapping != null) ? mapping.isProperty() : (mappingType == MappingType.PROPERTY);
}
/**
* {@inheritDoc}
*/
public boolean isRelationship() {
IMapping mapping = resolver.getMapping();
return (mapping != null) ? mapping.isRelationship() : (mappingType == MappingType.RELATIONSHIP);
}
/**
* {@inheritDoc}
*/
public boolean isTransient() {
IMapping mapping = resolver.getMapping();
return (mapping != null) ? mapping.isTransient() : false;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return name;
}
}
/**
* This visitor will traverse the SELECT
clause and create virtual mappings
* for the state field path expressions and any expression aliased with a result variable.
*/
protected class VirtualMappingBuilder extends AbstractEclipseLinkExpressionVisitor {
/**
* The virtual {@link IMapping} that wraps a select item mapped with the result variable if
* one was defined otherwise the name will be generated based on the type of select item.
*/
private Map mappings;
/**
*
*/
private MappingType mappingType;
/**
* The virtual {@link IManagedType}.
*/
protected IManagedType parent;
/**
* Creates a new VirtualMappingBuilder
.
*/
public VirtualMappingBuilder() {
super();
mappingType = MappingType.UNKNOWN;
}
/**
* Creates
*
* @param name
* @param resolver
* @return
*/
protected IMapping buildMapping(String name, Resolver resolver) {
return new VirtualMapping(parent, name, resolver, mappingType);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AbsExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AdditionExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(AvgFunction expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CollectionExpression expression) {
expression.acceptChildren(this);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ConcatExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(CountFunction expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(DivisionExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(IndexExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LengthExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LocateExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(LowerExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(MaxFunction expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(MinFunction expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(ResultVariable expression) {
String name = queryContext.literal(expression, RESULT_VARIABLE);
if (ExpressionTools.stringIsNotEmpty(name)) {
// Visit the select expression in order to get a mapping type that could
// help determine the mapping type
Expression selectExpression = expression.getSelectExpression();
selectExpression.accept(this);
// Retrieve the select expression's Resolver and wrap it with a virtual mapping
Resolver resolver = queryContext.getResolver(selectExpression);
mappings.put(name, buildMapping(name, resolver));
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SimpleSelectClause expression) {
expression.getSelectExpression().accept(this);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SimpleSelectStatement expression) {
expression.getSelectClause().accept(this);
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SizeExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SqrtExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(StateFieldPathExpression expression) {
if (!expression.startsWithDot()) {
String name = queryContext.literal(expression, PATH_EXPRESSION_LAST_PATH);
if (ExpressionTools.stringIsNotEmpty(name)) {
mappingType = MappingType.UNKNOWN;
Resolver resolver = queryContext.getResolver(expression);
mappings.put(name, buildMapping(name, resolver));
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SubstringExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SubtractionExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(SumFunction expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(TrimExpression expression) {
mappingType = MappingType.PROPERTY;
}
/**
* {@inheritDoc}
*/
@Override
public void visit(UpperExpression expression) {
mappingType = MappingType.PROPERTY;
}
}
}