blob: 45501ee0b4e72eca5a0c135445571171b504cdd4 [file] [log] [blame]
/*
* Copyright 2007-2011 Rickard Öberg.
* Copyright 2007-2010 Niclas Hedhman.
* Copyright 2008 Alin Dreghiciu.
* Copyright 2012 Stanislav Muhametsin.
* Copyright 2012-2014 Paul Merlin.
*
* 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
* ied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.qi4j.api.query;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import org.qi4j.api.association.Association;
import org.qi4j.api.association.GenericAssociationInfo;
import org.qi4j.api.association.ManyAssociation;
import org.qi4j.api.association.NamedAssociation;
import org.qi4j.api.composite.Composite;
import org.qi4j.api.entity.Identity;
import org.qi4j.api.injection.scope.State;
import org.qi4j.api.property.GenericPropertyInfo;
import org.qi4j.api.property.Property;
import org.qi4j.api.query.grammar.AndSpecification;
import org.qi4j.api.query.grammar.AssociationFunction;
import org.qi4j.api.query.grammar.AssociationNotNullSpecification;
import org.qi4j.api.query.grammar.AssociationNullSpecification;
import org.qi4j.api.query.grammar.ContainsAllSpecification;
import org.qi4j.api.query.grammar.ContainsSpecification;
import org.qi4j.api.query.grammar.EqSpecification;
import org.qi4j.api.query.grammar.GeSpecification;
import org.qi4j.api.query.grammar.GtSpecification;
import org.qi4j.api.query.grammar.LeSpecification;
import org.qi4j.api.query.grammar.LtSpecification;
import org.qi4j.api.query.grammar.ManyAssociationContainsSpecification;
import org.qi4j.api.query.grammar.ManyAssociationFunction;
import org.qi4j.api.query.grammar.MatchesSpecification;
import org.qi4j.api.query.grammar.NamedAssociationContainsNameSpecification;
import org.qi4j.api.query.grammar.NamedAssociationContainsSpecification;
import org.qi4j.api.query.grammar.NamedAssociationFunction;
import org.qi4j.api.query.grammar.NeSpecification;
import org.qi4j.api.query.grammar.NotSpecification;
import org.qi4j.api.query.grammar.OrSpecification;
import org.qi4j.api.query.grammar.OrderBy;
import org.qi4j.api.query.grammar.PropertyFunction;
import org.qi4j.api.query.grammar.PropertyNotNullSpecification;
import org.qi4j.api.query.grammar.PropertyNullSpecification;
import org.qi4j.api.query.grammar.PropertyReference;
import org.qi4j.api.query.grammar.Variable;
import org.qi4j.api.util.NullArgumentException;
import org.qi4j.functional.Specification;
import static org.qi4j.functional.Iterables.first;
import static org.qi4j.functional.Iterables.prepend;
/**
* Static factory methods for query expressions and operators.
*/
public final class QueryExpressions
{
// This is used for eq(Association,Composite)
private static final Method IDENTITY_METHOD;
static
{
try
{
IDENTITY_METHOD = Identity.class.getMethod( "identity" );
}
catch( NoSuchMethodException e )
{
throw new InternalError( "Zest Core API codebase is corrupted. Contact Zest team: QueryExpressions" );
}
}
// Templates and variables -----------------------------------------------|
/**
* Create a Query Template using the given type.
*
* @param <T> the type of the template
* @param clazz a class declaring the type of the template
*
* @return a new Query Template
*/
public static <T> T templateFor( Class<T> clazz )
{
NullArgumentException.validateNotNull( "Template class", clazz );
if( clazz.isInterface() )
{
return clazz.cast( Proxy.newProxyInstance( clazz.getClassLoader(),
array( clazz ),
new TemplateHandler<T>( null, null, null, null ) ) );
}
else
{
try
{
T mixin = clazz.newInstance();
for( Field field : clazz.getFields() )
{
if( field.getAnnotation( State.class ) != null )
{
if( field.getType().equals( Property.class ) )
{
field.set( mixin,
Proxy.newProxyInstance( field.getType().getClassLoader(),
array( field.getType() ),
new PropertyReferenceHandler<>( new PropertyFunction<T>( null, null, null, null, field ) ) ) );
}
else if( field.getType().equals( Association.class ) )
{
field.set( mixin,
Proxy.newProxyInstance( field.getType().getClassLoader(),
array( field.getType() ),
new AssociationReferenceHandler<>( new AssociationFunction<T>( null, null, null, field ) ) ) );
}
else if( field.getType().equals( ManyAssociation.class ) )
{
field.set( mixin,
Proxy.newProxyInstance( field.getType().getClassLoader(),
array( field.getType() ),
new ManyAssociationReferenceHandler<>( new ManyAssociationFunction<T>( null, null, null, field ) ) ) );
}
else if( field.getType().equals( NamedAssociation.class ) )
{
field.set( mixin,
Proxy.newProxyInstance( field.getType().getClassLoader(),
array( field.getType() ),
new NamedAssociationReferenceHandler<>( new NamedAssociationFunction<T>( null, null, null, field ) ) ) );
}
}
}
return mixin;
}
catch( IllegalAccessException | IllegalArgumentException | InstantiationException | SecurityException e )
{
throw new IllegalArgumentException( "Cannot use class as template", e );
}
}
}
/**
* Create a Query Template using the given mixin class and association.
*
* @param <T> the type of the template
* @param mixinType a class declaring the type of the template
* @param association an association
*
* @return a new Query Template
*/
public static <T> T templateFor( final Class<T> mixinType, Association<?> association )
{
NullArgumentException.validateNotNull( "Mixin class", mixinType );
NullArgumentException.validateNotNull( "Association", association );
return mixinType.cast( Proxy.newProxyInstance( mixinType.getClassLoader(),
array( mixinType ),
new TemplateHandler<T>( null,
association( association ),
null,
null ) ) );
}
public static <T> T oneOf( final ManyAssociation<T> association )
{
NullArgumentException.validateNotNull( "Association", association );
return association.get( 0 );
}
public static <T> T oneOf( final NamedAssociation<T> association )
{
NullArgumentException.validateNotNull( "Association", association );
return association.get( first( association ) );
}
/**
* Create a new Query Variable.
*
* @param name a name for the Variable
*
* @return a new Query Variable.
*/
public static Variable variable( String name )
{
NullArgumentException.validateNotNull( "Variable name", name );
return new Variable( name );
}
/**
* Create a new Query Template PropertyFunction.
*
* @param <T> type of the Property
* @param property a Property
*
* @return a new Query Template PropertyFunction
*/
@SuppressWarnings( "unchecked" )
public static <T> PropertyFunction<T> property( Property<T> property )
{
return ( (PropertyReferenceHandler<T>) Proxy.getInvocationHandler( property ) ).property();
}
/**
* Create a new Query Property instance.
*
* @param <T> type of the Property
* @param mixinClass mixin of the Property
* @param fieldName name of the Property field
*
* @return a new Query Property instance for the given mixin and property name.
*/
@SuppressWarnings( "unchecked" )
public static <T> Property<T> property( Class<?> mixinClass, String fieldName )
{
try
{
Field field = mixinClass.getField( fieldName );
if( !Property.class.isAssignableFrom( field.getType() ) )
{
throw new IllegalArgumentException( "Field must be of type Property<?>" );
}
return (Property<T>) Proxy.newProxyInstance(
mixinClass.getClassLoader(),
array( field.getType() ),
new PropertyReferenceHandler<>( new PropertyFunction<T>( null, null, null, null, field ) ) );
}
catch( NoSuchFieldException e )
{
throw new IllegalArgumentException( "No such field '" + fieldName + "' in mixin " + mixinClass.getName() );
}
}
/**
* Create a new Query Template AssociationFunction.
*
* @param <T> type of the Association
* @param association an Association
*
* @return a new Query Template AssociationFunction
*/
@SuppressWarnings( "unchecked" )
public static <T> AssociationFunction<T> association( Association<T> association )
{
return ( (AssociationReferenceHandler<T>) Proxy.getInvocationHandler( association ) ).association();
}
/**
* Create a new Query Template ManyAssociationFunction.
*
* @param <T> type of the ManyAssociation
* @param association a ManyAssociation
*
* @return a new Query Template ManyAssociationFunction
*/
@SuppressWarnings( "unchecked" )
public static <T> ManyAssociationFunction<T> manyAssociation( ManyAssociation<T> association )
{
return ( (ManyAssociationReferenceHandler<T>) Proxy.getInvocationHandler( association ) ).manyAssociation();
}
/**
* Create a new Query Template NamedAssociationFunction.
*
* @param <T> type of the NamedAssociation
* @param association a NamedAssociation
*
* @return a new Query Template NamedAssociationFunction
*/
@SuppressWarnings( "unchecked" )
public static <T> NamedAssociationFunction<T> namedAssociation( NamedAssociation<T> association )
{
return ( (NamedAssociationReferenceHandler<T>) Proxy.getInvocationHandler( association ) ).namedAssociation();
}
// And/Or/Not ------------------------------------------------------------|
/**
* Create a new AND specification.
*
* @param left first operand
* @param right second operand
* @param optionalRight optional operands
*
* @return a new AND specification
*/
@SafeVarargs
public static AndSpecification and( Specification<Composite> left,
Specification<Composite> right,
Specification<Composite>... optionalRight
)
{
return new AndSpecification( prepend( left, prepend( right, Arrays.asList( optionalRight ) ) ) );
}
/**
* Create a new OR specification.
*
* @param specs operands
*
* @return a new OR specification
*/
@SafeVarargs
public static OrSpecification or( Specification<Composite>... specs )
{
return new OrSpecification( Arrays.asList( specs ) );
}
/**
* Create a new NOT specification.
*
* @param operand specification to be negated
*
* @return a new NOT specification
*/
public static NotSpecification not( Specification<Composite> operand )
{
return new NotSpecification( operand );
}
// Comparisons -----------------------------------------------------------|
/**
* Create a new EQUALS specification for a Property.
*
* @param property a Property
* @param value its value
*
* @return a new EQUALS specification for a Property.
*/
public static <T> EqSpecification<T> eq( Property<T> property, T value )
{
return new EqSpecification<>( property( property ), value );
}
/**
* Create a new EQUALS specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new EQUALS specification for a Property using a named Variable.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> EqSpecification<T> eq( Property<T> property, Variable variable )
{
return new EqSpecification( property( property ), variable );
}
/**
* Create a new EQUALS specification for an Association.
*
* @param association an Association
* @param value its value
*
* @return a new EQUALS specification for an Association.
*/
public static <T> EqSpecification<String> eq( Association<T> association, T value )
{
return new EqSpecification<>( new PropertyFunction<String>( null,
association( association ),
null,
null,
IDENTITY_METHOD ),
value.toString() );
}
/**
* Create a new GREATER OR EQUALS specification for a Property.
*
* @param property a Property
* @param value its value
*
* @return a new GREATER OR EQUALS specification for a Property.
*/
public static <T> GeSpecification<T> ge( Property<T> property, T value )
{
return new GeSpecification<>( property( property ), value );
}
/**
* Create a new GREATER OR EQUALS specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new GREATER OR EQUALS specification for a Property using a named Variable.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> GeSpecification<T> ge( Property<T> property, Variable variable )
{
return new GeSpecification( property( property ), variable );
}
/**
* Create a new GREATER THAN specification for a Property.
*
* @param property a Property
* @param value its value
*
* @return a new GREATER THAN specification for a Property.
*/
public static <T> GtSpecification<T> gt( Property<T> property, T value )
{
return new GtSpecification<>( property( property ), value );
}
/**
* Create a new GREATER THAN specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new GREATER THAN specification for a Property using a named Variable.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> GtSpecification<T> gt( Property<T> property, Variable variable )
{
return new GtSpecification( property( property ), variable );
}
/**
* Create a new LESS OR EQUALS specification for a Property.
*
* @param property a Property
* @param value its value
*
* @return a new LESS OR EQUALS specification for a Property.
*/
public static <T> LeSpecification<T> le( Property<T> property, T value )
{
return new LeSpecification<>( property( property ), value );
}
/**
* Create a new LESS OR EQUALS specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new LESS OR EQUALS specification for a Property using a named Variable.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> LeSpecification<T> le( Property<T> property, Variable variable )
{
return new LeSpecification( property( property ), variable );
}
/**
* Create a new LESSER THAN specification for a Property.
*
* @param property a Property
* @param value its value
*
* @return a new LESSER THAN specification for a Property.
*/
public static <T> LtSpecification<T> lt( Property<T> property, T value )
{
return new LtSpecification<>( property( property ), value );
}
/**
* Create a new LESSER THAN specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new LESSER THAN specification for a Property using a named Variable.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> LtSpecification<T> lt( Property<T> property, Variable variable )
{
return new LtSpecification( property( property ), variable );
}
/**
* Create a new NOT EQUALS specification for a Property.
*
* @param property a Property
* @param value its value
*
* @return a new NOT EQUALS specification for a Property.
*/
public static <T> NeSpecification<T> ne( Property<T> property, T value )
{
return new NeSpecification<>( property( property ), value );
}
/**
* Create a new NOT EQUALS specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new NOT EQUALS specification for a Property using a named Variable.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> NeSpecification<T> ne( Property<T> property, Variable variable )
{
return new NeSpecification( property( property ), variable );
}
/**
* Create a new REGULAR EXPRESSION specification for a Property.
*
* @param property a Property
* @param regexp its value
*
* @return a new REGULAR EXPRESSION specification for a Property.
*/
public static MatchesSpecification matches( Property<String> property, String regexp )
{
return new MatchesSpecification( property( property ), regexp );
}
/**
* Create a new REGULAR EXPRESSION specification for a Property using a named Variable.
*
* @param property a Property
* @param variable a Query Variable
*
* @return a new REGULAR EXPRESSION specification for a Property using a named Variable.
*/
public static MatchesSpecification matches( Property<String> property, Variable variable )
{
return new MatchesSpecification( property( property ), variable );
}
// Null checks -----------------------------------------------------------|
/**
* Create a new NOT NULL specification for a Property.
*
* @param property a Property
*
* @return a new NOT NULL specification for a Property.
*/
public static <T> PropertyNotNullSpecification<T> isNotNull( Property<T> property )
{
return new PropertyNotNullSpecification<>( property( property ) );
}
/**
* Create a new NULL specification for a Property.
*
* @param property a Property
*
* @return a new NULL specification for a Property.
*/
public static <T> PropertyNullSpecification<T> isNull( Property<T> property )
{
return new PropertyNullSpecification<>( property( property ) );
}
/**
* Create a new NOT NULL specification for an Association.
*
* @param association an Association
*
* @return a new NOT NULL specification for an Association.
*/
public static <T> AssociationNotNullSpecification<T> isNotNull( Association<T> association )
{
return new AssociationNotNullSpecification<>( association( association ) );
}
/**
* Create a new NULL specification for an Association.
*
* @param association an Association
*
* @return a new NULL specification for an Association.
*/
public static <T> AssociationNullSpecification<T> isNull( Association<T> association )
{
return new AssociationNullSpecification<>( association( association ) );
}
// Collections -----------------------------------------------------------|
/**
* Create a new CONTAINS ALL specification for a Collection Property.
*
* @param collectionProperty a Collection Property
* @param values its values
*
* @return a new CONTAINS ALL specification for a Collection Property.
*/
public static <T> ContainsAllSpecification<T> containsAll( Property<? extends Collection<T>> collectionProperty,
Iterable<T> values )
{
NullArgumentException.validateNotNull( "Values", values );
return new ContainsAllSpecification<>( property( collectionProperty ), values );
}
/**
* Create a new CONTAINS ALL specification for a Collection Property using named Variables.
*
* @param collectionProperty a Collection Property
* @param variables named Variables
*
* @return a new CONTAINS ALL specification for a Collection Property using named Variables.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> ContainsAllSpecification<T> containsAllVariables(
Property<? extends Collection<T>> collectionProperty,
Iterable<Variable> variables )
{
NullArgumentException.validateNotNull( "Variables", variables );
return new ContainsAllSpecification( property( collectionProperty ), variables );
}
/**
* Create a new CONTAINS specification for a Collection Property.
*
* @param collectionProperty a Collection Property
* @param value the value
*
* @return a new CONTAINS specification for a Collection Property.
*/
public static <T> ContainsSpecification<T> contains( Property<? extends Collection<T>> collectionProperty,
T value )
{
NullArgumentException.validateNotNull( "Value", value );
return new ContainsSpecification<>( property( collectionProperty ), value );
}
/**
* Create a new CONTAINS specification for a Collection Property using named Variables.
*
* @param collectionProperty a Collection Property
* @param variable named Variable
*
* @return a new CONTAINS specification for a Collection Property using named Variables.
*/
@SuppressWarnings( {"raw", "unchecked"} )
public static <T> ContainsSpecification<T> contains( Property<? extends Collection<T>> collectionProperty,
Variable variable )
{
NullArgumentException.validateNotNull( "Variable", variable );
return new ContainsSpecification( property( collectionProperty ), variable );
}
/**
* Create a new CONTAINS specification for a ManyAssociation.
*
* @param manyAssoc a ManyAssociation
* @param value the value
*
* @return a new CONTAINS specification for a ManyAssociation.
*/
public static <T> ManyAssociationContainsSpecification<T> contains( ManyAssociation<T> manyAssoc, T value )
{
return new ManyAssociationContainsSpecification<>( manyAssociation( manyAssoc ), value );
}
/**
* Create a new CONTAINS specification for a NamedAssociation.
*
* @param namedAssoc a NamedAssociation
* @param value the value
*
* @return a new CONTAINS specification for a NamedAssociation.
*/
public static <T> NamedAssociationContainsSpecification<T> contains( NamedAssociation<T> namedAssoc, T value )
{
return new NamedAssociationContainsSpecification<>( namedAssociation( namedAssoc ), value );
}
/**
* Create a new CONTAINS NAME specification for a NamedAssociation.
*
* @param namedAssoc a NamedAssociation
* @param name the name
*
* @return a new CONTAINS NAME specification for a NamedAssociation.
*/
public static <T> NamedAssociationContainsNameSpecification<T> containsName( NamedAssociation<T> namedAssoc,
String name )
{
return new NamedAssociationContainsNameSpecification<>( namedAssociation( namedAssoc ), name );
}
// Ordering --------------------------------------------------------------|
/**
* Create a new Query ascending order segment for a Property.
*
* @param <T> type of the Property
* @param property a Property
*
* @return a new Query ascending order segment for a Property.
*/
public static <T> OrderBy orderBy( final Property<T> property )
{
return orderBy( property, OrderBy.Order.ASCENDING );
}
/**
* Create a new Query ordering segment for a Property.
*
* @param <T> type of the Property
* @param property a Property
* @param order ascending or descending
*
* @return a new Query ordering segment for a Property.
*/
public static <T> OrderBy orderBy( final Property<T> property, final OrderBy.Order order )
{
return new OrderBy( property( property ), order );
}
// Query Templates InvocationHandlers ------------------------------------|
private static class TemplateHandler<T>
implements InvocationHandler
{
private final PropertyFunction<?> compositeProperty;
private final AssociationFunction<?> compositeAssociation;
private final ManyAssociationFunction<?> compositeManyAssociation;
private final NamedAssociationFunction<?> compositeNamedAssociation;
private TemplateHandler( PropertyFunction<?> compositeProperty,
AssociationFunction<?> compositeAssociation,
ManyAssociationFunction<?> compositeManyAssociation,
NamedAssociationFunction<?> compositeNamedAssociation
)
{
this.compositeProperty = compositeProperty;
this.compositeAssociation = compositeAssociation;
this.compositeManyAssociation = compositeManyAssociation;
this.compositeNamedAssociation = compositeNamedAssociation;
}
@Override
public Object invoke( Object o, Method method, Object[] objects )
throws Throwable
{
if( Property.class.isAssignableFrom( method.getReturnType() ) )
{
return Proxy.newProxyInstance(
method.getReturnType().getClassLoader(),
array( method.getReturnType() ),
new PropertyReferenceHandler<>( new PropertyFunction<T>( compositeProperty,
compositeAssociation,
compositeManyAssociation,
compositeNamedAssociation,
method ) ) );
}
else if( Association.class.isAssignableFrom( method.getReturnType() ) )
{
return Proxy.newProxyInstance(
method.getReturnType().getClassLoader(),
array( method.getReturnType() ),
new AssociationReferenceHandler<>( new AssociationFunction<T>( compositeAssociation,
compositeManyAssociation,
compositeNamedAssociation,
method ) ) );
}
else if( ManyAssociation.class.isAssignableFrom( method.getReturnType() ) )
{
return Proxy.newProxyInstance(
method.getReturnType().getClassLoader(),
array( method.getReturnType() ),
new ManyAssociationReferenceHandler<>( new ManyAssociationFunction<T>( compositeAssociation,
compositeManyAssociation,
compositeNamedAssociation,
method ) ) );
}
else if( NamedAssociation.class.isAssignableFrom( method.getReturnType() ) )
{
return Proxy.newProxyInstance(
method.getReturnType().getClassLoader(),
array( method.getReturnType() ),
new NamedAssociationReferenceHandler<>( new NamedAssociationFunction<T>( compositeAssociation,
compositeManyAssociation,
compositeNamedAssociation,
method ) ) );
}
return null;
}
}
private static class PropertyReferenceHandler<T>
implements InvocationHandler
{
private final PropertyFunction<T> property;
private PropertyReferenceHandler( PropertyFunction<T> property )
{
this.property = property;
}
private PropertyFunction<T> property()
{
return property;
}
@Override
public Object invoke( Object o, final Method method, Object[] objects )
throws Throwable
{
if( method.equals( Property.class.getMethod( "get" ) ) )
{
Type propertyType = GenericPropertyInfo.propertyTypeOf( property.accessor() );
if( propertyType.getClass().equals( Class.class ) )
{
return Proxy.newProxyInstance( method.getDeclaringClass().getClassLoader(),
array( (Class<?>) propertyType, PropertyReference.class ),
new TemplateHandler<T>( property, null, null, null ) );
}
}
return null;
}
}
private static class AssociationReferenceHandler<T>
implements InvocationHandler
{
private final AssociationFunction<T> association;
private AssociationReferenceHandler( AssociationFunction<T> association )
{
this.association = association;
}
private AssociationFunction<T> association()
{
return association;
}
@Override
public Object invoke( Object o, final Method method, Object[] objects )
throws Throwable
{
if( method.equals( Association.class.getMethod( "get" ) ) )
{
Type associationType = GenericAssociationInfo.associationTypeOf( association.accessor() );
if( associationType.getClass().equals( Class.class ) )
{
return Proxy.newProxyInstance( method.getDeclaringClass().getClassLoader(),
array( (Class) associationType, PropertyReference.class ),
new TemplateHandler<T>( null, association, null, null ) );
}
}
return null;
}
}
private static class ManyAssociationReferenceHandler<T>
implements InvocationHandler
{
private final ManyAssociationFunction<T> manyAssociation;
private ManyAssociationReferenceHandler( ManyAssociationFunction<T> manyAssociation )
{
this.manyAssociation = manyAssociation;
}
public ManyAssociationFunction<T> manyAssociation()
{
return manyAssociation;
}
@Override
public Object invoke( Object o, final Method method, Object[] objects )
throws Throwable
{
if( method.equals( ManyAssociation.class.getMethod( "get", Integer.TYPE ) ) )
{
Type manyAssociationType = GenericAssociationInfo.associationTypeOf( manyAssociation.accessor() );
if( manyAssociationType.getClass().equals( Class.class ) )
{
return Proxy.newProxyInstance( method.getDeclaringClass().getClassLoader(),
array( (Class) manyAssociationType, PropertyReference.class ),
new TemplateHandler<T>( null, null, manyAssociation, null ) );
}
}
return null;
}
}
private static class NamedAssociationReferenceHandler<T>
implements InvocationHandler
{
private final NamedAssociationFunction<T> namedAssociation;
private NamedAssociationReferenceHandler( NamedAssociationFunction<T> namedAssociation )
{
this.namedAssociation = namedAssociation;
}
public NamedAssociationFunction<T> namedAssociation()
{
return namedAssociation;
}
@Override
public Object invoke( Object o, final Method method, Object[] objects )
throws Throwable
{
if( method.equals( NamedAssociation.class.getMethod( "get", String.class ) ) )
{
Type namedAssociationType = GenericAssociationInfo.associationTypeOf( namedAssociation.accessor() );
if( namedAssociationType.getClass().equals( Class.class ) )
{
return Proxy.newProxyInstance( method.getDeclaringClass().getClassLoader(),
array( (Class) namedAssociationType, PropertyReference.class ),
new TemplateHandler<T>( null, null, null, namedAssociation ) );
}
}
return null;
}
}
@SafeVarargs
private static <T> T[] array( T... array )
{
return array;
}
private QueryExpressions()
{
}
}