/*
* Copyright 2014 - 2017 Blazebit.
*
* Licensed 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 com.blazebit.persistence.criteria.impl.path;
import com.blazebit.persistence.criteria.BlazeExpression;
import com.blazebit.persistence.criteria.BlazePath;
import com.blazebit.persistence.criteria.impl.BlazeCriteriaBuilderImpl;
import com.blazebit.persistence.criteria.impl.RenderContext;
import com.blazebit.persistence.criteria.impl.expression.AbstractExpression;
import com.blazebit.persistence.criteria.impl.expression.PathTypeExpression;
import javax.persistence.criteria.Path;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* @author Christian Beikov
* @since 1.2.0
*/
public abstract class AbstractPath<X> extends AbstractExpression<X> implements BlazePath<X> {
private static final long serialVersionUID = 1L;
private final AbstractPath<?> basePath;
private final BlazeExpression<Class<? extends X>> typeExpression;
private Map<String, Path<?>> attributePathCache;
@SuppressWarnings({"unchecked"})
public AbstractPath(BlazeCriteriaBuilderImpl criteriaBuilder, Class<X> javaType, AbstractPath<?> basePath) {
super(criteriaBuilder, javaType);
this.basePath = basePath;
this.typeExpression = new PathTypeExpression(criteriaBuilder, getJavaType(), this);
}
public AbstractPath<?> getBasePath() {
return basePath;
}
@Override
public AbstractPath<?> getParentPath() {
return getBasePath();
}
public abstract <T extends X> AbstractPath<T> treatAs(Class<T> treatAsType);
protected final <T> EntityType<T> getTreatType(Class<T> type) {
return criteriaBuilder.getEntityMetamodel().entity(type);
}
@Override
@SuppressWarnings({"unchecked"})
public BlazeExpression<Class<? extends X>> type() {
return typeExpression;
}
public abstract Attribute<?, ?> getAttribute();
protected abstract Attribute<?, ?> findAttribute(String attributeName);
protected abstract boolean isDereferencable();
public String getPathExpression() {
return getBasePath().getPathExpression() + "." + getAttribute().getName();
}
private void checkGet(Attribute<?, ?> attribute) {
checkDereferenceAllowed();
if (attribute == null) {
throw new IllegalArgumentException("Null attribute");
}
}
protected final Path<?> getAttributePath(String attributeName) {
return attributePathCache == null ? null : attributePathCache.get(attributeName);
}
protected final void putAttributePath(String attributeName, Path<?> path) {
if (attributePathCache == null) {
attributePathCache = new HashMap<String, Path<?>>();
}
attributePathCache.put(attributeName, path);
}
@Override
@SuppressWarnings({"unchecked"})
public <Y> BlazePath<Y> get(SingularAttribute<? super X, Y> attribute) {
checkGet(attribute);
SingularAttributePath<Y> path = (SingularAttributePath<Y>) getAttributePath(attribute.getName());
if (path == null) {
path = new SingularAttributePath<Y>(criteriaBuilder, attribute.getJavaType(), this, attribute);
putAttributePath(attribute.getName(), path);
}
return path;
}
@Override
@SuppressWarnings({"unchecked"})
public <E, C extends Collection<E>> BlazeExpression<C> get(PluralAttribute<X, C, E> attribute) {
checkGet(attribute);
PluralAttributePath<C> path = (PluralAttributePath<C>) getAttributePath(attribute.getName());
if (path == null) {
path = new PluralAttributePath<C>(criteriaBuilder, this, attribute);
putAttributePath(attribute.getName(), path);
}
return path;
}
@Override
@SuppressWarnings({"unchecked"})
public <K, V, M extends Map<K, V>> BlazeExpression<M> get(MapAttribute<X, K, V> attribute) {
checkGet(attribute);
PluralAttributePath<M> path = (PluralAttributePath<M>) getAttributePath(attribute.getName());
if (path == null) {
path = new PluralAttributePath<M>(criteriaBuilder, this, (PluralAttribute<?, M, ?>) attribute);
putAttributePath(attribute.getName(), path);
}
return path;
}
@Override
@SuppressWarnings({"unchecked"})
public <Y> BlazePath<Y> get(String attributeName) {
checkDereferenceAllowed();
final Attribute attribute = getAttribute(attributeName);
if (attribute.isCollection()) {
final PluralAttribute<X, Y, ?> pluralAttribute = (PluralAttribute<X, Y, ?>) attribute;
if (PluralAttribute.CollectionType.MAP.equals(pluralAttribute.getCollectionType())) {
return (PluralAttributePath<Y>) this.<Object, Object, Map<Object, Object>>get((MapAttribute) pluralAttribute);
} else {
return (PluralAttributePath<Y>) this.get((PluralAttribute) pluralAttribute);
}
} else {
return get((SingularAttribute<X, Y>) attribute);
}
}
protected final Attribute getAttribute(String attributeName) {
if (attributeName == null) {
throw new IllegalArgumentException("Null attribute name");
}
final Attribute attribute = findAttribute(attributeName);
// Some old hibernate versions don't throw an exception but return null
if (attribute == null) {
throw new IllegalArgumentException("Could not find attribute '" + attributeName + "' in '" + getBasePath().getPathExpression() + "'");
}
return attribute;
}
public void prepareAlias(RenderContext context) {
AbstractPath<?> base = getBasePath();
if (base != null) {
base.prepareAlias(context);
}
}
public void renderPathExpression(RenderContext context) {
prepareAlias(context);
getBasePath().renderPathExpression(context);
context.getBuffer()
.append('.')
.append(getAttribute().getName());
}
@Override
public void render(RenderContext context) {
AbstractPath<?> base = getBasePath();
if (base != null) {
base.renderPathExpression(context);
context.getBuffer()
.append('.')
.append(getAttribute().getName());
} else {
context.getBuffer().append(getAttribute().getName());
}
}
private void checkDereferenceAllowed() {
if (!isDereferencable()) {
throw new IllegalArgumentException("Dereferencing attributes in '" + getBasePath().getPathExpression() + "' is not allowed!");
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof AbstractPath<?>)) {
return false;
}
AbstractPath<?> that = (AbstractPath<?>) o;
if (getAttribute() != null ? !getAttribute().equals(that.getAttribute()) : that.getAttribute() != null) {
return false;
}
return getAlias() != null ? getAlias().equals(that.getAlias()) : that.getAlias() == null;
}
@Override
public int hashCode() {
int result = getAttribute() != null ? getAttribute().hashCode() : 0;
result = 31 * result + (getAlias() != null ? getAlias().hashCode() : 0);
return result;
}
}