From b1cf8e0e7e8b012951f9cfbc5827cf0f5922dc13 Mon Sep 17 00:00:00 2001 From: Ruslan Penkrat Date: Sat, 5 Oct 2019 12:30:54 +0300 Subject: [PATCH] add src --- pom.xml | 52 ++++++++++ .../russ_p/externalsql/ExternalSQL.java | 17 ++++ .../externalsql/ExternalSQLFactory.java | 44 +++++++++ .../russ_p/externalsql/SqlResolver.java | 94 +++++++++++++++++++ .../externalsql/ExternalSQLFactoryTest.java | 26 +++++ .../russ_p/externalsql/SqlResolverTest.java | 34 +++++++ .../russ_p/externalsql/TestQueries.java | 9 ++ src/test/resources/test.sql | 17 ++++ 8 files changed, 293 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/github/russ_p/externalsql/ExternalSQL.java create mode 100644 src/main/java/com/github/russ_p/externalsql/ExternalSQLFactory.java create mode 100644 src/main/java/com/github/russ_p/externalsql/SqlResolver.java create mode 100644 src/test/java/com/github/russ_p/externalsql/ExternalSQLFactoryTest.java create mode 100644 src/test/java/com/github/russ_p/externalsql/SqlResolverTest.java create mode 100644 src/test/java/com/github/russ_p/externalsql/TestQueries.java create mode 100644 src/test/resources/test.sql diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c30fc10 --- /dev/null +++ b/pom.xml @@ -0,0 +1,52 @@ + + 4.0.0 + com.github.russ-p + external-sql + jar + 0.1-SNAPSHOT + external-sql + http://maven.apache.org + + + UTF-8 + 1.8 + 1.9.10 + 3.11.1 + 4.12 + + + + + net.bytebuddy + byte-buddy + ${bytebuddy.version} + + + junit + junit + ${junit.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + diff --git a/src/main/java/com/github/russ_p/externalsql/ExternalSQL.java b/src/main/java/com/github/russ_p/externalsql/ExternalSQL.java new file mode 100644 index 0000000..dbab4fa --- /dev/null +++ b/src/main/java/com/github/russ_p/externalsql/ExternalSQL.java @@ -0,0 +1,17 @@ +package com.github.russ_p.externalsql; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(value = ElementType.TYPE) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ExternalSQL { + + /** + * file with sql queries + */ + String value(); + +} diff --git a/src/main/java/com/github/russ_p/externalsql/ExternalSQLFactory.java b/src/main/java/com/github/russ_p/externalsql/ExternalSQLFactory.java new file mode 100644 index 0000000..335e0e1 --- /dev/null +++ b/src/main/java/com/github/russ_p/externalsql/ExternalSQLFactory.java @@ -0,0 +1,44 @@ +package com.github.russ_p.externalsql; + +import java.io.IOException; +import java.util.Objects; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.dynamic.DynamicType.Builder.MethodDefinition.ImplementationDefinition.Optional; +import net.bytebuddy.dynamic.DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition; +import net.bytebuddy.implementation.FixedValue; +import net.bytebuddy.matcher.ElementMatchers; + +public class ExternalSQLFactory { + + @SuppressWarnings("unchecked") + public T create(Class clazz) throws IOException, InstantiationException, IllegalAccessException { + ExternalSQL anno = clazz.getAnnotation(ExternalSQL.class); + Objects.requireNonNull(anno, "Class must be annotated with @ExternalSQL"); + + String srcFile = anno.value(); + SqlResolver resolver = new SqlResolver(srcFile); + + Optional implement = new ByteBuddy() + .subclass(Object.class) + .implement(clazz); + + ReceiverTypeDefinition intercept = implement + .method(ElementMatchers.named("toString")) + .intercept(FixedValue.value("SQL queries from " + srcFile)); + + for (String name : resolver.getQueryNames()) { + intercept = intercept + .method(ElementMatchers.named(name)) + .intercept(FixedValue.value(resolver.getQuery(name))); + } + + Class dynamicType = intercept + .make() + .load(getClass().getClassLoader()) + .getLoaded(); + + return (T) dynamicType.newInstance(); + } + +} diff --git a/src/main/java/com/github/russ_p/externalsql/SqlResolver.java b/src/main/java/com/github/russ_p/externalsql/SqlResolver.java new file mode 100644 index 0000000..3f4f9d1 --- /dev/null +++ b/src/main/java/com/github/russ_p/externalsql/SqlResolver.java @@ -0,0 +1,94 @@ +package com.github.russ_p.externalsql; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +class SqlResolver { + + private static final String NAME_COMMENT_PREFIX = "-- @"; + private static final String COMMENT_PREFIX = "--"; + + private final Map queries = new HashMap<>(); + + public SqlResolver(String filename) throws IOException { + this(new File(resolvePath(filename))); + } + + public SqlResolver(File file) throws IOException { + parseFile(file); + } + + public Set getQueryNames() { + return queries.keySet(); + } + + public String getQuery(String name) { + return queries.getOrDefault(name, ""); + } + + private void put(String name, String query) { + if (name == null || name.isEmpty()) + return; + if (query == null || query.isEmpty()) + return; + + queries.put(name, query); + } + + private void parseFile(File file) throws IOException { + String queryName = ""; + StringBuffer query = new StringBuffer(); + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line = reader.readLine(); + while (line != null) { + if (isSqlHeaderComment(line)) { + put(queryName, query.toString().trim()); + queryName = getName(line); + query = new StringBuffer(); + } else if (isSimpleComment(line)) { + + } else { + query.append(line).append("\n"); + } + + // read next line + line = reader.readLine(); + } + } + put(queryName, query.toString()); + } + + private boolean isSimpleComment(String line) { + return line.startsWith(COMMENT_PREFIX) && !isSqlHeaderComment(line); + } + + private boolean isSqlHeaderComment(String line) { + return line.startsWith(NAME_COMMENT_PREFIX); + } + + private String getName(String line) { + StringTokenizer tokenizer = new StringTokenizer(line, " "); + while (tokenizer.hasMoreElements()) { + String str = tokenizer.nextToken(); + if (str.startsWith("@")) { + return str.substring(1); + } + + } + throw new IllegalStateException("No sql query name found in line " + line); + } + + private static String resolvePath(String path) { + if (path.startsWith("classpath:")) { + return SqlResolver.class.getClassLoader().getResource(path.substring(10)).getFile(); + } + return path; + } + +} diff --git a/src/test/java/com/github/russ_p/externalsql/ExternalSQLFactoryTest.java b/src/test/java/com/github/russ_p/externalsql/ExternalSQLFactoryTest.java new file mode 100644 index 0000000..376e8cd --- /dev/null +++ b/src/test/java/com/github/russ_p/externalsql/ExternalSQLFactoryTest.java @@ -0,0 +1,26 @@ +package com.github.russ_p.externalsql; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +public class ExternalSQLFactoryTest { + + private ExternalSQLFactory factory; + + @Before + public void setUp() throws Exception { + factory = new ExternalSQLFactory(); + } + + @Test + public void testCreate() throws Exception { + TestQueries testQueries = factory.create(TestQueries.class); + + assertThat(testQueries.selectOne()).isNotEmpty(); + assertThat(testQueries.selectOne()).isEqualTo("select * from test_table;"); + assertThat(testQueries.selectTwo()).isNotEmpty(); + } + +} diff --git a/src/test/java/com/github/russ_p/externalsql/SqlResolverTest.java b/src/test/java/com/github/russ_p/externalsql/SqlResolverTest.java new file mode 100644 index 0000000..f817331 --- /dev/null +++ b/src/test/java/com/github/russ_p/externalsql/SqlResolverTest.java @@ -0,0 +1,34 @@ +package com.github.russ_p.externalsql; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +public class SqlResolverTest { + + @Before + public void setUp() throws Exception { + } + + @Test + public void testSqlResolverString() throws Exception { + SqlResolver resolver = new SqlResolver( + SqlResolverTest.class.getClassLoader().getResource("test.sql").getFile()); + + assertThat(resolver.getQueryNames()).hasSize(2); + + assertThat(resolver.getQuery("selectOne")).isNotEmpty(); + assertThat(resolver.getQuery("selectOne")).isEqualTo("select * from test_table;"); + + assertThat(resolver.getQuery("selectTwo")).isNotEmpty(); + assertThat(resolver.getQuery("notExist")).isBlank(); + } + + @Test + public void testSqlResolverClasspathString() throws Exception { + SqlResolver resolver = new SqlResolver("classpath:test.sql"); + + assertThat(resolver).isNotNull(); + } +} diff --git a/src/test/java/com/github/russ_p/externalsql/TestQueries.java b/src/test/java/com/github/russ_p/externalsql/TestQueries.java new file mode 100644 index 0000000..d642a43 --- /dev/null +++ b/src/test/java/com/github/russ_p/externalsql/TestQueries.java @@ -0,0 +1,9 @@ +package com.github.russ_p.externalsql; + +@ExternalSQL("classpath:test.sql") +public interface TestQueries { + + String selectOne(); + + String selectTwo(); +} diff --git a/src/test/resources/test.sql b/src/test/resources/test.sql new file mode 100644 index 0000000..91aaf72 --- /dev/null +++ b/src/test/resources/test.sql @@ -0,0 +1,17 @@ +-- Ext-SQL test file + +-- @selectOne +select * from test_table; + +-- @selectTwo +-- comment example +SELECT +a, +b, + +FROM test_table + +WHERE + a > 1; + +-- end of file \ No newline at end of file