Skip to content

Commit

Permalink
Java 17 and more date methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
CollinAlpert committed Aug 10, 2021
1 parent 4979539 commit e2364f0
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 48 deletions.
4 changes: 2 additions & 2 deletions README.MD
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Lambda2sql (lambda) -> "sql"
==========

**Please note:** This is a Java 13 library so make sure you have at least Java 13 installed when using it.
**Please note:** This is a Java 11 library so make sure you have at least Java 13 installed when using it.

Convert Java 8 lambdas to SQL statements.

Expand Down Expand Up @@ -77,7 +77,7 @@ You can include the Maven dependency:
<dependency>
<groupId>com.github.collinalpert</groupId>
<artifactId>lambda2sql</artifactId>
<version>2.4.0</version>
<version>2.5.0</version>
</dependency>
```

Expand Down
12 changes: 6 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.collinalpert</groupId>
<artifactId>lambda2sql</artifactId>
<version>2.4.0</version>
<version>2.5.0</version>
<packaging>jar</packaging>

<name>lambda2sql</name>
Expand Down Expand Up @@ -34,7 +34,7 @@
<scm>
<connection>scm:git:git://github.com/CollinAlpert/lambda2sql.git</connection>
<developerConnection>scm:git:ssh://github.com:CollinAlpert/lambda2sql.git</developerConnection>
<url>http://github.com/CollinAlpert/lambda2sql/tree/master</url>
<url>https://github.com/CollinAlpert/lambda2sql/tree/master</url>
</scm>

<distributionManagement>
Expand All @@ -49,7 +49,7 @@
</distributionManagement>

<properties>
<java-version>13</java-version>
<java-version>11</java-version>
<jdk.version>${java-version}</jdk.version>
<maven.compiler.release>${java-version}</maven.compiler.release>
<maven.compiler.source>${java-version}</maven.compiler.source>
Expand All @@ -61,13 +61,13 @@
<dependency>
<groupId>com.github.collinalpert</groupId>
<artifactId>expressions</artifactId>
<version>2.6.1</version>
<version>2.7.0</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<version>5.8.0-M1</version>
<scope>test</scope>
</dependency>

Expand Down Expand Up @@ -113,7 +113,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<additionalOptions>-html5</additionalOptions>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Lambda2Sql {
* @return A {@link String} describing the SQL where condition.
*/
public static String toSql(SerializedFunctionalInterface functionalInterface, String tableName, boolean withBackticks) {
var lambdaExpression = LambdaExpression.parse(functionalInterface);
LambdaExpression lambdaExpression = LambdaExpression.parse(functionalInterface);
return lambdaExpression.accept(new SqlVisitor(tableName, withBackticks)).toString();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.github.collinalpert.lambda2sql;

/**
* This class contains placeholder functions which can be used within an expression to make use of the respective SQL function.
*
* @author Collin Alpert
*/
public class SqlFunctions {
Expand Down
110 changes: 71 additions & 39 deletions src/main/java/com/github/collinalpert/lambda2sql/SqlVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ public class SqlVisitor implements ExpressionVisitor<StringBuilder> {
try {
operatorMethods.put(String.class.getDeclaredMethod("equals", Object.class), ExpressionType.Equal);
operatorMethods.put(Object.class.getDeclaredMethod("equals", Object.class), ExpressionType.Equal);
operatorMethods.put(LocalDate.class.getDeclaredMethod("isAfter", ChronoLocalDate.class), ExpressionType.GreaterThan);

operatorMethods.put(LocalTime.class.getDeclaredMethod("isAfter", LocalTime.class), ExpressionType.GreaterThan);
operatorMethods.put(LocalDate.class.getDeclaredMethod("isAfter", ChronoLocalDate.class), ExpressionType.GreaterThan);
operatorMethods.put(LocalDateTime.class.getDeclaredMethod("isAfter", ChronoLocalDateTime.class), ExpressionType.GreaterThan);

operatorMethods.put(LocalDate.class.getDeclaredMethod("isBefore", ChronoLocalDate.class), ExpressionType.LessThan);
operatorMethods.put(LocalTime.class.getDeclaredMethod("isBefore", LocalTime.class), ExpressionType.LessThan);
operatorMethods.put(LocalDateTime.class.getDeclaredMethod("isBefore", ChronoLocalDateTime.class), ExpressionType.LessThan);
Expand All @@ -52,7 +54,7 @@ public class SqlVisitor implements ExpressionVisitor<StringBuilder> {
/**
* More complex methods that can be used on Java objects inside the lambda expressions.
*/
private final Map<Member, TriFunction<Expression, Expression, Boolean, StringBuilder>> complexMethods;
private final Map<Member, TriFunction<Expression, Expression, Boolean, StringBuilder>> javaMethods;

private final StringBuilder sb;
private Expression body;
Expand All @@ -70,33 +72,61 @@ private SqlVisitor(String tableName, boolean withBackticks, Expression body, Lin
this.sb = new StringBuilder();
this.parameterConsumptionCount = new HashMap<>();

this.complexMethods = new HashMap<>(32, 1);
this.javaMethods = new HashMap<>(1 << 6, 1);
try {
this.complexMethods.put(String.class.getDeclaredMethod("startsWith", String.class), this::stringStartsWith);
this.complexMethods.put(String.class.getDeclaredMethod("endsWith", String.class), this::stringEndsWith);
this.complexMethods.put(String.class.getDeclaredMethod("contains", CharSequence.class), this::stringContains);
this.complexMethods.put(String.class.getDeclaredMethod("length"), (string, argument, isNegated) -> applySqlFunction(string, "LENGTH"));

this.complexMethods.put(List.class.getDeclaredMethod("contains", Object.class), this::listContains);
this.complexMethods.put(ArrayList.class.getDeclaredMethod("contains", Object.class), this::listContains);
this.complexMethods.put(LinkedList.class.getDeclaredMethod("contains", Object.class), this::listContains);

this.complexMethods.put(LocalTime.class.getDeclaredMethod("getSecond"), (date, argument, isNegated) -> applySqlFunction(date, "SECOND"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getSecond"), (date, argument, isNegated) -> applySqlFunction(date, "SECOND"));
this.complexMethods.put(LocalTime.class.getDeclaredMethod("getMinute"), (date, argument, isNegated) -> applySqlFunction(date, "MINUTE"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getMinute"), (date, argument, isNegated) -> applySqlFunction(date, "MINUTE"));
this.complexMethods.put(LocalTime.class.getDeclaredMethod("getHour"), (date, argument, isNegated) -> applySqlFunction(date, "HOUR"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getHour"), (date, argument, isNegated) -> applySqlFunction(date, "HOUR"));
this.complexMethods.put(LocalDate.class.getDeclaredMethod("getDayOfWeek"), (date, argument, isNegated) -> applySqlFunction(date, "DAYOFWEEK"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getDayOfWeek"), (date, argument, isNegated) -> applySqlFunction(date, "DAYOFWEEK"));
this.complexMethods.put(LocalDate.class.getDeclaredMethod("getDayOfMonth"), (date, argument, isNegated) -> applySqlFunction(date, "DAY"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getDayOfWeek"), (date, argument, isNegated) -> applySqlFunction(date, "DAY"));
this.complexMethods.put(LocalDate.class.getDeclaredMethod("getDayOfYear"), (date, argument, isNegated) -> applySqlFunction(date, "DAYOFYEAR"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getDayOfYear"), (date, argument, isNegated) -> applySqlFunction(date, "DAYOFYEAR"));
this.complexMethods.put(LocalDate.class.getDeclaredMethod("getMonthValue"), (date, argument, isNegated) -> applySqlFunction(date, "MONTH"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getMonthValue"), (date, argument, isNegated) -> applySqlFunction(date, "MONTH"));
this.complexMethods.put(LocalDate.class.getDeclaredMethod("getYear"), (date, argument, isNegated) -> applySqlFunction(date, "YEAR"));
this.complexMethods.put(LocalDateTime.class.getDeclaredMethod("getYear"), (date, argument, isNegated) -> applySqlFunction(date, "YEAR"));
this.javaMethods.put(String.class.getDeclaredMethod("startsWith", String.class), this::stringStartsWith);
this.javaMethods.put(String.class.getDeclaredMethod("endsWith", String.class), this::stringEndsWith);
this.javaMethods.put(String.class.getDeclaredMethod("contains", CharSequence.class), this::stringContains);
this.javaMethods.put(String.class.getDeclaredMethod("length"), applySqlFunction("LENGTH"));
this.javaMethods.put(String.class.getDeclaredMethod("toLowerCase"), applySqlFunction("UPPER"));
this.javaMethods.put(String.class.getDeclaredMethod("toUpperCase"), applySqlFunction("LOWER"));

this.javaMethods.put(List.class.getDeclaredMethod("contains", Object.class), this::listContains);
this.javaMethods.put(ArrayList.class.getDeclaredMethod("contains", Object.class), this::listContains);
this.javaMethods.put(LinkedList.class.getDeclaredMethod("contains", Object.class), this::listContains);

this.javaMethods.put(LocalTime.class.getDeclaredMethod("getSecond"), applySqlFunction("SECOND"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getSecond"), applySqlFunction("SECOND"));
this.javaMethods.put(OffsetTime.class.getDeclaredMethod("getSecond"), applySqlFunction("SECOND"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getSecond"), applySqlFunction("SECOND"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getSecond"), applySqlFunction("SECOND"));

this.javaMethods.put(LocalTime.class.getDeclaredMethod("getMinute"), applySqlFunction("MINUTE"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getMinute"), applySqlFunction("MINUTE"));
this.javaMethods.put(OffsetTime.class.getDeclaredMethod("getMinute"), applySqlFunction("MINUTE"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getMinute"), applySqlFunction("MINUTE"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getMinute"), applySqlFunction("MINUTE"));

this.javaMethods.put(LocalTime.class.getDeclaredMethod("getHour"), applySqlFunction("HOUR"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getHour"), applySqlFunction("HOUR"));
this.javaMethods.put(OffsetTime.class.getDeclaredMethod("getHour"), applySqlFunction("HOUR"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getHour"), applySqlFunction("HOUR"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getHour"), applySqlFunction("HOUR"));

this.javaMethods.put(LocalDate.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAYOFWEEK"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAYOFWEEK"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAYOFWEEK"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAYOFWEEK"));

this.javaMethods.put(LocalDate.class.getDeclaredMethod("getDayOfMonth"), applySqlFunction("DAY"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAY"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAY"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getDayOfWeek"), applySqlFunction("DAY"));

this.javaMethods.put(LocalDate.class.getDeclaredMethod("getDayOfYear"), applySqlFunction("DAYOFYEAR"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getDayOfYear"), applySqlFunction("DAYOFYEAR"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getDayOfYear"), applySqlFunction("DAYOFYEAR"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getDayOfYear"), applySqlFunction("DAYOFYEAR"));

this.javaMethods.put(LocalDate.class.getDeclaredMethod("getMonthValue"), applySqlFunction("MONTH"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getMonthValue"), applySqlFunction("MONTH"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getMonthValue"), applySqlFunction("MONTH"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getMonthValue"), applySqlFunction("MONTH"));

this.javaMethods.put(LocalDate.class.getDeclaredMethod("getYear"), applySqlFunction("YEAR"));
this.javaMethods.put(LocalDateTime.class.getDeclaredMethod("getYear"), applySqlFunction("YEAR"));
this.javaMethods.put(OffsetDateTime.class.getDeclaredMethod("getYear"), applySqlFunction("YEAR"));
this.javaMethods.put(ZonedDateTime.class.getDeclaredMethod("getYear"), applySqlFunction("YEAR"));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Expand Down Expand Up @@ -184,7 +214,7 @@ public StringBuilder visit(ConstantExpression e) {
return sb.append("'").append(escapeString(e.getValue().toString())).append("'");
}

return sb.append(e.getValue().toString());
return sb.append(e.getValue());
}

/**
Expand All @@ -205,7 +235,7 @@ public StringBuilder visit(InvocationExpression e) {
.map(ConstantExpression.class::cast)
.collect(Collectors.toList());
if (!list.isEmpty()) {
arguments.push(list);
this.arguments.push(list);
}
}

Expand All @@ -219,7 +249,7 @@ public StringBuilder visit(InvocationExpression e) {
}

if (e.getTarget().getExpressionType() == ExpressionType.MethodAccess && !e.getArguments().isEmpty()) {
javaMethodParameter = e.getArguments().get(0);
this.javaMethodParameter = e.getArguments().get(0);
}

return e.getTarget().accept(this);
Expand Down Expand Up @@ -261,8 +291,9 @@ public StringBuilder visit(MemberExpression e) {
return Expression.binary(operatorMethods.get(e.getMember()), e.getInstance(), this.javaMethodParameter).accept(this);
}

if (this.complexMethods.containsKey(e.getMember())) {
return sb.append(this.complexMethods.get(e.getMember()).apply(e.getInstance(), this.javaMethodParameter, false));
var javaMethodReplacer = this.javaMethods.get(e.getMember());
if (javaMethodReplacer != null) {
return sb.append(javaMethodReplacer.apply(e.getInstance(), this.javaMethodParameter, false));
}

var nameArray = e.getMember().getName().replaceAll("^(get)", "").toCharArray();
Expand Down Expand Up @@ -320,8 +351,8 @@ public StringBuilder visit(UnaryExpression e) {
var memberExpression = (MemberExpression) invocationExpression.getTarget();
if (operatorMethods.containsKey(memberExpression.getMember())) {
return Expression.logicalNot(Expression.binary(operatorMethods.get(memberExpression.getMember()), memberExpression.getInstance(), invocationExpression.getArguments().get(0))).accept(this);
} else if (complexMethods.containsKey(memberExpression.getMember())) {
return sb.append(complexMethods.get(memberExpression.getMember()).apply(memberExpression.getInstance(), invocationExpression.getArguments().get(0), true));
} else if (this.javaMethods.containsKey(memberExpression.getMember())) {
return sb.append(this.javaMethods.get(memberExpression.getMember()).apply(memberExpression.getInstance(), invocationExpression.getArguments().get(0), true));
} else {
sb.append("!");
}
Expand All @@ -348,15 +379,16 @@ private StringBuilder stringContains(Expression string, Expression argument, boo
}

private StringBuilder listContains(Expression list, Expression argument, boolean isNegated) {
List l = (List) arguments.pop().get(((ParameterExpression) list).getIndex()).getValue();
@SuppressWarnings("unchecked")
List<Object> l = (List<Object>) this.arguments.pop().get(((ParameterExpression) list).getIndex()).getValue();
var joiner = new StringJoiner(", ", "(", ")");
l.forEach(x -> joiner.add(x.toString()));

return argument.accept(new SqlVisitor(this.tableName, this.withBackticks, this.body, this.arguments)).append(isNegated ? " NOT" : "").append(" IN ").append(joiner.toString());
return argument.accept(new SqlVisitor(this.tableName, this.withBackticks, this.body, this.arguments)).append(isNegated ? " NOT" : "").append(" IN ").append(joiner);
}

private StringBuilder applySqlFunction(Expression date, String field) {
return new StringBuilder().append(field).append("(").append(date.accept(new SqlVisitor(this.tableName, this.withBackticks, this.body, this.arguments))).append(')');
private TriFunction<Expression, Expression, Boolean, StringBuilder> applySqlFunction(String field) {
return (value, argument, isNegated) -> new StringBuilder().append(field).append("(").append(value.accept(new SqlVisitor(this.tableName, this.withBackticks, this.body, this.arguments))).append(')');
}

//endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ void testLastParameterNull() {
void testMultipleSameParameter() {
String s = "Steve";
SqlPredicate<IPerson> p = x -> x.getName() == s || x.getLastName() == s;
SqlPredicate<IPerson> p2 = x -> x.getName() == s || x.getName() == s && x.isActive();
SqlPredicate<IPerson> p3 = x -> x.getName() == s || x.getLastName() == s && x.isActive();
assertPredicateEqual("`person`.`name` = 'Steve' OR `person`.`lastName` = 'Steve'", p);
assertPredicateEqual("`person`.`name` = 'Steve' OR `person`.name` = 'Steve' AND `person`.`isActive`", p2);
assertPredicateEqual("`person`.`name` = 'Steve' OR `person`.`lastName` = 'Steve' AND `person`.`isActive`", p3);
}

@Test
Expand Down

0 comments on commit e2364f0

Please sign in to comment.