/*
* Copyright 2015, The Querydsl Team (http://www.querydsl.com/team)
*
* 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.querydsl.core.types.dsl;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
import com.querydsl.core.types.*;
/**
* {@code BeanPath} represents bean paths
*
* @author tiwe
*
* @param <T> expression type
*/
public class BeanPath<T> extends SimpleExpression<T> implements Path<T> {
private static final long serialVersionUID = -1845524024957822731L;
private final Map<Class<?>, Object> casts = new ConcurrentHashMap<Class<?>, Object>();
@Nullable
private final PathInits inits;
private final PathImpl<T> pathMixin;
public BeanPath(Class<? extends T> type, String variable) {
this(type, PathMetadataFactory.forVariable(variable), null);
}
public BeanPath(Class<? extends T> type, Path<?> parent, String property) {
this(type, PathMetadataFactory.forProperty(parent, property), null);
}
public BeanPath(Class<? extends T> type, PathMetadata metadata) {
this(type, metadata, null);
}
public BeanPath(Class<? extends T> type, PathMetadata metadata, @Nullable PathInits inits) {
super(ExpressionUtils.path(type, metadata));
this.pathMixin = (PathImpl<T>) mixin;
this.inits = inits;
}
@Override
public final <R,C> R accept(Visitor<R,C> v, C context) {
// mixin is not used here, because subtype instances may have data that needs to be made available
return v.visit(this, context);
}
/**
* Cast the path to a subtype querytype
*
* @param <U>
* @param clazz subtype class
* @return subtype instance with the same identity
*/
@SuppressWarnings("unchecked")
public <U extends BeanPath<? extends T>> U as(Class<U> clazz) {
try {
if (!casts.containsKey(clazz)) {
PathMetadata metadata;
if (pathMixin.getMetadata().getPathType() != PathType.COLLECTION_ANY) {
metadata = PathMetadataFactory.forDelegate(pathMixin);
} else {
metadata = pathMixin.getMetadata();
}
U rv;
// the inits for the subtype will be wider, if it's a variable path
if (inits != null && pathMixin.getMetadata().getPathType() != PathType.VARIABLE) {
rv = clazz.getConstructor(PathMetadata.class, PathInits.class).newInstance(metadata, inits);
} else {
rv = clazz.getConstructor(PathMetadata.class).newInstance(metadata);
}
casts.put(clazz, rv);
return rv;
} else {
return (U) casts.get(clazz);
}
} catch (InstantiationException e) {
throw new ExpressionException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new ExpressionException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new ExpressionException(e.getMessage(), e);
} catch (NoSuchMethodException e) {
throw new ExpressionException(e.getMessage(), e);
}
}
/**
* Template method for tracking child path creation
*
* @param <P>
* @param path path to be tracked
* @return path
*/
protected <P extends Path<?>> P add(P path) {
return path;
}
/**
* Create a new array path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
protected <A, E> ArrayPath<A, E> createArray(String property, Class<? super A> type) {
return add(new ArrayPath<A, E>(type, forProperty(property)));
}
/**
* Create a new Boolean path
*
* @param property property name
* @return property path
*/
protected BooleanPath createBoolean(String property) {
return add(new BooleanPath(forProperty(property)));
}
/**
* Create a new Collection typed path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A, Q extends SimpleExpression<? super A>> CollectionPath<A, Q> createCollection(String property, Class<? super A> type, Class<? super Q> queryType, PathInits inits) {
return add(new CollectionPath<A, Q>(type, (Class) queryType, forProperty(property), inits));
}
/**
* Create a new Comparable typed path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A extends Comparable> ComparablePath<A> createComparable(String property, Class<? super A> type) {
return add(new ComparablePath<A>((Class) type, forProperty(property)));
}
/**
* Create a new Enum path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
protected <A extends Enum<A>> EnumPath<A> createEnum(String property, Class<A> type) {
return add(new EnumPath<A>(type, forProperty(property)));
}
/**
* Create a new Date path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A extends Comparable> DatePath<A> createDate(String property, Class<? super A> type) {
return add(new DatePath<A>((Class) type, forProperty(property)));
}
/**
* Create a new DateTime path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A extends Comparable> DateTimePath<A> createDateTime(String property, Class<? super A> type) {
return add(new DateTimePath<A>((Class) type, forProperty(property)));
}
/**
* Create a new List typed path
*
* @param <A>
* @param <E>
* @param property property name
* @param type property type
* @param queryType expression type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A, E extends SimpleExpression<? super A>> ListPath<A, E> createList(String property, Class<? super A> type, Class<? super E> queryType, PathInits inits) {
return add(new ListPath<A, E>(type, (Class) queryType, forProperty(property), inits));
}
/**
* Create a new Map typed path
*
* @param <K>
* @param <V>
* @param <E>
* @param property property name
* @param key key type
* @param value value type
* @param queryType expression type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <K, V, E extends SimpleExpression<? super V>> MapPath<K, V, E> createMap(String property, Class<? super K> key, Class<? super V> value, Class<? super E> queryType) {
return add(new MapPath<K, V, E>(key, value, (Class) queryType, forProperty(property)));
}
/**
* Create a new Number path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A extends Number & Comparable<?>> NumberPath<A> createNumber(String property, Class<? super A> type) {
return add(new NumberPath<A>((Class) type, forProperty(property)));
}
/**
* Create a new Set typed path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A, E extends SimpleExpression<? super A>> SetPath<A, E> createSet(String property, Class<? super A> type, Class<? super E> queryType, PathInits inits) {
return add(new SetPath<A, E>(type, (Class) queryType, forProperty(property), inits));
}
/**
* Create a new Simple path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A> SimplePath<A> createSimple(String property, Class<? super A> type) {
return add(new SimplePath<A>((Class<A>) type, forProperty(property)));
}
/**
* Create a new String path
*
* @param property property name
* @return property path
*/
protected StringPath createString(String property) {
return add(new StringPath(forProperty(property)));
}
/**
* Create a new Time path
*
* @param <A>
* @param property property name
* @param type property type
* @return property path
*/
@SuppressWarnings("unchecked")
protected <A extends Comparable> TimePath<A> createTime(String property, Class<? super A> type) {
return add(new TimePath<A>((Class) type, forProperty(property)));
}
protected PathMetadata forProperty(String property) {
return PathMetadataFactory.forProperty(this, property);
}
@Override
public PathMetadata getMetadata() {
return pathMixin.getMetadata();
}
@Override
public Path<?> getRoot() {
return pathMixin.getRoot();
}
/**
* Create an {@code this instanceOf type} expression
*
* @param <B>
* @param type rhs of the expression
* @return instanceof expression
*/
public <B extends T> BooleanExpression instanceOf(Class<B> type) {
return Expressions.booleanOperation(Ops.INSTANCE_OF, pathMixin, ConstantImpl.create(type));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public BooleanExpression instanceOfAny(Class... types) {
BooleanExpression[] exprs = new BooleanExpression[types.length];
for (int i = 0; i < types.length; i++) {
exprs[i] = this.instanceOf(types[i]);
}
return Expressions.anyOf(exprs);
}
@Override
public AnnotatedElement getAnnotatedElement() {
return pathMixin.getAnnotatedElement();
}
}