diff --git a/.travis.yml b/.travis.yml index df6bcdc..67f92d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: java jdk: -- oraclejdk8 +- openjdk8 diff --git a/pom.xml b/pom.xml index b76c860..9fe3c56 100644 --- a/pom.xml +++ b/pom.xml @@ -1,31 +1,16 @@ - + 4.0.0 - - de.labathome - de-labathome-parent - 1.0.0 - - - de.labathome + pl.joegreen lambda-from-string 1.7-SNAPSHOT jar Lambda from String Java8 library that can create a lambda from its code stored in a string - - ${jonathanschilling.git.url}${project.name} - - ${jonathanschilling.git.root}${project.name}.git - ${jonathanschilling.git.root}${project.name}.git - ${jonathanschilling.git.url}${project.name}/tree/master - - - ${jonathanschilling.git.url}${project.name}/issues - GitLab Issues - - + https://github.com/greenjoe/lambdaFromString + MIT License @@ -40,22 +25,134 @@ http://joegreen.pl - Jonathan Schilling - jonathan.schilling@mail.de - http://www.labathome.de/ + Jonathan Schilling + jonathan.schilling@mail.de + http://www.labathome.de/ + + scm:git:git@github.com:greenjoe/lambdaFromString.git + scm:git:git@github.com:greenjoe/lambdaFromString.git + git@github.com:greenjoe/lambdaFromString.git + HEAD + + + + UTF-8 + 5.6.0 + 1.6.0 + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.jupiter.version} + test + org.eclipse.jdt.core.compiler ecj 4.4.2 + org.apache.commons commons-lang3 3.6 + + + + + + maven-compiler-plugin + 2.5.1 + + 1.8 + 1.8 + -Xlint:all + -Werror + true + true + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.1 + + + attach-javadocs + + jar + + + -Xdoclint:none + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + true + + + + + + + + + gpg + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + diff --git a/src/main/java/pl/joegreen/lambdaFromString/JavaCompilerProvider.java b/src/main/java/pl/joegreen/lambdaFromString/JavaCompilerProvider.java index 6ad1e29..94a90c7 100644 --- a/src/main/java/pl/joegreen/lambdaFromString/JavaCompilerProvider.java +++ b/src/main/java/pl/joegreen/lambdaFromString/JavaCompilerProvider.java @@ -8,34 +8,34 @@ class JavaCompilerProvider { - /** - * Obtain (if available) the default JDK compiler or, if running on JRE, the - * Eclipse compiler. Prioritize the JDK compiler since it works for higher Java - * versions as well. - * - * @return a Java compiler - */ - static Optional findDefaultJavaCompiler() { - Optional jdkJavaCompiler = getJdkJavaCompiler(); - Optional eclipseJavaCompiler = getEclipseJavaCompiler(); - if (jdkJavaCompiler.isPresent()) { - return jdkJavaCompiler; - } else if (eclipseJavaCompiler.isPresent()) { - return eclipseJavaCompiler; - } else { - return Optional.empty(); - } - } + /** + * Obtain (if available) the default JDK compiler or, if running on JRE, the + * Eclipse compiler. Prioritize the JDK compiler since it works for higher Java + * versions as well. + * + * @return a Java compiler + */ + static Optional findDefaultJavaCompiler() { + Optional jdkJavaCompiler = getJdkJavaCompiler(); + Optional eclipseJavaCompiler = getEclipseJavaCompiler(); + if (jdkJavaCompiler.isPresent()) { + return jdkJavaCompiler; + } else if (eclipseJavaCompiler.isPresent()) { + return eclipseJavaCompiler; + } else { + return Optional.empty(); + } + } - static Optional getEclipseJavaCompiler() { - try { - return Optional.of(new EclipseCompiler()); - } catch (NoClassDefFoundError err) { - return Optional.empty(); - } - } + static Optional getEclipseJavaCompiler() { + try { + return Optional.of(new EclipseCompiler()); + } catch (NoClassDefFoundError err) { + return Optional.empty(); + } + } - static Optional getJdkJavaCompiler() { - return Optional.ofNullable(ToolProvider.getSystemJavaCompiler()); - } + static Optional getJdkJavaCompiler() { + return Optional.ofNullable(ToolProvider.getSystemJavaCompiler()); + } } diff --git a/src/main/java/pl/joegreen/lambdaFromString/LambdaFactory.java b/src/main/java/pl/joegreen/lambdaFromString/LambdaFactory.java index b9ff01c..a7f6d91 100644 --- a/src/main/java/pl/joegreen/lambdaFromString/LambdaFactory.java +++ b/src/main/java/pl/joegreen/lambdaFromString/LambdaFactory.java @@ -14,6 +14,7 @@ public class LambdaFactory { /** * Returns a LambdaFactory instance with default configuration. + * * @throws JavaCompilerNotFoundException if the library cannot find any java compiler */ public static LambdaFactory get() { @@ -22,8 +23,9 @@ public static LambdaFactory get() { /** * Returns a LambdaFactory instance with the given configuration. + * * @throws JavaCompilerNotFoundException if the library cannot find any java compiler and it's not provided - * in the configuration + * in the configuration */ public static LambdaFactory get(LambdaFactoryConfiguration configuration) { JavaCompiler compiler = Optional.ofNullable(configuration.getJavaCompiler()).orElseThrow(JavaCompilerNotFoundException::new); @@ -77,7 +79,7 @@ public T createLambda(String code, TypeReference typeReference) throws La Method lambdaReturningMethod = helperClass.getMethod(helperProvider.getLambdaReturningMethodName()); @SuppressWarnings("unchecked") // the whole point of the class template and runtime compilation is to make this cast work well :-) - T lambda = (T) lambdaReturningMethod.invoke(null); + T lambda = (T) lambdaReturningMethod.invoke(null); return lambda; } catch (ReflectiveOperationException | RuntimeException | NoClassDefFoundError e) { // NoClassDefFoundError can be thrown if provided parent class loader cannot load classes used by the lambda @@ -89,11 +91,11 @@ public T createLambda(String code, TypeReference typeReference) throws La } private List createOptionsForCompilationClasspath(String compilationClassPath, boolean enablePreview) { - if (enablePreview && DefaultClassFactory.getJavaVersion() >= 11) { - return Arrays.asList("-classpath", compilationClassPath, "--enable-preview"); - } + if (enablePreview && DefaultClassFactory.getJavaVersion() >= 11) { + return Arrays.asList("-classpath", compilationClassPath, "--enable-preview"); + } - return Arrays.asList("-classpath", compilationClassPath); + return Arrays.asList("-classpath", compilationClassPath); } /** diff --git a/src/main/java/pl/joegreen/lambdaFromString/LambdaFactoryConfiguration.java b/src/main/java/pl/joegreen/lambdaFromString/LambdaFactoryConfiguration.java index 9c69285..6128e9c 100644 --- a/src/main/java/pl/joegreen/lambdaFromString/LambdaFactoryConfiguration.java +++ b/src/main/java/pl/joegreen/lambdaFromString/LambdaFactoryConfiguration.java @@ -76,9 +76,9 @@ public ClassLoader getParentClassLoader() { public JavaCompiler getJavaCompiler() { return javaCompiler; } - + public boolean getEnablePreview() { - return enablePreview; + return enablePreview; } /** @@ -154,9 +154,9 @@ public LambdaFactoryConfiguration withJavaCompiler(JavaCompiler javaCompiler) { } public LambdaFactoryConfiguration withEnablePreview(boolean enablePreview) { - return copy().setEnablePreview(enablePreview); + return copy().setEnablePreview(enablePreview); } - + private LambdaFactoryConfiguration setDefaultHelperClassSourceProvider(HelperClassSourceProvider helperClassSourceProvider) { this.helperClassSourceProvider = helperClassSourceProvider; @@ -192,10 +192,10 @@ private LambdaFactoryConfiguration setJavaCompiler(JavaCompiler javaCompiler) { this.javaCompiler = javaCompiler; return this; } - + private LambdaFactoryConfiguration setEnablePreview(boolean enablePreview) { - this.enablePreview = enablePreview; - return this; + this.enablePreview = enablePreview; + return this; } private static List listWithNewElements(List oldList, T... newElements) { @@ -203,26 +203,26 @@ private static List listWithNewElements(List oldList, T... newElements } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof LambdaFactoryConfiguration)) { - return false; - } - LambdaFactoryConfiguration other = (LambdaFactoryConfiguration) obj; - return Objects.equals(classFactory, other.classFactory) - && Objects.equals(compilationClassPath, other.compilationClassPath) - && enablePreview == other.enablePreview - && Objects.equals(helperClassSourceProvider, other.helperClassSourceProvider) - && Objects.equals(imports, other.imports) && Objects.equals(javaCompiler, other.javaCompiler) - && Objects.equals(parentClassLoader, other.parentClassLoader) - && Objects.equals(staticImports, other.staticImports); - } + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof LambdaFactoryConfiguration)) { + return false; + } + LambdaFactoryConfiguration other = (LambdaFactoryConfiguration) obj; + return Objects.equals(classFactory, other.classFactory) + && Objects.equals(compilationClassPath, other.compilationClassPath) + && enablePreview == other.enablePreview + && Objects.equals(helperClassSourceProvider, other.helperClassSourceProvider) + && Objects.equals(imports, other.imports) && Objects.equals(javaCompiler, other.javaCompiler) + && Objects.equals(parentClassLoader, other.parentClassLoader) + && Objects.equals(staticImports, other.staticImports); + } @Override - public int hashCode() { - return Objects.hash(classFactory, compilationClassPath, enablePreview, helperClassSourceProvider, imports, - javaCompiler, parentClassLoader, staticImports); - } + public int hashCode() { + return Objects.hash(classFactory, compilationClassPath, enablePreview, helperClassSourceProvider, imports, + javaCompiler, parentClassLoader, staticImports); + } } diff --git a/src/main/java/pl/joegreen/lambdaFromString/classFactory/DefaultClassFactory.java b/src/main/java/pl/joegreen/lambdaFromString/classFactory/DefaultClassFactory.java index c076477..33a88b7 100644 --- a/src/main/java/pl/joegreen/lambdaFromString/classFactory/DefaultClassFactory.java +++ b/src/main/java/pl/joegreen/lambdaFromString/classFactory/DefaultClassFactory.java @@ -38,9 +38,9 @@ protected Map compileClasses( ClassSourceJavaObject classSourceObject = new ClassSourceJavaObject(fullClassName, sourceCode); /* * diagnosticListener = null -> compiler's default reporting - * diagnostics; locale = null -> default locale to format diagnostics; - * charset = null -> uses platform default charset - */ + * diagnostics; locale = null -> default locale to format diagnostics; + * charset = null -> uses platform default charset + */ try (InMemoryFileManager stdFileManager = new InMemoryFileManager(compiler.getStandardFileManager(null, null, null))) { StringWriter stdErrWriter = new StringWriter(); DiagnosticCollector diagnosticsCollector = new DiagnosticCollector<>(); @@ -59,30 +59,30 @@ protected Map compileClasses( } } - /** - * Query the feature version of the JVM this is running on based on the - * {@code java.version} system property. - * - * @return e.g. 6 for {@code java.version}="1.6.0_23" and 9 for "9.0.1" - * @see https://stackoverflow.com/a/2591122 - */ - public static int getJavaVersion() { - String version = System.getProperty("java.version"); - if (version.startsWith("1.")) { - version = version.substring(2, 3); - } else { - int dot = version.indexOf("."); - if (dot != -1) { - version = version.substring(0, dot); - } - } - return Integer.parseInt(version); - } - - protected List getDefaultCompilerOptions() { - int javaVersion = getJavaVersion(); - String javaVersionString = (javaVersion <= 8 ? "1." : "") + Integer.toString(javaVersion); - return Arrays.asList("-target", javaVersionString, "-source", javaVersionString); + /** + * Query the feature version of the JVM this is running on based on the + * {@code java.version} system property. + * + * @return e.g. 6 for {@code java.version}="1.6.0_23" and 9 for "9.0.1" + * @see https://stackoverflow.com/a/2591122 + */ + public static int getJavaVersion() { + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } + + protected List getDefaultCompilerOptions() { + int javaVersion = getJavaVersion(); + String javaVersionString = (javaVersion <= 8 ? "1." : "") + Integer.toString(javaVersion); + return Arrays.asList("-target", javaVersionString, "-source", javaVersionString); } private List mergeStringLists(List firstList, List sendList) { diff --git a/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryConfigurationTest.java b/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryConfigurationTest.java index 0b5fa1d..ac96697 100644 --- a/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryConfigurationTest.java +++ b/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryConfigurationTest.java @@ -2,21 +2,17 @@ import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; import org.junit.jupiter.api.Test; - import pl.joegreen.lambdaFromString.classFactory.ClassFactory; import pl.joegreen.lambdaFromString.classFactory.DefaultClassFactory; import javax.tools.JavaCompiler; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertSame; - import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + public class LambdaFactoryConfigurationTest { @Test @@ -51,9 +47,14 @@ public void usingWithSetsParameters() { JavaCompiler javaCompiler = new EclipseCompiler(); LambdaFactoryConfiguration changedConfiguration = LambdaFactoryConfiguration.get() - .withHelperClassSourceProvider(helper).withClassFactory(classFactory).withStaticImports(staticImports) - .withImports(imports).withCompilationClassPath(compilationClassPath) - .withParentClassLoader(parentClassLoader).withJavaCompiler(javaCompiler); + .withHelperClassSourceProvider(helper) + .withClassFactory(classFactory) + .withStaticImports(staticImports) + .withImports(imports) + .withCompilationClassPath(compilationClassPath) + .withParentClassLoader(parentClassLoader) + .withJavaCompiler(javaCompiler) + .withEnablePreview(true); assertSame(helper, changedConfiguration.getDefaultHelperClassSourceProvider()); assertSame(classFactory, changedConfiguration.getClassFactory()); @@ -62,6 +63,7 @@ public void usingWithSetsParameters() { assertEquals(compilationClassPath, changedConfiguration.getCompilationClassPath()); assertSame(parentClassLoader, changedConfiguration.getParentClassLoader()); assertSame(javaCompiler, changedConfiguration.getJavaCompiler()); + assertTrue(changedConfiguration.getEnablePreview()); } diff --git a/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryTest.java b/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryTest.java index 34f18c7..3f999fd 100644 --- a/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryTest.java +++ b/src/test/java/pl/joegreen/lambdaFromString/LambdaFactoryTest.java @@ -1,24 +1,14 @@ package pl.joegreen.lambdaFromString; import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; - import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - import pl.joegreen.lambdaFromString.classFactory.DefaultClassFactory; import pl.joegreen.lambdaFromString.dummy.CustomInterface; import pl.joegreen.lambdaFromString.dummy.CustomInterfaceUsingInnerClass; import javax.tools.JavaCompiler; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import java.lang.reflect.Method; import java.math.BigDecimal; import java.net.URL; @@ -33,247 +23,249 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -class LambdaFactoryTest { - - public static final String INCORRECT_CODE = "()->a"; - - static Stream jdkAndEclipse() { - int javaVersion = DefaultClassFactory.getJavaVersion(); - - if (javaVersion <= 8) { - return Stream.of(Arguments.of(JavaCompilerProvider.getJdkJavaCompiler().get()), - Arguments.of(new EclipseCompiler())); - } - - return Stream.of(Arguments.of(JavaCompilerProvider.getJdkJavaCompiler().get())); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void integerIncrement(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - Function lambda = factory.createLambdaUnchecked("i -> i+1", - new TypeReference>() { - }); - assertTrue(1 == lambda.apply(0)); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void integerMultiply(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - IntBinaryOperator lambda = factory.createLambdaUnchecked("(a,b) -> a*b", - new TypeReference() { - }); - assertEquals(1 * 2 * 3 * 4, IntStream.range(1, 5).reduce(lambda).getAsInt()); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void integerToString(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - Function lambda = factory.createLambdaUnchecked("i -> \"ABC\"+i+\"DEF\"", - new TypeReference>() { - }); - assertEquals("ABC101DEF", lambda.apply(101)); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void useImportToAddBigDecimals(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory - .get(LambdaFactoryConfiguration.get().withImports(BigDecimal.class).withJavaCompiler(jc)); - assertDoesNotThrow(() -> { - BiFunction lambda = factory.createLambda("(a,b) -> a.add(b)", - new TypeReference>() { - }); - assertEquals(new BigDecimal("11"), lambda.apply(BigDecimal.ONE, BigDecimal.TEN)); - }); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void useStaticImportToIncrementBigDecimals(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withImports(BigDecimal.class) - .withStaticImports("java.math.BigDecimal.ONE").withJavaCompiler(jc)); - Function lambda = factory.createLambdaUnchecked("a -> a.add(ONE)", - new TypeReference>() { - }); - assertEquals(new BigDecimal("11"), lambda.apply(BigDecimal.TEN)); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void usingNoArgStringConstructorAsCode(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - Supplier lambda = factory.createLambdaUnchecked("String::new", new TypeReference>() { - }); - String string = lambda.get(); - assertEquals("", string); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void creatingIntegerInsteadOfLambda(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - Integer result = factory.createLambdaUnchecked("1+2", new TypeReference() { - }); - assertEquals(3, result.intValue()); - } +import static org.junit.jupiter.api.Assertions.*; - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaCreatingAnonymousClass(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - String code = "() -> new Object(){ public String toString(){return \"test\";}}"; - Supplier lambda = factory.createLambdaUnchecked(code, new TypeReference>() { - }); - Object object = lambda.get(); - assertEquals("test", object.toString()); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaCreatingMapEntry(JavaCompiler jc) { - String code = "(str, num) -> new SimpleEntry(str, num)"; - LambdaFactory factory = LambdaFactory - .get(LambdaFactoryConfiguration.get().withImports(SimpleEntry.class).withJavaCompiler(jc)); - BiFunction> lambda = factory.createLambdaUnchecked(code, - new TypeReference>>() { - }); - SimpleEntry se = lambda.apply("a", 1L); - assertEquals(new SimpleEntry<>("a", 1L), se); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaCreatingComplicatedGenericType(JavaCompiler jc) { - String code = "() -> ( x -> new ArrayList<>())"; - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get() - .withImports(SimpleEntry.class, ArrayList.class).withJavaCompiler(jc).withEnablePreview(true)); - Supplier>>> lambda = factory - .createLambdaUnchecked(code, - new TypeReference>>>>() { - }); - Function>> function = lambda.get(); - assertEquals(new ArrayList>(), function.apply(null)); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void integerIncrementWithDynamicTypeReference(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - Object uncheckedLambda = factory.createLambdaUnchecked("i -> i+1", - new DynamicTypeReference("Function")); - @SuppressWarnings("unchecked") - Function lambda = (Function) uncheckedLambda; - assertTrue(1 == lambda.apply(0)); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void integerMultiplyWithDynamicTypeReference(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - IntBinaryOperator lambda = (IntBinaryOperator) factory.createLambdaUnchecked("(a,b) -> a*b", - new DynamicTypeReference("IntBinaryOperator")); - assertEquals(1 * 2 * 3 * 4, IntStream.range(1, 5).reduce(lambda).getAsInt()); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaImplementingNonStandardInterface(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withImports(CustomInterface.class) - .withJavaCompiler(jc).withEnablePreview(true)); - String code = " x -> 10"; - CustomInterface lambda = factory.createLambdaUnchecked(code, new TypeReference() { - }); - assertEquals(lambda.customFunction("abc"), 10); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaImplementingBinaryClassFileOnCustomClassPath(JavaCompiler jc) { - URL classPathDirectory = this.getClass().getClassLoader().getResource("binaryFileTests/"); - URLClassLoader customClassLoader = new URLClassLoader(new URL[] { classPathDirectory }); - String classPathExtractedFromClassLoader = ClassPathExtractor.getUrlClassLoaderClassPath(customClassLoader); - LambdaFactory factory = LambdaFactory - .get(LambdaFactoryConfiguration.get().withCompilationClassPath(classPathExtractedFromClassLoader) - .withParentClassLoader(customClassLoader).withJavaCompiler(jc)); - String code = " x -> x.toUpperCase()"; - Object lambda = factory.createLambdaUnchecked(code, - new DynamicTypeReference("CustomCompiledStringMapperInterface")); - assertDoesNotThrow(() -> { - Class customInterfaceClass = customClassLoader.loadClass("CustomCompiledStringMapperInterface"); - Object uppercaseMapperImpl = customInterfaceClass.cast(lambda); - Method mappingMethod = customInterfaceClass.getMethod("map", String.class); - assertEquals("ALA", mappingMethod.invoke(uppercaseMapperImpl, "ala")); - }); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaImplementingBinaryClassFileOnCustomClassPathContainingSpace(JavaCompiler jc) { - URL classPathDirectory = this.getClass().getClassLoader().getResource("binaryFileTests/with space/"); - URLClassLoader customClassLoader = new URLClassLoader(new URL[] { classPathDirectory }); - String classPathExtractedFromClassLoader = ClassPathExtractor.getUrlClassLoaderClassPath(customClassLoader); - LambdaFactory factory = LambdaFactory - .get(LambdaFactoryConfiguration.get().withCompilationClassPath(classPathExtractedFromClassLoader) - .withParentClassLoader(customClassLoader).withJavaCompiler(jc)); - String code = " x -> x.toUpperCase()"; - Object lambda = factory.createLambdaUnchecked(code, - new DynamicTypeReference("CustomCompiledStringMapperInterface")); - assertDoesNotThrow(() -> { - Class customInterfaceClass = customClassLoader.loadClass("CustomCompiledStringMapperInterface"); - Object uppercaseMapperImpl = customInterfaceClass.cast(lambda); - Method mappingMethod = customInterfaceClass.getMethod("map", String.class); - assertEquals("ALA", mappingMethod.invoke(uppercaseMapperImpl, "ala")); - }); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void lambdaImplementingNonStandardInterfaceUsingInnerClass(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get() - .withImports(CustomInterfaceUsingInnerClass.class, CustomInterfaceUsingInnerClass.InnerClass.class) - .withJavaCompiler(jc).withEnablePreview(true)); - String code = " () -> new InnerClass()"; - CustomInterfaceUsingInnerClass lambda = factory.createLambdaUnchecked(code, - new TypeReference() { - }); - assertEquals(CustomInterfaceUsingInnerClass.InnerClass.class, lambda.createInnerClass().getClass()); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void exceptionContainsCompilationDetailsWhenCompilationFails(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - - LambdaCreationException ex = assertThrows(LambdaCreationException.class, - () -> factory.createLambda(INCORRECT_CODE, new TypeReference>() { - })); - - assertTrue(ex.getCompilationDetails().isPresent()); - assertFalse(ex.getCompilationDetails().get().getDiagnostics().isEmpty()); - } - - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void runtimeExceptionContainsNestedCheckedException(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - - LambdaCreationRuntimeException ex = assertThrows(LambdaCreationRuntimeException.class, - () -> factory.createLambdaUnchecked(INCORRECT_CODE, new TypeReference>() { - })); - - assertNotNull(ex.getNestedCheckedException()); - } +class LambdaFactoryTest { - @ParameterizedTest - @MethodSource("jdkAndEclipse") - void emptyCodeFailsWithException(JavaCompiler jc) { - LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); - assertThrows(LambdaCreationRuntimeException.class, - () -> factory.createLambdaUnchecked("", new TypeReference>() { - })); - } + public static final String INCORRECT_CODE = "()->a"; + + static Stream jdkAndEclipse() { + int javaVersion = DefaultClassFactory.getJavaVersion(); + + if (javaVersion <= 8) { + return Stream.of(Arguments.of(JavaCompilerProvider.getJdkJavaCompiler().get()), + Arguments.of(new EclipseCompiler())); + } + + return Stream.of(Arguments.of(JavaCompilerProvider.getJdkJavaCompiler().get())); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void integerIncrement(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + Function lambda = factory.createLambdaUnchecked("i -> i+1", + new TypeReference>() { + }); + assertTrue(1 == lambda.apply(0)); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void integerMultiply(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + IntBinaryOperator lambda = factory.createLambdaUnchecked("(a,b) -> a*b", + new TypeReference() { + }); + assertEquals(1 * 2 * 3 * 4, IntStream.range(1, 5).reduce(lambda).getAsInt()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void integerToString(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + Function lambda = factory.createLambdaUnchecked("i -> \"ABC\"+i+\"DEF\"", + new TypeReference>() { + }); + assertEquals("ABC101DEF", lambda.apply(101)); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void useImportToAddBigDecimals(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory + .get(LambdaFactoryConfiguration.get().withImports(BigDecimal.class).withJavaCompiler(jc)); + assertDoesNotThrow(() -> { + BiFunction lambda = factory.createLambda("(a,b) -> a.add(b)", + new TypeReference>() { + }); + assertEquals(new BigDecimal("11"), lambda.apply(BigDecimal.ONE, BigDecimal.TEN)); + }); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void useStaticImportToIncrementBigDecimals(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withImports(BigDecimal.class) + .withStaticImports("java.math.BigDecimal.ONE").withJavaCompiler(jc)); + Function lambda = factory.createLambdaUnchecked("a -> a.add(ONE)", + new TypeReference>() { + }); + assertEquals(new BigDecimal("11"), lambda.apply(BigDecimal.TEN)); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void usingNoArgStringConstructorAsCode(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + Supplier lambda = factory.createLambdaUnchecked("String::new", new TypeReference>() { + }); + String string = lambda.get(); + assertEquals("", string); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void creatingIntegerInsteadOfLambda(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + Integer result = factory.createLambdaUnchecked("1+2", new TypeReference() { + }); + assertEquals(3, result.intValue()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaCreatingAnonymousClass(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + String code = "() -> new Object(){ public String toString(){return \"test\";}}"; + Supplier lambda = factory.createLambdaUnchecked(code, new TypeReference>() { + }); + Object object = lambda.get(); + assertEquals("test", object.toString()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaCreatingMapEntry(JavaCompiler jc) { + String code = "(str, num) -> new SimpleEntry(str, num)"; + LambdaFactory factory = LambdaFactory + .get(LambdaFactoryConfiguration.get().withImports(SimpleEntry.class).withJavaCompiler(jc)); + BiFunction> lambda = factory.createLambdaUnchecked(code, + new TypeReference>>() { + }); + SimpleEntry se = lambda.apply("a", 1L); + assertEquals(new SimpleEntry<>("a", 1L), se); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaCreatingComplicatedGenericType(JavaCompiler jc) { + String code = "() -> ( x -> new ArrayList<>())"; + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get() + .withImports(SimpleEntry.class, ArrayList.class).withJavaCompiler(jc).withEnablePreview(true)); + Supplier>>> lambda = factory + .createLambdaUnchecked(code, + new TypeReference>>>>() { + }); + Function>> function = lambda.get(); + assertEquals(new ArrayList>(), function.apply(null)); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void integerIncrementWithDynamicTypeReference(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + Object uncheckedLambda = factory.createLambdaUnchecked("i -> i+1", + new DynamicTypeReference("Function")); + @SuppressWarnings("unchecked") + Function lambda = (Function) uncheckedLambda; + assertTrue(1 == lambda.apply(0)); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void integerMultiplyWithDynamicTypeReference(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + IntBinaryOperator lambda = (IntBinaryOperator) factory.createLambdaUnchecked("(a,b) -> a*b", + new DynamicTypeReference("IntBinaryOperator")); + assertEquals(1 * 2 * 3 * 4, IntStream.range(1, 5).reduce(lambda).getAsInt()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaImplementingNonStandardInterface(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withImports(CustomInterface.class) + .withJavaCompiler(jc).withEnablePreview(true)); + String code = " x -> 10"; + CustomInterface lambda = factory.createLambdaUnchecked(code, new TypeReference() { + }); + assertEquals(lambda.customFunction("abc"), 10); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaImplementingBinaryClassFileOnCustomClassPath(JavaCompiler jc) { + URL classPathDirectory = this.getClass().getClassLoader().getResource("binaryFileTests/"); + URLClassLoader customClassLoader = new URLClassLoader(new URL[]{classPathDirectory}); + String classPathExtractedFromClassLoader = ClassPathExtractor.getUrlClassLoaderClassPath(customClassLoader); + LambdaFactory factory = LambdaFactory + .get(LambdaFactoryConfiguration.get().withCompilationClassPath(classPathExtractedFromClassLoader) + .withParentClassLoader(customClassLoader).withJavaCompiler(jc)); + String code = " x -> x.toUpperCase()"; + Object lambda = factory.createLambdaUnchecked(code, + new DynamicTypeReference("CustomCompiledStringMapperInterface")); + assertDoesNotThrow(() -> { + Class customInterfaceClass = customClassLoader.loadClass("CustomCompiledStringMapperInterface"); + Object uppercaseMapperImpl = customInterfaceClass.cast(lambda); + Method mappingMethod = customInterfaceClass.getMethod("map", String.class); + assertEquals("ALA", mappingMethod.invoke(uppercaseMapperImpl, "ala")); + }); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaImplementingBinaryClassFileOnCustomClassPathContainingSpace(JavaCompiler jc) { + URL classPathDirectory = this.getClass().getClassLoader().getResource("binaryFileTests/with space/"); + URLClassLoader customClassLoader = new URLClassLoader(new URL[]{classPathDirectory}); + String classPathExtractedFromClassLoader = ClassPathExtractor.getUrlClassLoaderClassPath(customClassLoader); + LambdaFactory factory = LambdaFactory + .get(LambdaFactoryConfiguration.get().withCompilationClassPath(classPathExtractedFromClassLoader) + .withParentClassLoader(customClassLoader).withJavaCompiler(jc)); + String code = " x -> x.toUpperCase()"; + Object lambda = factory.createLambdaUnchecked(code, + new DynamicTypeReference("CustomCompiledStringMapperInterface")); + assertDoesNotThrow(() -> { + Class customInterfaceClass = customClassLoader.loadClass("CustomCompiledStringMapperInterface"); + Object uppercaseMapperImpl = customInterfaceClass.cast(lambda); + Method mappingMethod = customInterfaceClass.getMethod("map", String.class); + assertEquals("ALA", mappingMethod.invoke(uppercaseMapperImpl, "ala")); + }); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void lambdaImplementingNonStandardInterfaceUsingInnerClass(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get() + .withImports(CustomInterfaceUsingInnerClass.class, CustomInterfaceUsingInnerClass.InnerClass.class) + .withJavaCompiler(jc).withEnablePreview(true)); + String code = " () -> new InnerClass()"; + CustomInterfaceUsingInnerClass lambda = factory.createLambdaUnchecked(code, + new TypeReference() { + }); + assertEquals(CustomInterfaceUsingInnerClass.InnerClass.class, lambda.createInnerClass().getClass()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void exceptionContainsCompilationDetailsWhenCompilationFails(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + + LambdaCreationException ex = assertThrows(LambdaCreationException.class, + () -> factory.createLambda(INCORRECT_CODE, new TypeReference>() { + })); + + assertTrue(ex.getCompilationDetails().isPresent()); + assertFalse(ex.getCompilationDetails().get().getDiagnostics().isEmpty()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void runtimeExceptionContainsNestedCheckedException(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + + LambdaCreationRuntimeException ex = assertThrows(LambdaCreationRuntimeException.class, + () -> factory.createLambdaUnchecked(INCORRECT_CODE, new TypeReference>() { + })); + + assertNotNull(ex.getNestedCheckedException()); + } + + @ParameterizedTest + @MethodSource("jdkAndEclipse") + void emptyCodeFailsWithException(JavaCompiler jc) { + LambdaFactory factory = LambdaFactory.get(LambdaFactoryConfiguration.get().withJavaCompiler(jc)); + assertThrows(LambdaCreationRuntimeException.class, + () -> factory.createLambdaUnchecked("", new TypeReference>() { + })); + } }