Skip to content

Commit

Permalink
Merge pull request #8 from axelsean/master
Browse files Browse the repository at this point in the history
Added BigDecimal and Date support (from fstn), also embedded mapping
  • Loading branch information
tennaito committed May 5, 2016
2 parents 2d29b74 + a2348f9 commit abcaab6
Show file tree
Hide file tree
Showing 22 changed files with 474 additions and 78 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.github.tennaito</groupId>
<artifactId>rsql-jpa</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.6-SNAPSHOT</version>
<packaging>jar</packaging>

<!--//////////////////// ABOUT ////////////////////-->
Expand Down
Empty file.
Empty file.
11 changes: 10 additions & 1 deletion src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public abstract class AbstractJpaVisitor<T, E> implements RSQLVisitor<T, EntityM
/**
* Construtor with template varargs for entityClass discovery.
*
* @param t not for usage
* @param e not for usage
*/
public AbstractJpaVisitor(E... e) {
// getting class from template... :P
Expand All @@ -60,6 +60,15 @@ public AbstractJpaVisitor(E... e) {
}
}

/**
* Set the entity class explicitly, needed when the entity type is itself a generic
*
* @param clazz Class to set.
*/
public void setEntityClass(Class<E> clazz) {
entityClass = clazz;
}

/**
* Get builder tools.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* The MIT License
*
* Copyright 2015 Antonio Rabelo.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.tennaito.rsql.jpa;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import cz.jirutka.rsql.parser.ast.AndNode;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import cz.jirutka.rsql.parser.ast.OrNode;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;

/**
* JpaCriteriaQueryVisitor
*
* Visitor class for Criteria Query count creation from RSQL AST Nodes.
*
* @author sza
*
* @param <T> Entity type
*/
public class JpaCriteriaCountQueryVisitor<T> extends AbstractJpaVisitor<CriteriaQuery<Long>, T> implements RSQLVisitor<CriteriaQuery<Long>, EntityManager> {

private static final Logger LOG = Logger.getLogger(JpaCriteriaCountQueryVisitor.class.getName());

private final JpaPredicateVisitor<T> predicateVisitor;

private Root<T> root;

/**
* Construtor with template varargs for entityClass discovery.
*
* @param t not for usage
*/
@SafeVarargs
public JpaCriteriaCountQueryVisitor(T... t) {
super(t);
this.predicateVisitor = new JpaPredicateVisitor<T>(t);
}

/**
* Get the Predicate Visitor instance.
*
* @return Return the Predicate Visitor.
*/
protected JpaPredicateVisitor<T> getPredicateVisitor() {
this.predicateVisitor.setBuilderTools(this.getBuilderTools());
return this.predicateVisitor;
}

/* (non-Javadoc)
* @see cz.jirutka.rsql.parser.ast.RSQLVisitor#visit(cz.jirutka.rsql.parser.ast.AndNode, java.lang.Object)
*/
public CriteriaQuery<Long> visit(AndNode node, EntityManager entityManager) {
LOG.log(Level.INFO, "Creating CriteriaQuery for AndNode: {0}", node);

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
root = cq.from(entityClass);
cq.select(cb.countDistinct(root));
cq.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager));

return cq;
}

/* (non-Javadoc)
* @see cz.jirutka.rsql.parser.ast.RSQLVisitor#visit(cz.jirutka.rsql.parser.ast.OrNode, java.lang.Object)
*/
public CriteriaQuery<Long> visit(OrNode node, EntityManager entityManager) {
LOG.log(Level.INFO, "Creating CriteriaQuery for OrNode: {0}", node);

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
root = cq.from(entityClass);
cq.select(cb.countDistinct(root));
root = cq.from(entityClass);
cq.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager));
return cq;
}

/* (non-Javadoc)
* @see cz.jirutka.rsql.parser.ast.RSQLVisitor#visit(cz.jirutka.rsql.parser.ast.ComparisonNode, java.lang.Object)
*/
public CriteriaQuery<Long> visit(ComparisonNode node, EntityManager entityManager) {
LOG.log(Level.INFO, "Creating CriteriaQuery for ComparisonNode: {0}", node);

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
root = cq.from(entityClass);
cq.select(cb.countDistinct(root));
cq.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager));
return cq;
}

public Root<T> getRoot() {
return root;
}

public void setRoot(Root<T> root) {
this.root = root;
}


}
2 changes: 1 addition & 1 deletion src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public JpaCriteriaQueryVisitor(T... t) {
super(t);
this.predicateVisitor = new JpaPredicateVisitor<T>(t);
}

/**
* Get the Predicate Visitor instance.
*
Expand Down
Empty file.
128 changes: 101 additions & 27 deletions src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.metamodel.Attribute;
Expand All @@ -43,6 +44,8 @@
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.PluralAttribute;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
Expand All @@ -65,6 +68,9 @@ public final class PredicateBuilder {

public static final Character LIKE_WILDCARD = '*';

private static final Date START_DATE = new Date(0L) ;
private static final Date END_DATE = new Date(99999999999999999L) ;

/**
* Private constructor.
*/
Expand Down Expand Up @@ -173,7 +179,7 @@ public static <T> Predicate createPredicate(ComparisonNode comparison, From star
* @return The Path for the property path
* @throws IllegalArgumentException if attribute of the given property name does not exist
*/
public static <T> Path<?> findPropertyPath(String propertyPath, From startRoot, EntityManager entityManager, BuilderTools misc) {
public static <T> Path<?> findPropertyPath(String propertyPath, Path startRoot, EntityManager entityManager, BuilderTools misc) {
String[] graph = propertyPath.split("\\.");

Metamodel metaModel = entityManager.getMetamodel();
Expand All @@ -183,25 +189,34 @@ public static <T> Path<?> findPropertyPath(String propertyPath, From startRoot,

for (String property : graph) {
String mappedProperty = misc.getPropertiesMapper().translate(property, classMetadata.getJavaType());
if (!hasPropertyName(mappedProperty, classMetadata)) {
throw new IllegalArgumentException("Unknown property: " + mappedProperty + " from entity " + classMetadata.getJavaType().getName());
}

if (isAssociationType(mappedProperty, classMetadata)) {
Class<?> associationType = findPropertyType(mappedProperty, classMetadata);
String previousClass = classMetadata.getJavaType().getName();
classMetadata = metaModel.managedType(associationType);
LOG.log(Level.INFO, "Create a join between {0} and {1}.", new Object[] {previousClass, classMetadata.getJavaType().getName()});
root = ((From) root).join(mappedProperty);
} else {
LOG.log(Level.INFO, "Create property path for type {0} property {1}.", new Object[] {classMetadata.getJavaType().getName(), mappedProperty});
root = root.get(mappedProperty);

if (isEmbeddedType(mappedProperty, classMetadata)) {
Class<?> embeddedType = findPropertyType(mappedProperty, classMetadata);
classMetadata = metaModel.managedType(embeddedType);
}
}
if( !mappedProperty.equals( property) ) {
root = findPropertyPath( mappedProperty, root, entityManager, misc );
} else {
if (!hasPropertyName(mappedProperty, classMetadata)) {
throw new IllegalArgumentException("Unknown property: " + mappedProperty + " from entity " + classMetadata.getJavaType().getName());
}

if (isAssociationType(mappedProperty, classMetadata)) {
Class<?> associationType = findPropertyType(mappedProperty, classMetadata);
String previousClass = classMetadata.getJavaType().getName();
classMetadata = metaModel.managedType(associationType);
LOG.log(Level.INFO, "Create a join between {0} and {1}.", new Object[]{previousClass, classMetadata.getJavaType().getName()});

if (root instanceof Join) {
root = root.get(mappedProperty);
} else {
root = ((From) root).join(mappedProperty);
}
} else {
LOG.log(Level.INFO, "Create property path for type {0} property {1}.", new Object[]{classMetadata.getJavaType().getName(), mappedProperty});
root = root.get(mappedProperty);

if (isEmbeddedType(mappedProperty, classMetadata)) {
Class<?> embeddedType = findPropertyType(mappedProperty, classMetadata);
classMetadata = metaModel.managedType(embeddedType);
}
}
}
}

return root;
Expand Down Expand Up @@ -245,27 +260,69 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper
}
case GREATER_THAN : {
Object argument = arguments.get(0);
return createGreaterThan(propertyPath, (Number)argument, manager);
}
Predicate predicate;
if (argument instanceof Date) {
int days = 1;
predicate = createBetweenThan(propertyPath, modifyDate(argument, days), END_DATE, manager);
} else {
predicate = createGreaterThan(propertyPath, (Number) argument, manager);
}
return predicate;
}
case GREATER_THAN_OR_EQUAL : {
Object argument = arguments.get(0);
return createGreaterEqual(propertyPath, (Number)argument, manager);
Predicate predicate;
if (argument instanceof Date){
predicate = createBetweenThan(propertyPath, (Date)argument, END_DATE, manager);
}else{
predicate = createGreaterEqual(propertyPath, (Number)argument, manager);
}
return predicate;

}
case LESS_THAN : {
Object argument = arguments.get(0);
return createLessThan(propertyPath, (Number)argument, manager);
}
Predicate predicate;
if (argument instanceof Date) {
int days = -1;
predicate = createBetweenThan(propertyPath, START_DATE, modifyDate(argument, days), manager);
} else {
predicate = createLessThan(propertyPath, (Number) argument, manager);
}
return predicate;
}
case LESS_THAN_OR_EQUAL : {
Object argument = arguments.get(0);
return createLessEqual(propertyPath, (Number)argument, manager);
}

Predicate predicate;
if (argument instanceof Date){
predicate = createBetweenThan(propertyPath,START_DATE, (Date)argument, manager);
}else{
predicate = createLessEqual(propertyPath, (Number)argument, manager);
}
return predicate;
}
case IN : return createIn(propertyPath, arguments, manager);
case NOT_IN : return createNotIn(propertyPath, arguments, manager);
}
}
throw new IllegalArgumentException("Unknown operator: " + operator);
}

/**
+ * Creates the between than.
+ *
+ * @param propertyPath the property path
+ * @param startDate the start date
+ * @param argument the argument
+ * @param manager the manager
+ * @return the predicate
+ */
private static Predicate createBetweenThan(Expression propertyPath, Date start, Date end, EntityManager manager) {
CriteriaBuilder builder = manager.getCriteriaBuilder();
return builder.between(propertyPath, start, end);
}

/**
* Apply a case-insensitive "like" constraint to the property path. Value
* should contains wildcards "*" (% in SQL) and "_".
Expand Down Expand Up @@ -486,4 +543,21 @@ private static <T> Class<?> findPropertyType(String property, ManagedType<T> cla
private static boolean isNullArgument(Object argument) {
return argument == null;
}


/**
* Get date regarding the operation (less then or greater than)
*
* @param argument Date to be modified
* @param days Days to be added or removed form argument;
*@return Date modified date
*/
private static Date modifyDate(Object argument, int days) {
Date date = (Date) argument;
Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, days);
date = c.getTime();
return date;
}
}
Empty file.
Empty file.
Empty file modified src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java
100644 → 100755
Empty file.
2 changes: 2 additions & 0 deletions src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
Expand Down Expand Up @@ -72,6 +73,7 @@ public <T> T parse(String argument, Class<T> type)
if (type.equals(Float.class) || type.equals(float.class)) return (T) Float.valueOf(argument);
if (type.equals(Double.class) || type.equals(double.class)) return (T) Double.valueOf(argument);
if (type.equals(Long.class) || type.equals(long.class)) return (T) Long.valueOf(argument);
if (type.equals(BigDecimal.class) ) return (T) new BigDecimal(argument);
} catch (IllegalArgumentException ex) {
throw new ArgumentFormatException(argument, type);
}
Expand Down
Empty file modified src/main/java/com/github/tennaito/rsql/misc/Mapper.java
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
*/
public class SimpleMapper implements Mapper {

private static final Logger LOG = Logger.getLogger(DefaultArgumentParser.class.getName());
private static final Logger LOG = Logger.getLogger(SimpleMapper.class.getName());

private Map<Class<?>, Map<String, String>> mapping;

Expand Down
Empty file.
Loading

0 comments on commit abcaab6

Please sign in to comment.