com.mysema.query.alias.AliasFactory Maven / Gradle / Ivy
/*
* Copyright 2011, Mysema Ltd
*
* 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.mysema.query.alias;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mysema.commons.lang.Pair;
import com.mysema.query.QueryException;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.Expression;
import com.mysema.query.types.PathMetadataFactory;
/**
* AliasFactory is a factory class for alias creation
*
* @author tiwe
*/
public class AliasFactory {
private final ThreadLocal> current = new ThreadLocal>();
private final PathFactory pathFactory;
private final TypeSystem typeSystem;
// caches top level paths (class/var as key)
private final LoadingCache,String>, EntityPath>> pathCache;
private final LoadingCache,Expression>>, ManagedObject> proxyCache =
CacheBuilder.newBuilder().build(
new CacheLoader,Expression>>,ManagedObject>() {
@Override
public ManagedObject load(Pair, Expression>> input) {
return (ManagedObject) createProxy(input.getFirst(), input.getSecond());
}
});
public AliasFactory(final PathFactory pathFactory, TypeSystem typeSystem) {
this.pathFactory = pathFactory;
this.typeSystem = typeSystem;
this.pathCache = CacheBuilder.newBuilder().build(
new CacheLoader, String>, EntityPath>>() {
@Override
public EntityPath> load( Pair, String> input) {
return (EntityPath>)pathFactory.createEntityPath(
input.getFirst(),
PathMetadataFactory.forVariable(input.getSecond()));
}
});
}
/**
* Create an alias instance for the given class and Expression
*
* @param
* @param cl
* @param expr
* @return
*/
@SuppressWarnings("unchecked")
public A createAliasForExpr(Class cl, Expression extends A> expr) {
try {
return (A) proxyCache.get(Pair., Expression>>of(cl, expr));
} catch (ExecutionException e) {
throw new QueryException(e);
}
}
/**
* Create an alias instance for the given class, parent and path
*
* @param
* @param cl
* @param parent
* @param path
* @return
*/
public A createAliasForProperty(Class cl, Object parent, Expression> path) {
return createProxy(cl, path);
}
/**
* Create an alias instance for the given class and variable name
*
* @param
* @param cl
* @param var
* @return
*/
@SuppressWarnings("unchecked")
public A createAliasForVariable(Class cl, String var) {
try {
Expression> path = pathCache.get(Pair., String>of(cl, var));
return (A) proxyCache.get(Pair., Expression>>of(cl, path));
} catch (ExecutionException e) {
throw new QueryException(e);
}
}
/**
* Create a proxy instance for the given class and path
*
* @param
* @param cl
* @param path
* @return
*/
@SuppressWarnings("unchecked")
protected A createProxy(Class cl, Expression> path) {
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(AliasFactory.class.getClassLoader());
if (cl.isInterface()) {
enhancer.setInterfaces(new Class[] { cl, ManagedObject.class });
} else {
enhancer.setSuperclass(cl);
enhancer.setInterfaces(new Class[] { ManagedObject.class });
}
// creates one handler per proxy
MethodInterceptor handler = new PropertyAccessInvocationHandler(path, this, pathFactory, typeSystem);
enhancer.setCallback(handler);
return (A) enhancer.create();
}
/**
* Get the current thread bound expression without reseting it
*
* @param
* @return
*/
@SuppressWarnings("unchecked")
@Nullable
public > A getCurrent() {
return (A) current.get();
}
/**
* Get the current thread bound expression and reset it
*
* @param
* @return
*/
@Nullable
public > A getCurrentAndReset() {
A rv = this. getCurrent();
reset();
return rv;
}
/**
* Reset the thread bound expression to null
*/
public void reset() {
current.set(null);
}
/**
* Set the thread bound expression to the given value
*
* @param expr
*/
public void setCurrent(Expression> expr) {
current.set(expr);
}
}