http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/InvalidPropertyTypeException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/InvalidPropertyTypeException.java b/core/api/src/main/java/org/qi4j/api/property/InvalidPropertyTypeException.java new file mode 100644 index 0000000..6afee4d --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/InvalidPropertyTypeException.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.qi4j.api.property; + +import java.lang.reflect.AccessibleObject; +import org.qi4j.api.common.ConstructionException; + +/** + * Thrown when attempting to subclass Property. + */ +public class InvalidPropertyTypeException extends ConstructionException +{ + public InvalidPropertyTypeException( AccessibleObject accessor ) + { + super( createMessage(accessor) ); + } + + private static String createMessage( AccessibleObject accessor ) + { + StringBuilder builder = new StringBuilder(); + builder.append( "Not allowed to subclass " + Property.class.getName() + ". Property accessor " + accessor + " is returning a Property subclass." ); + return builder.toString(); + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/Numbers.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/Numbers.java b/core/api/src/main/java/org/qi4j/api/property/Numbers.java new file mode 100644 index 0000000..00c0e5f --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/Numbers.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2009, Rickard Ãberg. All Rights Reserved. + * + * 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 org.qi4j.api.property; + +import java.math.BigDecimal; + +/** + * Convenience class for mathematical operations on numerical properties. + * <pre>import static org.qi4j.api.property.Numbers.*; + * ... + * add( object.numberProperty(), 5 );</pre> + */ +public final class Numbers +{ + // Integer operations + + public static Property<Integer> add( Property<Integer> property, int amount ) + { + property.set( property.get() + amount ); + return property; + } + + public static Property<Integer> mult( Property<Integer> property, int amount ) + { + property.set( property.get() * amount ); + return property; + } + + public static Property<Integer> sub( Property<Integer> property, int amount ) + { + property.set( property.get() - amount ); + return property; + } + + public static Property<Integer> div( Property<Integer> property, int amount ) + { + property.set( property.get() / amount ); + return property; + } + + // Long operations + + public static Property<Long> add( Property<Long> property, long amount ) + { + property.set( property.get() + amount ); + return property; + } + + public static Property<Long> mult( Property<Long> property, long amount ) + { + property.set( property.get() * amount ); + return property; + } + + public static Property<Long> sub( Property<Long> property, long amount ) + { + property.set( property.get() - amount ); + return property; + } + + public static Property<Long> div( Property<Long> property, long amount ) + { + property.set( property.get() / amount ); + return property; + } + + // Double operations + + public static Property<Double> add( Property<Double> property, double amount ) + { + property.set( property.get() + amount ); + return property; + } + + public static Property<Double> mult( Property<Double> property, double amount ) + { + property.set( property.get() * amount ); + return property; + } + + public static Property<Double> sub( Property<Double> property, double amount ) + { + property.set( property.get() - amount ); + return property; + } + + public static Property<Double> div( Property<Double> property, double amount ) + { + property.set( property.get() / amount ); + return property; + } + + // Float operations + + public static Property<Float> add( Property<Float> property, float amount ) + { + property.set( property.get() + amount ); + return property; + } + + public static Property<Float> mult( Property<Float> property, float amount ) + { + property.set( property.get() * amount ); + return property; + } + + public static Property<Float> sub( Property<Float> property, float amount ) + { + property.set( property.get() - amount ); + return property; + } + + public static Property<Float> div( Property<Float> property, float amount ) + { + property.set( property.get() / amount ); + return property; + } + + // BigDecimal operations + + public static Property<BigDecimal> add( Property<BigDecimal> property, BigDecimal amount ) + { + property.set( property.get().add( amount ) ); + return property; + } + + public static Property<BigDecimal> mult( Property<BigDecimal> property, BigDecimal amount ) + { + property.set( property.get().multiply( amount ) ); + return property; + } + + public static Property<BigDecimal> sub( Property<BigDecimal> property, BigDecimal amount ) + { + property.set( property.get().subtract( amount ) ); + return property; + } + + public static Property<BigDecimal> div( Property<BigDecimal> property, BigDecimal amount ) + { + property.set( property.get().divide( amount ) ); + return property; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/Property.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/Property.java b/core/api/src/main/java/org/qi4j/api/property/Property.java new file mode 100644 index 0000000..9c9cfa8 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/Property.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007-2011, Rickard Ãberg. All Rights Reserved. + * + * 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 org.qi4j.api.property; + +/** + * Properties are declared in Composite interfaces by using this interface. + * <p> + * It creates a first-class object for the property from which you can get and set the value, and access any + * metadata about it. + * </p> + * <p>The type of the Property can be one of the following:</p> + * <ul> + * <li> A boxed primitive (Long,Integer,Boolean, etc.)</li> + * <li> String</li> + * <li> BigInteger</li> + * <li> BigDecimal</li> + * <li> Date</li> + * <li> DateTime (Joda Time)</li> + * <li> LocalDateTime (Joda Time)</li> + * <li> A serializable</li> + * <li> A ValueComposite</li> + * <li> A List, Set or Collection of any of the above</li> + * </ul> + * + * @param <T> Parameterized type of the Property + */ +public interface Property<T> +{ + /** + * Get the value of the property. + * + * @return the value + */ + T get(); + + /** + * Set the value of the property + * + * @param newValue the new value + * + * @throws IllegalArgumentException if the value has an invalid value + * @throws IllegalStateException if the property is immutable + */ + void set( T newValue ) + throws IllegalArgumentException, IllegalStateException; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/PropertyDescriptor.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/PropertyDescriptor.java b/core/api/src/main/java/org/qi4j/api/property/PropertyDescriptor.java new file mode 100644 index 0000000..aa986b1 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/PropertyDescriptor.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * + * 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 org.qi4j.api.property; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Type; +import org.qi4j.api.common.QualifiedName; +import org.qi4j.api.structure.MetaInfoHolder; +import org.qi4j.api.structure.Module; +import org.qi4j.api.type.ValueType; + +/** + * Property Descriptor. + */ +public interface PropertyDescriptor extends MetaInfoHolder +{ + boolean isImmutable(); + + /** + * Get the qualified name of the property which is equal to: + * <pre><code> + * <interface name>:<method name> + * </code></pre> + * + * @return the qualified name of the property + */ + QualifiedName qualifiedName(); + + /** + * Get the type of the property. If the property is declared + * as Property<X> then X is returned. + * + * @return the property type + */ + Type type(); + + AccessibleObject accessor(); + + Object initialValue( Module module ); + + ValueType valueType(); + + boolean queryable(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/PropertyMixin.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/PropertyMixin.java b/core/api/src/main/java/org/qi4j/api/property/PropertyMixin.java new file mode 100644 index 0000000..26f8bd5 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/PropertyMixin.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * + * 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 org.qi4j.api.property; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import org.qi4j.api.common.AppliesTo; +import org.qi4j.api.common.AppliesToFilter; +import org.qi4j.api.injection.scope.State; + +/** + * Generic mixin for properties. + */ +// START SNIPPET: actual +@AppliesTo( { PropertyMixin.PropertyFilter.class } ) +public final class PropertyMixin + implements InvocationHandler +{ + @State + private StateHolder state; + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + return state.propertyFor( method ); + } + + /** + * Filter Property methods to apply generic Property Mixin. + */ + public static class PropertyFilter + implements AppliesToFilter + { + @Override + public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> modifierClass ) + { + return Property.class.isAssignableFrom( method.getReturnType() ); + } + } +} +// END SNIPPET: actual http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/PropertyWrapper.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/PropertyWrapper.java b/core/api/src/main/java/org/qi4j/api/property/PropertyWrapper.java new file mode 100644 index 0000000..dc2259d --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/PropertyWrapper.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.qi4j.api.property; + +/** + * If you want to catch getting and setting properties, then create a GenericConcern + * that wraps the Zest-supplied Property instance with PropertyWrappers. Override + * get() and/or set() to perform your custom code. + */ +public class PropertyWrapper + implements Property<Object> +{ + protected Property<Object> next; + + public PropertyWrapper( Property<Object> next ) + { + this.next = next; + } + + public Property<Object> next() + { + return next; + } + + @Override + public Object get() + { + return next.get(); + } + + @Override + public void set( Object newValue ) + throws IllegalArgumentException, IllegalStateException + { + next.set( newValue ); + } + + @Override + public int hashCode() + { + return next.hashCode(); + } + + @Override + public boolean equals( Object obj ) + { + return next.equals( obj ); + } + + @Override + public String toString() + { + return next.toString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/StateHolder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/StateHolder.java b/core/api/src/main/java/org/qi4j/api/property/StateHolder.java new file mode 100644 index 0000000..dfd0b29 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/StateHolder.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. All Rights Reserved. + * + * 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 org.qi4j.api.property; + +import java.lang.reflect.AccessibleObject; + +/** + * This represents the state of a composite (properties). + */ +public interface StateHolder +{ + /** + * Get a property for a specific accessor + * + * @param accessor of the property + * + * @return the property + * + * @throws IllegalArgumentException if no property for given accessor exists + */ + <T> Property<T> propertyFor( AccessibleObject accessor ) + throws IllegalArgumentException; + + Iterable<? extends Property<?>> properties(); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/property/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/property/package.html b/core/api/src/main/java/org/qi4j/api/property/package.html new file mode 100644 index 0000000..8fc2a93 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/property/package.html @@ -0,0 +1,21 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +--> +<html> + <body> + <h2>Property API.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/MissingIndexingSystemException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/MissingIndexingSystemException.java b/core/api/src/main/java/org/qi4j/api/query/MissingIndexingSystemException.java new file mode 100644 index 0000000..6bfd216 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/MissingIndexingSystemException.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, Niclas Hedhman. All Rights Reserved. + * + * 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 org.qi4j.api.query; + +/** + * This Exception is thrown in <code>QueryBuilderFactory.newQueryBuilder()</code> method if + * no indexing subsystem has been declared in the assembly. + */ +public final class MissingIndexingSystemException + extends QueryException +{ + private static final long serialVersionUID = 5147421865890379209L; + + public MissingIndexingSystemException() + { + super( "No EntityFinder has been declared in the assembly of the application." ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/NotQueryableException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/NotQueryableException.java b/core/api/src/main/java/org/qi4j/api/query/NotQueryableException.java new file mode 100644 index 0000000..74737eb --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/NotQueryableException.java @@ -0,0 +1,88 @@ +/* + * Copyright 2009 Alin Dreghiciu. + * + * 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 org.qi4j.api.query; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import org.qi4j.api.entity.Queryable; +import org.qi4j.api.property.GenericPropertyInfo; +import org.qi4j.api.util.Classes; + +/** + * Thrown in case that a non queryable type or accessor (marked with @Queriable(false)) is used during query building, + * or when non-Property, non-Associations are trying to be queried (possibly can not happen). + */ +public class NotQueryableException + extends QueryException +{ + private static final long serialVersionUID = 1L; + + /** + * Constructor. + * + * @param message exception message + */ + public NotQueryableException( final String message ) + { + super( message ); + } + + /** + * Verify that the provided accessor method has not been marked with a Queryable(false). + * + * @param accessor accessor method + * + * @throws NotQueryableException - If accessor method has been marked as not queryable + */ + public static void throwIfNotQueryable( final AccessibleObject accessor ) + { + Queryable queryable = accessor.getAnnotation( Queryable.class ); + if( queryable != null && !queryable.value() ) + { + throw new NotQueryableException( + String.format( + "%1$s \"%2$s\" (%3$s) is not queryable as has been marked with @Queryable(false)", + Classes.RAW_CLASS.map( GenericPropertyInfo.propertyTypeOf( accessor ) ).getSimpleName(), + ( (Member) accessor ).getName(), + ( (Member) accessor ).getDeclaringClass().getName() + ) + ); + } + } + + /** + * Verify that the provided type has not been marked with a Queryable(false). + * + * @param type a type + * + * @throws NotQueryableException - If type has been marked as not queryable + */ + public static void throwIfNotQueryable( final Class<?> type ) + { + Queryable queryable = type.getAnnotation( Queryable.class ); + if( queryable != null && !queryable.value() ) + { + throw new NotQueryableException( + String.format( + "Type \"%1$s\" is not queryable as has been marked with @Queryable(false)", + type.getName() + ) + ); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/Query.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/Query.java b/core/api/src/main/java/org/qi4j/api/query/Query.java new file mode 100644 index 0000000..504b2e2 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/Query.java @@ -0,0 +1,138 @@ +/* + * Copyright 2007 Rickard Ãberg. + * Copyright 2007 Niclas Hedhman. + * Copyright 2008 Alin Dreghiciu. + * + * 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 org.qi4j.api.query; + +import java.io.Serializable; +import org.qi4j.api.property.Property; +import org.qi4j.api.query.grammar.OrderBy; + +/** + * This represents a Query in an indexing system. It is created from a + * {@link QueryBuilder}, which decides the "where" clause in the query. + * <p> + * Additional limitations, such as paging, ordering, and variables, can be set on + * a Query before it is executed by calling one of find(), iterator(), + * or count(). + * </p> + * <p> + * DDD tip: typically Queries are created in the Domain Model and passed to the UI, + * which sets the order and paging before executing it. + * </p> + */ +public interface Query<T> + extends Iterable<T>, Serializable +{ + /** + * Set the ordering rules. If many segments are used for ordering + * then they will be applied in order. + * + * @param segments the segments to order by + * + * @return the Query + */ + Query<T> orderBy( OrderBy... segments ); + + /** + * Append an ordering rule to the existing segments. + * + * @param property the property to order by + * @param order the order to apply + * + * @return the Query + */ + Query<T> orderBy( final Property<?> property, final OrderBy.Order order ); + + /** + * Append an ascending ordering rule to the existing segments. + * + * @param property the property to order by + * + * @return the Query + */ + Query<T> orderBy( Property<?> property ); + + /** + * Set the index of the first result. Default is 0 (zero). + * + * @param firstResult which index to use as the first one + * + * @return the Query + */ + Query<T> firstResult( int firstResult ); + + /** + * Set how many results should be returned. Default is that + * there is no limit set. + * + * @param maxResults that shouldbe returned + * + * @return the query + */ + Query<T> maxResults( int maxResults ); + + /** + * Get the first Entity that matches the criteria. This + * executes the Query. + * + * @return the first found Entity or null if none were found + * + * @throws QueryExecutionException if the query fails + */ + T find() + throws QueryExecutionException; + + /** + * Set the value of a named variable. + * + * @param name of the variable + * @param value of the variable + * + * @return the query + */ + Query<T> setVariable( String name, Object value ); + + /** + * Get the value of a named variable. + * + * @param name of the variable + * + * @return value of the variable + */ + <V> V getVariable( String name ); + + /** + * Get the result type of this Query + * + * @return the result type + */ + Class<T> resultType(); + + /** + * Count how many results would be returned by this Query. + * This executes the Query. + * + * @return result count + * + * @throws QueryExecutionException if the query fails + */ + long count() + throws QueryExecutionException; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/QueryBuilder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/QueryBuilder.java b/core/api/src/main/java/org/qi4j/api/query/QueryBuilder.java new file mode 100644 index 0000000..c07552c --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/QueryBuilder.java @@ -0,0 +1,56 @@ +/* + * Copyright 2007 Rickard Ãberg. + * Copyright 2008 Alin Dreghiciu. + * + * 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 org.qi4j.api.query; + +import org.qi4j.api.composite.Composite; +import org.qi4j.functional.Specification; + +/** + * QueryBuilders are used to create {@link Query} instances. + * Iteratively add where() clauses to the query, and then use + * {@link org.qi4j.api.unitofwork.UnitOfWork#newQuery(QueryBuilder)} to instantiate the Query. + * QueryBuilders are immutable, so when adding new where-clauses you get new instances. This + * + * DDD tip: Query objects are not executed immediately, so they + * should be constructed in the domain model and handed over to + * the UI, which can then further constrain it before actual + * execution. + */ +public interface QueryBuilder<T> +{ + /** + * Add a where-clause to the Query. Use {@link QueryExpressions} + * to create the expression. + * + * @param specification the where clause + * + * @return a new builder with the added where-clause + */ + QueryBuilder<T> where( Specification<Composite> specification ); + + /** + * Create a new query with the declared where-clauses that will be evaluated against the iterable entries. + * + * @param iterable collection of objects (composites?) + * + * @return a new Query instance + */ + Query<T> newQuery( Iterable<T> iterable ); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/QueryBuilderFactory.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/QueryBuilderFactory.java b/core/api/src/main/java/org/qi4j/api/query/QueryBuilderFactory.java new file mode 100644 index 0000000..8841030 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/QueryBuilderFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2007 Niclas Hedhman. + * + * 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 org.qi4j.api.query; + +/** + * This is used to create QueryBuilders. + * + * @see QueryBuilder + */ +public interface QueryBuilderFactory +{ + /** + * Create a new QueryBuilder. + * + * @param resultType the type of the result that you want + * + * @return a QueryBuilder + * + * @throws MissingIndexingSystemException if there is no EntityFinder service available + */ + <T> QueryBuilder<T> newQueryBuilder( Class<T> resultType ) + throws MissingIndexingSystemException; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/QueryException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/QueryException.java b/core/api/src/main/java/org/qi4j/api/query/QueryException.java new file mode 100644 index 0000000..6caca4c --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/QueryException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008, Niclas Hedhman. All Rights Reserved. + * + * 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 org.qi4j.api.query; + +/** + * Base class for Query exceptions. + */ +public abstract class QueryException + extends RuntimeException +{ + private static final long serialVersionUID = -3602596752342902060L; + + public QueryException() + { + } + + public QueryException( final String message ) + { + super( message ); + } + + public QueryException( final String message, final Throwable cause ) + { + super( message, cause ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/QueryExecutionException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/QueryExecutionException.java b/core/api/src/main/java/org/qi4j/api/query/QueryExecutionException.java new file mode 100644 index 0000000..f8cad23 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/QueryExecutionException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. All Rights Reserved. + * + * 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 org.qi4j.api.query; + +/** + * Throw this exception if a query could not be executed + */ +public final class QueryExecutionException + extends QueryException +{ + private static final long serialVersionUID = 5147421865890379209L; + + public QueryExecutionException( String message ) + { + super( message ); + } + + public QueryExecutionException( String message, Throwable cause ) + { + super( message, cause ); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/QueryExpressionException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/QueryExpressionException.java b/core/api/src/main/java/org/qi4j/api/query/QueryExpressionException.java new file mode 100644 index 0000000..f0a7f8e --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/QueryExpressionException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2009 Niclas Hedhman. + * + * 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 org.qi4j.api.query; + +/** + * Throw this exception if a QueryExpression is invalid. + */ +public class QueryExpressionException + extends QueryException +{ + + private static final long serialVersionUID = 1L; + + public QueryExpressionException( String message ) + { + super( message ); + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/QueryExpressions.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/QueryExpressions.java b/core/api/src/main/java/org/qi4j/api/query/QueryExpressions.java new file mode 100644 index 0000000..45501ee --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/QueryExpressions.java @@ -0,0 +1,944 @@ +/* + * 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() + { + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/grammar/AndSpecification.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/grammar/AndSpecification.java b/core/api/src/main/java/org/qi4j/api/query/grammar/AndSpecification.java new file mode 100644 index 0000000..138ad00 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/grammar/AndSpecification.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.qi4j.api.query.grammar; + +import org.qi4j.api.composite.Composite; +import org.qi4j.functional.Specification; +import org.qi4j.functional.Specifications; + +/** + * AND Specification. + */ +public class AndSpecification + extends BinarySpecification +{ + + public AndSpecification( Iterable<Specification<Composite>> operands ) + { + super( operands ); + } + + @Override + public boolean satisfiedBy( Composite item ) + { + return Specifications.and( operands ).satisfiedBy( item ); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder( "(" ); + String and = ""; + for( Specification<Composite> operand : operands ) + { + sb.append( and ).append( operand ); + and = " and "; + } + return sb.append( ")" ).toString(); + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationFunction.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationFunction.java b/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationFunction.java new file mode 100644 index 0000000..ee423a0 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationFunction.java @@ -0,0 +1,148 @@ +/* + * Copyright 2007-2011 Rickard Ãberg. + * Copyright 2007-2010 Niclas Hedhman. + * + * 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.grammar; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import org.qi4j.api.association.Association; +import org.qi4j.api.association.AssociationStateHolder; +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.composite.CompositeInstance; +import org.qi4j.api.query.QueryExpressionException; +import org.qi4j.api.util.Classes; +import org.qi4j.functional.Function; + +import static org.qi4j.api.util.Classes.typeOf; + +/** + * Function to get Entity Associations + */ +public class AssociationFunction<T> + implements Function<Composite, Association<T>> +{ + private final AssociationFunction<?> traversedAssociation; + private final ManyAssociationFunction<?> traversedManyAssociation; + private final NamedAssociationFunction<?> traversedNamedAssociation; + private final AccessibleObject accessor; + + public AssociationFunction( AssociationFunction<?> traversedAssociation, + ManyAssociationFunction<?> traversedManyAssociation, + NamedAssociationFunction<?> traversedNamedAssociation, + AccessibleObject accessor + ) + { + this.traversedAssociation = traversedAssociation; + this.traversedManyAssociation = traversedManyAssociation; + this.traversedNamedAssociation = traversedNamedAssociation; + this.accessor = accessor; + + Type returnType = typeOf( accessor ); + if( !Association.class.isAssignableFrom( Classes.RAW_CLASS.map( returnType ) ) + && !ManyAssociation.class.isAssignableFrom( Classes.RAW_CLASS.map( returnType ) ) + && !NamedAssociation.class.isAssignableFrom( Classes.RAW_CLASS.map( returnType ) ) ) + { + throw new QueryExpressionException( "Unsupported association type:" + returnType ); + } + Type associationTypeAsType = GenericAssociationInfo.toAssociationType( returnType ); + if( !( associationTypeAsType instanceof Class ) ) + { + throw new QueryExpressionException( "Unsupported association type:" + associationTypeAsType ); + } + } + + public AssociationFunction<?> traversedAssociation() + { + return traversedAssociation; + } + + public ManyAssociationFunction<?> traversedManyAssociation() + { + return traversedManyAssociation; + } + + public NamedAssociationFunction<?> traversedNamedAssociation() + { + return traversedNamedAssociation; + } + + public AccessibleObject accessor() + { + return accessor; + } + + @Override + public Association<T> map( Composite entity ) + { + try + { + Object target = entity; + if( traversedAssociation != null ) + { + Association<?> association = traversedAssociation.map( entity ); + if( association == null ) + { + return null; + } + target = association.get(); + } + else if( traversedManyAssociation != null ) + { + throw new IllegalArgumentException( "Cannot evaluate a ManyAssociation" ); + } + else if( traversedNamedAssociation != null ) + { + throw new IllegalArgumentException( "Cannot evaluate a NamedAssociation" ); + } + + if( target == null ) + { + return null; + } + + CompositeInstance handler = (CompositeInstance) Proxy.getInvocationHandler( target ); + return ( (AssociationStateHolder) handler.state() ).associationFor( accessor ); + } + catch( IllegalArgumentException e ) + { + throw e; + } + catch( Throwable e ) + { + throw new IllegalArgumentException( e ); + } + } + + @Override + public String toString() + { + if( traversedAssociation != null ) + { + return traversedAssociation.toString() + "." + ( (Member) accessor ).getName(); + } + else + { + return ( (Member) accessor ).getName(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNotNullSpecification.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNotNullSpecification.java b/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNotNullSpecification.java new file mode 100644 index 0000000..64d6def --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNotNullSpecification.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.qi4j.api.query.grammar; + +import org.qi4j.api.association.Association; +import org.qi4j.api.composite.Composite; + +/** + * Association not null Specification. + */ +public class AssociationNotNullSpecification<T> + extends ExpressionSpecification +{ + private AssociationFunction<T> association; + + public AssociationNotNullSpecification( AssociationFunction<T> association ) + { + this.association = association; + } + + public AssociationFunction<T> association() + { + return association; + } + + @Override + public boolean satisfiedBy( Composite item ) + { + try + { + Association<T> assoc = association.map( item ); + + if( assoc == null ) + { + return false; + } + + return assoc.get() != null; + } + catch( IllegalArgumentException e ) + { + return false; + } + } + + @Override + public String toString() + { + return association.toString() + "is not null"; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNullSpecification.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNullSpecification.java b/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNullSpecification.java new file mode 100644 index 0000000..9f06b98 --- /dev/null +++ b/core/api/src/main/java/org/qi4j/api/query/grammar/AssociationNullSpecification.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.qi4j.api.query.grammar; + +import org.qi4j.api.association.Association; +import org.qi4j.api.composite.Composite; + +/** + * Association null Specification. + */ +public class AssociationNullSpecification<T> + extends ExpressionSpecification +{ + private AssociationFunction<T> association; + + public AssociationNullSpecification( AssociationFunction<T> association ) + { + this.association = association; + } + + public AssociationFunction<T> association() + { + return association; + } + + @Override + public boolean satisfiedBy( Composite item ) + { + try + { + Association<T> assoc = association.map( item ); + + if( assoc == null ) + { + return true; + } + + return assoc.get() == null; + } + catch( IllegalArgumentException e ) + { + return true; + } + } + + @Override + public String toString() + { + return association.toString() + "is null"; + } +}
