diff --git a/projo-runtime-code-generation/pom.xml b/projo-runtime-code-generation/pom.xml index e34eb15..098564d 100644 --- a/projo-runtime-code-generation/pom.xml +++ b/projo-runtime-code-generation/pom.xml @@ -53,10 +53,15 @@ 1 test + + jakarta.inject + jakarta.inject-api + 2.0.1 + com.google.inject guice - 4.2.2 + 6.0.0 test diff --git a/projo-runtime-code-generation/src/main/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandler.java b/projo-runtime-code-generation/src/main/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandler.java index 8ccf82e..8d2973f 100644 --- a/projo-runtime-code-generation/src/main/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandler.java +++ b/projo-runtime-code-generation/src/main/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandler.java @@ -1,5 +1,5 @@ // // -// Copyright 2017 - 2023 Mirko Raner // +// Copyright 2017 - 2024 Mirko Raner // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -126,8 +126,6 @@ public class RuntimeCodeGenerationHandler<_Artifact_> extends ProjoHandler<_Arti private Map, Class> implementationClassCache = new ConcurrentHashMap<>(); - private final String injected = "javax.inject.Inject"; - private final static String SUFFIX = "$Projo"; private static Map, Class> baseClasses = new HashMap<>(); @@ -320,7 +318,7 @@ private Builder<_Artifact_> add(Builder<_Artifact_> builder, Method method, List String methodName = annotations.get(Overrides.class).map(Overrides::value).orElse(method.getName()); String propertyName = matcher.propertyName(methodName); UnaryOperator> addFieldForGetter; - Optional inject = annotations.get(injected); + Optional inject = annotations.getInject(); Class returnType = method.getReturnType(); TypeDescription.Generic type = isGetter? getFieldType(annotations, returnType, classLoader):VOID.asGenericType(); addFieldForGetter = isGetter? localBuilder -> annotate(inject, localBuilder.defineField(propertyName, type, PRIVATE)):identity(); @@ -369,9 +367,10 @@ private Builder<_Artifact_> add(Builder<_Artifact_> builder, Method method, List Implementation getAccessor(Method method, AnnotationList annotations, Type returnType, String property, List additionalImplements, ClassLoader classLoader) { - if (annotations.contains(injected)) + Optional inject; + if ((inject = annotations.getInject()).isPresent()) { - return get(property, returnType, classLoader); + return get(property, returnType, inject.get(), classLoader); } if (annotations.contains(Cached.class)) { @@ -463,9 +462,9 @@ private Implementation cached(Cached cached, String field, Type type) } } - private Implementation get(String field, Type type, ClassLoader classLoader) + private Implementation get(String field, Type type, Annotation inject, ClassLoader classLoader) { - Class provider = Projo.forName("javax.inject.Provider", classLoader); + Class provider = Projo.forName(provider(inject), classLoader); Generic genericProvider = Generic.Builder.parameterizedType(provider, type).build(); MethodDescription get = latent(genericProvider.asErasure(), OBJECT.asGenericType(), "get"); return MethodCall.invoke(get).onField(field).withAssigner(DEFAULT, DYNAMIC); @@ -501,13 +500,14 @@ private ByteBuddy codeGenerator() **/ Generic getFieldType(AnnotationList annotations, Class originalReturnType, ClassLoader classLoader) { - if (!annotations.contains(injected) && !annotations.contains(Cached.class)) + if (!annotations.containsInject() && !annotations.contains(Cached.class)) { return Generic.Builder.rawType(originalReturnType).build(); } else { - Class container = annotations.contains(injected)? Projo.forName("javax.inject.Provider", classLoader):Cache.class; + Optional> optionalContainer = annotations.getInject().map(inject -> Projo.forName(provider(inject), classLoader)); + Class container = optionalContainer.orElse(Cache.class); Type wrappedType = MethodType.methodType(originalReturnType).wrap().returnType(); Optional returns = annotations.get(Returns.class); if (returns.isPresent()) @@ -535,6 +535,17 @@ Generic getFieldType(AnnotationList annotations, Class originalReturnType, Cl } } + /** + * Returns the corresponding provider annotation name for an inject annotation. + * + * @param inject the {@code @Inject} annotation + * @return the fully qualified name of the corresponding {@code @Provider} + **/ + private String provider(Annotation inject) + { + return inject.annotationType().getPackage().getName() + ".Provider"; + } + private <_ValuableBuilder_ extends Valuable<_Artifact_> & Builder<_Artifact_>> Builder<_Artifact_> annotate(Optional annotation, _ValuableBuilder_ builder) { diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/ProjoBooleanInjectionTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/ProjoBooleanInjectionTest.java index 33feb4b..9da92bd 100644 --- a/projo-runtime-code-generation/src/test/java/pro/projo/ProjoBooleanInjectionTest.java +++ b/projo-runtime-code-generation/src/test/java/pro/projo/ProjoBooleanInjectionTest.java @@ -1,5 +1,5 @@ // // -// Copyright 2019 Mirko Raner // +// Copyright 2019 - 2024 Mirko Raner // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -49,21 +49,25 @@ static interface Boolean static interface True extends Boolean, Booleans { + @Override default Boolean or(Boolean other) { return TRUE(); } + @Override default Boolean xor(Boolean other) { return other.not(); } + @Override default Boolean and(Boolean other) { return other; } + @Override default Boolean not() { return FALSE(); @@ -72,21 +76,25 @@ default Boolean not() static interface False extends Boolean, Booleans { + @Override default Boolean or(Boolean other) { return other; } + @Override default Boolean xor(Boolean other) { return other; } + @Override default Boolean and(Boolean other) { return FALSE(); } + @Override default Boolean not() { return TRUE(); @@ -118,6 +126,7 @@ static class TestUnary extends TestData this.expected = expected; } + @Override void test(Function converter) { Boolean object = converter.apply(left); @@ -140,6 +149,7 @@ static class TestBinary extends TestData this.expected = expected; } + @Override void test(Function converter) { Boolean object = converter.apply(left); @@ -180,6 +190,7 @@ public void test() throws Exception { Module module = new AbstractModule() { + @Override protected void configure() { bind(True.class).to(Projo.getImplementationClass(True.class)).asEagerSingleton(); diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/ProjoInjectionSupportTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/ProjoInjectionSupportTest.java index 827b081..c99180a 100644 --- a/projo-runtime-code-generation/src/test/java/pro/projo/ProjoInjectionSupportTest.java +++ b/projo-runtime-code-generation/src/test/java/pro/projo/ProjoInjectionSupportTest.java @@ -1,5 +1,5 @@ // // -// Copyright 2019 - 2022 Mirko Raner // +// Copyright 2019 - 2024 Mirko Raner // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -108,6 +108,7 @@ public void testThatPropertyAnnotationWorksInConjunctionWithInject() throws Exce { Module module = new AbstractModule() { + @Override protected void configure() { bind(True.class).to(Projo.getImplementationClass(True.class)).asEagerSingleton(); @@ -133,6 +134,7 @@ public void testThatReturnsAnnotationWorksInConjunctionWithInject() throws Excep Module module = new AbstractModule() { + @Override @SuppressWarnings("unchecked") protected void configure() { diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/ProjoNaturalTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/ProjoNaturalTest.java index 492e83d..b9abf32 100644 --- a/projo-runtime-code-generation/src/test/java/pro/projo/ProjoNaturalTest.java +++ b/projo-runtime-code-generation/src/test/java/pro/projo/ProjoNaturalTest.java @@ -1,5 +1,5 @@ // // -// Copyright 2022 Mirko Raner // +// Copyright 2022 - 2024 Mirko Raner // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -112,6 +112,7 @@ private Module module(Literals literals) { return new AbstractModule() { + @Override @SuppressWarnings("unchecked") protected void configure() { diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandlerTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandlerTest.java index 231e9d7..429a602 100644 --- a/projo-runtime-code-generation/src/test/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandlerTest.java +++ b/projo-runtime-code-generation/src/test/java/pro/projo/internal/rcg/RuntimeCodeGenerationHandlerTest.java @@ -1,5 +1,5 @@ // // -// Copyright 2017 - 2023 Mirko Raner // +// Copyright 2017 - 2024 Mirko Raner // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -15,6 +15,7 @@ // // package pro.projo.internal.rcg; +import java.lang.reflect.Method; import javax.inject.Inject; import org.junit.Test; import net.bytebuddy.description.type.TypeDescription.Generic; @@ -78,6 +79,17 @@ public void testFieldTypeForInjectedMethod() throws Exception assertEquals("javax.inject.Provider", fieldType.toString()); } + @Test + @jakarta.inject.Inject + public void testFieldTypeForInjectedMethodJakarta() throws Exception + { + RuntimeCodeGenerationHandler handler = new RuntimeCodeGenerationHandler<>(); + Method declaredMethod = getClass().getDeclaredMethod("testFieldTypeForInjectedMethodJakarta"); + jakarta.inject.Inject inject = declaredMethod.getAnnotation(jakarta.inject.Inject.class); + Generic fieldType = handler.getFieldType(new AnnotationList(inject), String.class, classLoader); + assertEquals("jakarta.inject.Provider", fieldType.toString()); + } + @Test @Cached public void testFieldTypeForCachedMethod() throws Exception diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoBooleanInjectionTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoBooleanInjectionTest.java new file mode 100644 index 0000000..a99d062 --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoBooleanInjectionTest.java @@ -0,0 +1,206 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.jakarta; + +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; +import jakarta.inject.Inject; +import pro.projo.Projo; +import static org.junit.Assert.assertEquals; + +/** +* {@link ProjoBooleanInjectionTest} tests Projo's code generation for injected fields for a +* bootstrapped Boolean algebra that requires circular dependencies. +* +* @author Mirko Raner +**/ +@RunWith(Parameterized.class) +public class ProjoBooleanInjectionTest +{ + static interface Boolean + { + Boolean or(Boolean other); + Boolean xor(Boolean other); + Boolean and(Boolean other); + Boolean not(); + } + + static interface True extends Boolean, Booleans + { + @Override + default Boolean or(Boolean other) + { + return TRUE(); + } + + @Override + default Boolean xor(Boolean other) + { + return other.not(); + } + + @Override + default Boolean and(Boolean other) + { + return other; + } + + @Override + default Boolean not() + { + return FALSE(); + } + } + + static interface False extends Boolean, Booleans + { + @Override + default Boolean or(Boolean other) + { + return other; + } + + @Override + default Boolean xor(Boolean other) + { + return other; + } + + @Override + default Boolean and(Boolean other) + { + return FALSE(); + } + + @Override + default Boolean not() + { + return TRUE(); + } + } + + static interface Booleans + { + @Inject True TRUE(); + @Inject False FALSE(); + } + + static abstract class TestData + { + boolean left; + boolean expected; + + abstract void test(Function converter); + } + + static class TestUnary extends TestData + { + UnaryOperator operator; + + TestUnary(UnaryOperator operator, boolean left, boolean expected) + { + this.operator = operator; + this.left = left; + this.expected = expected; + } + + @Override + void test(Function converter) + { + Boolean object = converter.apply(left); + Boolean result = operator.apply(object); + Boolean expected = converter.apply(this.expected); + assertEquals(expected, result); + } + } + + static class TestBinary extends TestData + { + BinaryOperator operator; + boolean right; + + TestBinary(BinaryOperator operator, boolean left, boolean right, boolean expected) + { + this.operator = operator; + this.left = left; + this.right = right; + this.expected = expected; + } + + @Override + void test(Function converter) + { + Boolean object = converter.apply(left); + Boolean other = converter.apply(right); + Boolean result = operator.apply(object, other); + Boolean expected = converter.apply(this.expected); + assertEquals(expected, result); + } + } + + @Parameters + public static TestData[] getTestData() + { + return new TestData[] + { + new TestUnary(Boolean::not, false, true), + new TestUnary(Boolean::not, true, false), + new TestBinary(Boolean::and, false, false, false), + new TestBinary(Boolean::and, true, false, false), + new TestBinary(Boolean::and, false, true, false), + new TestBinary(Boolean::and, true, true, true), + new TestBinary(Boolean::xor, false, false, false), + new TestBinary(Boolean::xor, true, false, true), + new TestBinary(Boolean::xor, false, true, true), + new TestBinary(Boolean::xor, true, true, false), + new TestBinary(Boolean::or, false, false, false), + new TestBinary(Boolean::or, true, false, true), + new TestBinary(Boolean::or, false, true, true), + new TestBinary(Boolean::or, true, true, true) + }; + } + + @Parameter + public TestData test; + + @Test + public void test() throws Exception + { + Module module = new AbstractModule() + { + @Override + protected void configure() + { + bind(True.class).to(Projo.getImplementationClass(True.class)).asEagerSingleton(); + bind(False.class).to(Projo.getImplementationClass(False.class)).asEagerSingleton(); + bind(Booleans.class).to(Projo.getImplementationClass(Booleans.class)).asEagerSingleton(); + } + }; + Injector injector = Guice.createInjector(module); + Booleans booleans = injector.getInstance(Booleans.class); + test.test(value -> value? booleans.TRUE():booleans.FALSE()); + } +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoInjectionSupportTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoInjectionSupportTest.java new file mode 100644 index 0000000..12838de --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoInjectionSupportTest.java @@ -0,0 +1,151 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.jakarta; + +import java.awt.Point; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.junit.Test; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.TypeLiteral; +import pro.projo.Projo; +import pro.projo.annotations.Property; +import pro.projo.test.implementations.IntegerProcessor; +import pro.projo.test.implementations.jakarta.PointProcessor; +import pro.projo.test.interfaces.Processor; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class ProjoInjectionSupportTest +{ + static interface True + { + // + } + + static interface False + { + // + } + + static interface Booleans + { + @Inject True _true(); + @Inject False _false(); + } + + static interface Pass + { + @Inject + @Property + default Booleans booleans() + { + throw new NoSuchMethodError(); + } + + default True evaluate() + { + return booleans()._true(); + } + } + + @Test + public void testInjectAnnotationCarriesOverToGeneratedFields() throws Exception + { + Class booleans = Projo.getImplementationClass(Booleans.class); + Field trueField = booleans.getDeclaredField("_true"); + Field falseField = booleans.getDeclaredField("_false"); + Inject[] trueInject = trueField.getAnnotationsByType(Inject.class); + Inject[] falseInject = falseField.getAnnotationsByType(Inject.class); + int[] expected = {1, 1}; + int[] actual = {trueInject.length, falseInject.length}; + assertArrayEquals(expected, actual); + } + + @Test + public void testThatInjectedFieldsAreAlwaysUsingAProvider() throws Exception + { + Class booleans = Projo.getImplementationClass(Booleans.class); + Field trueField = booleans.getDeclaredField("_true"); + Field falseField = booleans.getDeclaredField("_false"); + Type trueType = trueField.getGenericType(); + Type falseType = falseField.getGenericType(); + class Expected + { + @SuppressWarnings("unused") Provider trueExpected; + @SuppressWarnings("unused") Provider falseExpected; + } + Type[] expected = + { + Expected.class.getDeclaredField("trueExpected").getGenericType(), + Expected.class.getDeclaredField("falseExpected").getGenericType() + }; + Type[] actual = {trueType, falseType}; + assertArrayEquals(expected, actual); + } + + @Test + public void testThatPropertyAnnotationWorksInConjunctionWithInject() throws Exception + { + Module module = new AbstractModule() + { + @Override + protected void configure() + { + bind(True.class).to(Projo.getImplementationClass(True.class)).asEagerSingleton(); + bind(False.class).to(Projo.getImplementationClass(False.class)).asEagerSingleton(); + bind(Booleans.class).to(Projo.getImplementationClass(Booleans.class)).asEagerSingleton(); + bind(Pass.class).to(Projo.getImplementationClass(Pass.class)).asEagerSingleton(); + } + }; + Injector injector = Guice.createInjector(module); + Booleans booleans = injector.getInstance(Booleans.class); + Pass pass = injector.getInstance(Pass.class); + assertSame(booleans._true(), pass.evaluate()); + } + + @Test + public void testThatReturnsAnnotationWorksInConjunctionWithInject() throws Exception + { + @SuppressWarnings("rawtypes") + TypeLiteral pointProcessor = new TypeLiteral>() {}; + + @SuppressWarnings("rawtypes") + TypeLiteral integerProcessor = new TypeLiteral>() {}; + + Module module = new AbstractModule() + { + @Override + @SuppressWarnings("unchecked") + protected void configure() + { + bind((TypeLiteral)pointProcessor).to(Projo.getImplementationClass(PointProcessor.class)); + bind((TypeLiteral)integerProcessor).to(Projo.getImplementationClass(IntegerProcessor.class)); + } + }; + Injector injector = Guice.createInjector(module); + Processor processor = injector.getInstance(Key.get(new TypeLiteral>() {})); + Object result = processor.process(new Point(3, 14159265)); + assertEquals("3, 14159265", result); + } +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoProviderTest.java b/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoProviderTest.java new file mode 100644 index 0000000..d303e4c --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/jakarta/ProjoProviderTest.java @@ -0,0 +1,74 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.jakarta; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import org.junit.Test; +import pro.projo.Projo; +import pro.projo.test.implementations.jakarta.Provider; +import pro.projo.test.implementations.jakarta.Strings; +import static org.junit.Assert.assertEquals; + +public class ProjoProviderTest +{ + Class provider = Projo.getImplementationClass(Provider.class); + Class strings = Projo.getImplementationClass(Strings.class); + + @Test + public void testLiteralsFieldType() throws Exception + { + Field literals = provider.getDeclaredField("literals"); + ParameterizedType literalsType = (ParameterizedType)literals.getGenericType(); + assertEquals("jakarta.inject.Provider", literalsType.getRawType().getTypeName()); + assertEquals("pro.projo.test.interfaces.Literals", literalsType.getActualTypeArguments()[0].getTypeName()); + } + + @Test + public void testLiteralsReturnType() throws Exception + { + Method literals = provider.getDeclaredMethod("literals"); + Type literalsType = literals.getGenericReturnType(); + assertEquals("pro.projo.test.interfaces.Literals", literalsType.getTypeName()); + } + + @Test + public void testComparatorFieldType() throws Exception + { + Field literals = provider.getDeclaredField("comparator"); + ParameterizedType literalsType = (ParameterizedType)literals.getGenericType(); + assertEquals("jakarta.inject.Provider>", literalsType.getTypeName()); + } + + @Test + public void testInheritedLiteralsFieldType() throws Exception + { + Field literals = strings.getDeclaredField("literals"); + ParameterizedType literalsType = (ParameterizedType)literals.getGenericType(); + assertEquals("jakarta.inject.Provider", literalsType.getRawType().getTypeName()); + assertEquals("pro.projo.test.interfaces.Literals", literalsType.getActualTypeArguments()[0].getTypeName()); + } + + @Test + public void testInheritedLiteralsReturnType() throws Exception + { + Method literals = strings.getDeclaredMethod("literals"); + Type literalsType = literals.getGenericReturnType(); + assertEquals("pro.projo.test.interfaces.Literals", literalsType.getTypeName()); + } +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/PointProcessor.java b/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/PointProcessor.java new file mode 100644 index 0000000..10fb72e --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/PointProcessor.java @@ -0,0 +1,39 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.test.implementations.jakarta; + +import java.awt.Point; +import jakarta.inject.Inject; +import pro.projo.annotations.Expects; +import pro.projo.annotations.Implements; +import pro.projo.annotations.Returns; +import pro.projo.test.implementations.IntegerProcessor; + +@Implements("pro.projo.test.interfaces.Processor") +public interface PointProcessor +{ + @Inject + @Returns("pro.projo.test.interfaces.Processor") + IntegerProcessor integerProcessor(); + + @Returns("java.lang.String") + default String process(@Expects("java.lang.Object") Object item) + { + Point point = (Point)item; + return integerProcessor().process(Integer.valueOf(point.x)) + ", " + + integerProcessor().process(Integer.valueOf(point.y)); + } +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/Provider.java b/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/Provider.java new file mode 100644 index 0000000..b2915a0 --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/Provider.java @@ -0,0 +1,31 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.test.implementations.jakarta; + +import jakarta.inject.Inject; +import pro.projo.annotations.Returns; + +/** +* {@link pro.projo.test.implementations.jakarta.Provider} is a test interface +* that is used by {@link pro.projo.ProjoProviderTest}. +* +* @author Mirko Raner +**/ +public interface Provider +{ + @Inject @Returns("pro.projo.test.interfaces.Literals") Object literals(); + @Inject @Returns("java.util.Comparator") Object comparator(); +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/Strings.java b/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/Strings.java new file mode 100644 index 0000000..2938016 --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/test/implementations/jakarta/Strings.java @@ -0,0 +1,24 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.test.implementations.jakarta; + +/** +* {@link pro.projo.test.implementations.jakarta.Strings} is a test interface +* that is used by {@link pro.projo.ProjoProviderTest}. +* +* @author Mirko Raner +**/ +public interface Strings extends Provider {} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Natural.java b/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Natural.java new file mode 100644 index 0000000..c3de652 --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Natural.java @@ -0,0 +1,38 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.test.interfaces.jakarta; + +import jakarta.inject.Inject; +import pro.projo.annotations.Property; +import pro.projo.test.implementations.Ranges; + +public interface Natural<$ extends Natural<$>> +{ + Natural successor(); + + Natural plus(Natural other); + + Natural times(Natural other); + + boolean equals(Natural other); + + @Inject + @Property + default Ranges ranges() + { + throw new NoSuchMethodError("ranges() should have been implemented"); + } +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Naturals.java b/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Naturals.java new file mode 100644 index 0000000..063193d --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Naturals.java @@ -0,0 +1,32 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.test.interfaces.jakarta; + +import jakarta.inject.Inject; +import pro.projo.test.implementations.Natural; +import pro.projo.test.implementations.Utilities; + +public interface Naturals<$ extends Naturals<$>> extends Provider +{ + @Inject + Utilities utilities(); + + @Override + public default Natural parse(Object literal) + { + return utilities().natural(String.valueOf(literal)); + } +} diff --git a/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Provider.java b/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Provider.java new file mode 100644 index 0000000..d6dea02 --- /dev/null +++ b/projo-runtime-code-generation/src/test/java/pro/projo/test/interfaces/jakarta/Provider.java @@ -0,0 +1,29 @@ +// // +// Copyright 2024 Mirko Raner // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +package pro.projo.test.interfaces.jakarta; + +import jakarta.inject.Inject; +import pro.projo.test.interfaces.Literals; + +public interface Provider +{ + @Inject + Literals literals(); + + String version(); + + TYPE parse(Object object); // <== must not be implemented, because @Implemented type provides implementation +} diff --git a/projo/src/main/java/pro/projo/utilities/AnnotationList.java b/projo/src/main/java/pro/projo/utilities/AnnotationList.java index b5ec380..6bc9df0 100644 --- a/projo/src/main/java/pro/projo/utilities/AnnotationList.java +++ b/projo/src/main/java/pro/projo/utilities/AnnotationList.java @@ -1,5 +1,5 @@ // // -// Copyright 2022 Mirko Raner // +// Copyright 2022 - 2024 Mirko Raner // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -23,7 +23,8 @@ /** * An {@link AnnotationList} provides easy access to the annotations of an -* {@link AnnotatedElement}. +* {@link AnnotatedElement}. It also provides convenience methods for handling +* JSR-330 and Jakarta dependency injection annotations. * * @author Mirko Raner **/ @@ -31,6 +32,9 @@ public class AnnotationList extends ArrayList { private final static long serialVersionUID = 7598992303415745213L; + private final static String javaxInject = "javax.inject.Inject"; + private final static String jakartaInject = "jakarta.inject.Inject"; + public AnnotationList(AnnotatedElement element) { this(element.getAnnotations()); @@ -51,6 +55,22 @@ public boolean contains(String annotationName) return stream().anyMatch(it -> it.annotationType().getName().equals(annotationName)); } + public boolean containsInject() + { + return stream().anyMatch(this::isInject); + } + + public Optional getInject() + { + return stream().filter(this::isInject).findFirst(); + } + + public boolean isInject(Annotation annotation) + { + String name = annotation.annotationType().getName(); + return name.equals(javaxInject) || name.equals(jakartaInject); + } + @SuppressWarnings("unchecked") public Optional get(Class annotation) {