| /* |
| * 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.apache.zest.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.apache.zest.api.association.Association; |
| import org.apache.zest.api.association.GenericAssociationInfo; |
| import org.apache.zest.api.association.ManyAssociation; |
| import org.apache.zest.api.association.NamedAssociation; |
| import org.apache.zest.api.composite.Composite; |
| import org.apache.zest.api.entity.Identity; |
| import org.apache.zest.api.injection.scope.State; |
| import org.apache.zest.api.property.GenericPropertyInfo; |
| import org.apache.zest.api.property.Property; |
| import org.apache.zest.api.query.grammar.AndSpecification; |
| import org.apache.zest.api.query.grammar.AssociationFunction; |
| import org.apache.zest.api.query.grammar.AssociationNotNullSpecification; |
| import org.apache.zest.api.query.grammar.AssociationNullSpecification; |
| import org.apache.zest.api.query.grammar.ContainsAllSpecification; |
| import org.apache.zest.api.query.grammar.ContainsSpecification; |
| import org.apache.zest.api.query.grammar.EqSpecification; |
| import org.apache.zest.api.query.grammar.GeSpecification; |
| import org.apache.zest.api.query.grammar.GtSpecification; |
| import org.apache.zest.api.query.grammar.LeSpecification; |
| import org.apache.zest.api.query.grammar.LtSpecification; |
| import org.apache.zest.api.query.grammar.ManyAssociationContainsSpecification; |
| import org.apache.zest.api.query.grammar.ManyAssociationFunction; |
| import org.apache.zest.api.query.grammar.MatchesSpecification; |
| import org.apache.zest.api.query.grammar.NamedAssociationContainsNameSpecification; |
| import org.apache.zest.api.query.grammar.NamedAssociationContainsSpecification; |
| import org.apache.zest.api.query.grammar.NamedAssociationFunction; |
| import org.apache.zest.api.query.grammar.NeSpecification; |
| import org.apache.zest.api.query.grammar.NotSpecification; |
| import org.apache.zest.api.query.grammar.OrSpecification; |
| import org.apache.zest.api.query.grammar.OrderBy; |
| import org.apache.zest.api.query.grammar.PropertyFunction; |
| import org.apache.zest.api.query.grammar.PropertyNotNullSpecification; |
| import org.apache.zest.api.query.grammar.PropertyNullSpecification; |
| import org.apache.zest.api.query.grammar.PropertyReference; |
| import org.apache.zest.api.query.grammar.Variable; |
| import org.apache.zest.api.util.NullArgumentException; |
| import org.apache.zest.functional.Specification; |
| |
| import static org.apache.zest.functional.Iterables.first; |
| import static org.apache.zest.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() |
| { |
| } |
| } |