Skip to content

Commit 79cb979

Browse files
committed
initial
0 parents  commit 79cb979

File tree

6 files changed

+213
-0
lines changed

6 files changed

+213
-0
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.gradle/
2+
.classpath
3+
.project
4+
.settings/
5+
*.class
6+
*.jar
7+
bin/
8+
build/
9+
target/
10+

build.gradle

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
apply plugin: 'java'
2+
apply plugin: 'eclipse'
3+
4+
sourceCompatibility = 1.8
5+
targetCompatibility = 1.8
6+
7+
version = '0.1'
8+
jar {
9+
manifest {
10+
attributes 'Implementation-Title': 'Lambda2sql', 'Implementation-Version': version
11+
}
12+
}
13+
14+
repositories {
15+
mavenCentral()
16+
}
17+
18+
dependencies {
19+
compile 'com.trigersoft:jaque:2.0.4'
20+
21+
testCompile group: 'junit', name: 'junit', version: '4.+'
22+
}
23+
24+
test {
25+
systemProperties 'jdk.internal.lambda.dumpProxyClasses': '/tmp'
26+
}
27+
28+
uploadArchives {
29+
repositories {
30+
flatDir {
31+
dirs 'repos'
32+
}
33+
}
34+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package lambda2sql;
2+
3+
import static java.nio.file.Files.createTempDirectory;
4+
5+
import java.io.IOException;
6+
import java.util.function.Predicate;
7+
8+
import com.trigersoft.jaque.expression.LambdaExpression;
9+
10+
/**
11+
* A utility class for converting java lambdas to SQL.
12+
* It must be initializes with {@link #init()} before any lambdas are created.
13+
*/
14+
public class Lambda2Sql {
15+
16+
private static final String DUMP_CLASSES_PROP = "jdk.internal.lambda.dumpProxyClasses";
17+
18+
/**
19+
* Initializes the jdk.internal.lambda.dumpProxyClasses system property with a temporary directory.
20+
* See https://bugs.openjdk.java.net/browse/JDK-8023524
21+
*/
22+
public static void init() {
23+
try {
24+
if( System.getProperty(DUMP_CLASSES_PROP) == null )
25+
System.setProperty(DUMP_CLASSES_PROP, createTempDirectory("lambda").toString());
26+
} catch (IOException e) {
27+
throw new IllegalStateException(e);
28+
}
29+
}
30+
31+
/**
32+
* Converts a predicate lambda to SQL. Make sure to {@link Lamda2Sql#init()} before creating the predicate.<br/>
33+
* <pre>{@code person -> person.getAge() > 50 && person.isActive() }</pre>
34+
* Becomes a string:
35+
* <pre>{@code "age > 50 AND active" }</pre>
36+
* Supported operators: >,>=,<,<=,=,!=,&&,||,!
37+
*/
38+
public static <T> String toSql(Predicate<T> predicate) {
39+
LambdaExpression<Predicate<T>> lambdaExpression = LambdaExpression.parse(predicate);
40+
return lambdaExpression.accept(new ToSqlVisitor());
41+
}
42+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package lambda2sql;
2+
3+
import static com.trigersoft.jaque.expression.ExpressionType.Equal;
4+
import static com.trigersoft.jaque.expression.ExpressionType.LogicalAnd;
5+
import static com.trigersoft.jaque.expression.ExpressionType.LogicalOr;
6+
7+
import com.trigersoft.jaque.expression.*;
8+
9+
public class ToSqlVisitor implements ExpressionVisitor<String> {
10+
11+
boolean top = true;
12+
13+
14+
@Override
15+
public String visit(BinaryExpression e) {
16+
boolean quote = !top && e.getExpressionType() == LogicalOr;
17+
top = false;
18+
19+
StringBuilder sb = new StringBuilder();
20+
if( quote ) sb.append('(');
21+
22+
String first = e.getFirst().accept(this);
23+
String second = e.getSecond().accept(this);
24+
25+
sb.append(first).append(' ').append(toSqlOp(e.getExpressionType())).append(' ').append(second);
26+
27+
if( quote ) sb.append(')');
28+
29+
return sb.toString();
30+
}
31+
32+
public static String toSqlOp(int expressionType) {
33+
switch(expressionType) {
34+
case Equal: return "=";
35+
case LogicalAnd: return "AND";
36+
case LogicalOr: return "OR";
37+
}
38+
return ExpressionType.toString(expressionType);
39+
}
40+
41+
@Override
42+
public String visit(ConstantExpression e) {
43+
return e.getValue().toString();
44+
}
45+
46+
@Override
47+
public String visit(InvocationExpression e) {
48+
return e.getTarget().accept(this);
49+
}
50+
51+
@Override
52+
public String visit(LambdaExpression<?> e) {
53+
return e.getBody().accept(this);
54+
}
55+
56+
@Override
57+
public String visit(MemberExpression e) {
58+
String name = e.getMember().getName();
59+
return name.replaceAll("^(get|is)", "").toLowerCase();
60+
}
61+
62+
@Override
63+
public String visit(ParameterExpression e) {
64+
return "";
65+
}
66+
67+
@Override
68+
public String visit(UnaryExpression e) {
69+
String first = e.getFirst().accept(this);
70+
return ExpressionType.toString(e.getExpressionType()) + first;
71+
}
72+
73+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package lambda2sql;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import java.util.function.Predicate;
6+
7+
import org.junit.BeforeClass;
8+
import org.junit.Test;
9+
10+
11+
public class Lambda2SqlTest {
12+
13+
@BeforeClass
14+
public static void init() throws Exception {
15+
Lambda2Sql.init();
16+
}
17+
18+
@Test
19+
public void testComparisons() throws Exception {
20+
assertEqual("age = 1", e -> e.getAge() == 1);
21+
assertEqual("age > 1", e -> e.getAge() > 1);
22+
assertEqual("age < 1", e -> e.getAge() < 1);
23+
assertEqual("age >= 1", e -> e.getAge() >= 1);
24+
assertEqual("age <= 1", e -> e.getAge() <= 1);
25+
assertEqual("age != 1", e -> e.getAge() != 1);
26+
}
27+
28+
@Test
29+
public void testLogicalOps() throws Exception {
30+
assertEqual("!active", e -> ! e.isActive() );
31+
assertEqual("age < 100 AND height > 200", e -> e.getAge() < 100 && e.getHeight() > 200 );
32+
assertEqual("age < 100 OR height > 200", e -> e.getAge() < 100 || e.getHeight() > 200 );
33+
}
34+
35+
@Test
36+
public void testMultiLogicalOps() throws Exception {
37+
assertEqual("active AND (age < 100 OR height > 200)", e -> e.isActive() && (e.getAge() < 100 || e.getHeight() > 200) );
38+
}
39+
40+
private void assertEqual(String expectedSql, Predicate<Person> p) {
41+
String sql = Lambda2Sql.toSql(p);
42+
assertEquals(expectedSql, sql);
43+
}
44+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package lambda2sql;
2+
3+
public interface Person {
4+
5+
public String getName();
6+
public int getAge();
7+
public int getHeight();
8+
public boolean isActive();
9+
10+
}

0 commit comments

Comments
 (0)