diff --git a/core/pom.xml b/core/pom.xml index d02ec7524..71a29c955 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -116,6 +116,12 @@ ${jpa-streamer.version} + + com.speedment.jpastreamer + termopmodifier-standard + ${jpa-streamer.version} + + com.speedment.jpastreamer announcer diff --git a/fieldgenerator/fieldgenerator-standard/src/main/java/com/speedment/jpastreamer/fieldgenerator/internal/InternalFieldGeneratorProcessor.java b/fieldgenerator/fieldgenerator-standard/src/main/java/com/speedment/jpastreamer/fieldgenerator/internal/InternalFieldGeneratorProcessor.java index 5b22c946f..89fd62e88 100644 --- a/fieldgenerator/fieldgenerator-standard/src/main/java/com/speedment/jpastreamer/fieldgenerator/internal/InternalFieldGeneratorProcessor.java +++ b/fieldgenerator/fieldgenerator-standard/src/main/java/com/speedment/jpastreamer/fieldgenerator/internal/InternalFieldGeneratorProcessor.java @@ -95,11 +95,19 @@ public boolean process(Set annotations, RoundEnvironment return false; } - roundEnv.getElementsAnnotatedWith(Entity.class).stream() + Set entities = roundEnv.getElementsAnnotatedWith(Entity.class); + + if (entities.isEmpty()) { + System.out.format("[JPAStreamer Field Generator Processor] Found no classes annotated with jakarta.persistence.Entity.\n"); + return true; + } + + entities.stream() .filter(ae -> ae.getKind() == ElementKind.CLASS) .forEach(ae -> { try { final String entityName = ae.asType().toString(); + System.out.format("[JPAStreamer Field Generator Processor] Generating class for: %s\n", entityName); final String shortEntityName = shortName(entityName); final String prefix = processingEnv.getOptions().getOrDefault("jpaStreamerPrefix", ""); diff --git a/pipeline/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/IntermediateOperationFactory.java b/pipeline/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/IntermediateOperationFactory.java index c12aa5483..0fdf4173a 100644 --- a/pipeline/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/IntermediateOperationFactory.java +++ b/pipeline/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/IntermediateOperationFactory.java @@ -12,6 +12,8 @@ */ package com.speedment.jpastreamer.pipeline.intermediate; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperation; + import java.util.Comparator; import java.util.function.*; import java.util.stream.DoubleStream; @@ -22,8 +24,7 @@ public interface IntermediateOperationFactory { IntermediateOperation, Stream> createFilter(Predicate predicate); - - + IntermediateOperation, Stream> createMap(Function mapper); IntermediateOperation, IntStream> createMapToInt(ToIntFunction mapper); diff --git a/provider/pipeline-standard/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/standard/internal/InternalIntermediateOperationFactory.java b/provider/pipeline-standard/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/standard/internal/InternalIntermediateOperationFactory.java index 964e0a1fe..8e5b0f37c 100644 --- a/provider/pipeline-standard/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/standard/internal/InternalIntermediateOperationFactory.java +++ b/provider/pipeline-standard/src/main/java/com/speedment/jpastreamer/pipeline/intermediate/standard/internal/InternalIntermediateOperationFactory.java @@ -15,6 +15,7 @@ import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationType; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperation; import java.util.Comparator; import java.util.function.*; @@ -51,7 +52,6 @@ public IntermediateOperation, Stream> createFilter(final Predic Stream.class, function, predicate); - } @Override diff --git a/provider/renderer-standard/pom.xml b/provider/renderer-standard/pom.xml index 49ec2b861..f32d4fd54 100644 --- a/provider/renderer-standard/pom.xml +++ b/provider/renderer-standard/pom.xml @@ -60,19 +60,23 @@ com.speedment.jpastreamer interopoptimizer - + com.speedment.jpastreamer rootfactory + + com.speedment.jpastreamer + termopmodifier + + jakarta.persistence jakarta.persistence-api ${jakarta.version} - diff --git a/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java b/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java index 5b1eee121..707121d81 100644 --- a/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java +++ b/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java @@ -29,6 +29,7 @@ import com.speedment.jpastreamer.rootfactory.RootFactory; import com.speedment.jpastreamer.streamconfiguration.StreamConfiguration; +import com.speedment.jpastreamer.termopmodifier.TerminalOperationModifierFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.TypedQuery; @@ -48,6 +49,7 @@ final class StandardRenderer implements Renderer { private final CriteriaFactory criteriaFactory; private final IntermediateOperationOptimizerFactory intermediateOperationOptimizerFactory; + private final TerminalOperationModifierFactory terminalOperationModifierFactory; private final MergerFactory mergerFactory; @@ -59,6 +61,7 @@ final class StandardRenderer implements Renderer { this.entityManager = requireNonNull(entityManagerSupplier).get(); this.criteriaFactory = RootFactory.getOrThrow(CriteriaFactory.class, ServiceLoader::load); this.intermediateOperationOptimizerFactory = RootFactory.getOrThrow(IntermediateOperationOptimizerFactory.class, ServiceLoader::load); + this.terminalOperationModifierFactory = RootFactory.getOrThrow(TerminalOperationModifierFactory.class, ServiceLoader::load); this.mergerFactory = RootFactory.getOrThrow(MergerFactory.class, ServiceLoader::load); } @@ -66,12 +69,14 @@ final class StandardRenderer implements Renderer { this.entityManager = entityManager; this.criteriaFactory = RootFactory.getOrThrow(CriteriaFactory.class, ServiceLoader::load); this.intermediateOperationOptimizerFactory = RootFactory.getOrThrow(IntermediateOperationOptimizerFactory.class, ServiceLoader::load); + this.terminalOperationModifierFactory = RootFactory.getOrThrow(TerminalOperationModifierFactory.class, ServiceLoader::load); this.mergerFactory = RootFactory.getOrThrow(MergerFactory.class, ServiceLoader::load); } @Override @SuppressWarnings("unchecked") public > RenderResult render(final Pipeline pipeline, final StreamConfiguration streamConfiguration) { + modifyPipeline(pipeline); optimizePipeline(pipeline); final Class entityClass = pipeline.root(); @@ -180,7 +185,11 @@ private > S replay(final Stream stream, fina return decorated; */ } - + + private void modifyPipeline(final Pipeline pipeline) { + terminalOperationModifierFactory.get().modify(pipeline); + } + private void optimizePipeline(final Pipeline pipeline) { intermediateOperationOptimizerFactory.stream().forEach(intermediateOperationOptimizer -> intermediateOperationOptimizer.optimize(pipeline)); } diff --git a/provider/renderer-standard/src/main/java/module-info.java b/provider/renderer-standard/src/main/java/module-info.java index f73e330e7..3e216b7d8 100644 --- a/provider/renderer-standard/src/main/java/module-info.java +++ b/provider/renderer-standard/src/main/java/module-info.java @@ -25,6 +25,7 @@ requires jpastreamer.criteria; requires jpastreamer.merger; requires jpastreamer.interopoptimizer; + requires jpastreamer.termopmodifier; uses CriteriaFactory; uses MergerFactory; diff --git a/provider/termopmodifier-standard/pom.xml b/provider/termopmodifier-standard/pom.xml index 18e42946b..dab9ffb19 100644 --- a/provider/termopmodifier-standard/pom.xml +++ b/provider/termopmodifier-standard/pom.xml @@ -35,10 +35,28 @@ + com.speedment.jpastreamer termopmodifier + + + com.speedment.jpastreamer + rootfactory + + + + com.speedment.jpastreamer + pipeline-standard + test + + + + com.speedment.jpastreamer + field + + diff --git a/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/InternalTerminalOperatorModifierFactory.java b/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/InternalTerminalOperatorModifierFactory.java index 6d29e34cc..c5a664602 100644 --- a/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/InternalTerminalOperatorModifierFactory.java +++ b/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/InternalTerminalOperatorModifierFactory.java @@ -23,4 +23,5 @@ public final class InternalTerminalOperatorModifierFactory implements TerminalOp public TerminalOperationModifier get() { return singleton; } + } diff --git a/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperatorModifier.java b/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperatorModifier.java index 73fab24c6..49b979ee1 100644 --- a/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperatorModifier.java +++ b/provider/termopmodifier-standard/src/main/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperatorModifier.java @@ -12,17 +12,93 @@ */ package com.speedment.jpastreamer.termopmodifier.standard.internal; +import com.speedment.jpastreamer.field.predicate.SpeedmentPredicate; import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperation; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationType; +import com.speedment.jpastreamer.rootfactory.RootFactory; import com.speedment.jpastreamer.termopmodifier.TerminalOperationModifier; +import java.util.Optional; +import java.util.ServiceLoader; + import static java.util.Objects.requireNonNull; final class StandardTerminalOperatorModifier implements TerminalOperationModifier { + private final IntermediateOperationFactory intermediateOperationFactory; + private final TerminalOperationFactory terminalOperationFactory; + + StandardTerminalOperatorModifier() { + this.intermediateOperationFactory = RootFactory.getOrThrow(IntermediateOperationFactory.class, ServiceLoader::load); + this.terminalOperationFactory = RootFactory.getOrThrow(TerminalOperationFactory.class, ServiceLoader::load); + } + @Override public Pipeline modify(Pipeline pipeline) { requireNonNull(pipeline); - // For now, just return whatever we get. + + final TerminalOperationType terminalOperationType = pipeline.terminatingOperation().type(); + + switch (terminalOperationType) { + case ANY_MATCH: + return modifyAnyMatch(pipeline); + case NONE_MATCH: + return modifyNoneMatch(pipeline); + case FIND_FIRST: + return modifyFindFirst(pipeline); + case FIND_ANY: + return modifyFindAny(pipeline); + default: + return pipeline; + } + } + + private Pipeline modifyAnyMatch(Pipeline pipeline) { + this.getPredicate(pipeline.terminatingOperation()).ifPresent(speedmentPredicate -> { + pipeline.intermediateOperations().add(intermediateOperationFactory.createFilter(speedmentPredicate)); + pipeline.intermediateOperations().add(intermediateOperationFactory.createLimit(1)); + pipeline.terminatingOperation(terminalOperationFactory.createAnyMatch(p -> true)); + }); return pipeline; } + + private Pipeline modifyNoneMatch(Pipeline pipeline) { + this.getPredicate(pipeline.terminatingOperation()).ifPresent(speedmentPredicate -> { + pipeline.intermediateOperations().add(intermediateOperationFactory.createFilter(speedmentPredicate)); + pipeline.intermediateOperations().add(intermediateOperationFactory.createLimit(1)); + // NoneMatch() - If the stream is empty then true is returned and the predicate is not evaluated. + // If the expression is evaluated => There is a match and the expression is always false. + pipeline.terminatingOperation(terminalOperationFactory.createNoneMatch(e -> true)); + }); + return pipeline; + } + + private Pipeline modifyFindFirst(Pipeline pipeline) { + pipeline.intermediateOperations().add(intermediateOperationFactory.createLimit(1)); + return pipeline; + } + + private Pipeline modifyFindAny(Pipeline pipeline) { + pipeline.ordered(false); + pipeline.intermediateOperations().add(intermediateOperationFactory.createLimit(1)); + return pipeline; + } + + private Optional> getPredicate(final TerminalOperation operation) { + final Object[] arguments = operation.arguments(); + + if (arguments.length != 1) { + return Optional.empty(); + } + + if (arguments[0] instanceof SpeedmentPredicate) { + return Optional.of((SpeedmentPredicate) arguments[0]); + } + + return Optional.empty(); + } + } diff --git a/provider/termopmodifier-standard/src/main/java/module-info.java b/provider/termopmodifier-standard/src/main/java/module-info.java index 2d7152eb6..1e6961634 100644 --- a/provider/termopmodifier-standard/src/main/java/module-info.java +++ b/provider/termopmodifier-standard/src/main/java/module-info.java @@ -1,3 +1,6 @@ +import com.speedment.jpastreamer.pipeline.PipelineFactory; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationFactory; import com.speedment.jpastreamer.termopmodifier.TerminalOperationModifierFactory; import com.speedment.jpastreamer.termopmodifier.standard.StandardTerminalOperatorModifierFactory; @@ -15,8 +18,15 @@ */ module jpastreamer.termopmodifier.standard { requires transitive jpastreamer.termopmodifier; + requires jpastreamer.rootfactory; + requires jpastreamer.pipeline; + requires jpastreamer.field; exports com.speedment.jpastreamer.termopmodifier.standard; + uses PipelineFactory; + uses TerminalOperationFactory; + uses IntermediateOperationFactory; + provides TerminalOperationModifierFactory with StandardTerminalOperatorModifierFactory; } diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/StandardTerminalOperationModifierFactoryTest.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/StandardTerminalOperationModifierFactoryTest.java new file mode 100644 index 000000000..11a65650a --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/StandardTerminalOperationModifierFactoryTest.java @@ -0,0 +1,16 @@ +package com.speedment.jpastreamer.termopmodifier.standard; + +import com.speedment.jpastreamer.termopmodifier.TerminalOperationModifierFactory; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class StandardTerminalOperationModifierFactoryTest { + + @Test + void get() { + final TerminalOperationModifierFactory terminalOperationModifierFactory = new StandardTerminalOperatorModifierFactory(); + assertNotNull(terminalOperationModifierFactory.get()); + } + +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/AnyMatchTest.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/AnyMatchTest.java new file mode 100644 index 000000000..ef87937a9 --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/AnyMatchTest.java @@ -0,0 +1,75 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal; + +import com.speedment.jpastreamer.field.predicate.SpeedmentPredicate; +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film$; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class AnyMatchTest extends StandardTerminalOperationModifierTest { + + @Override + Class getEntityClass() { + return Film.class; + } + + @Override + protected Stream> pipelines() { + return Stream.of( + noAnyMatch(), + anyMatchLambda(), + anyMatchSpeedmentPredicate() + ); + } + + private PipelineTestCase noAnyMatch() { + final Pipeline noAnyMatch = createPipeline( + tof.createAllMatch(s -> s.equals("test")), + iof.createLimit(100) + ); + + final Pipeline noAnyMatchExpected = createPipeline( + tof.createAllMatch(s -> s.equals("test")), + iof.createLimit(100) + ); + + return new PipelineTestCase<>("No Any Match", noAnyMatch, noAnyMatchExpected); + } + + private PipelineTestCase anyMatchLambda() { + final Predicate p = f -> f.getTitle().startsWith("A"); + + final Pipeline anyMatch = createPipeline( + tof.createAnyMatch(p), + iof.createLimit(100) + ); + + final Pipeline anyMatchExpected = createPipeline( + tof.createAnyMatch(s -> true), + iof.createLimit(100) + ); + + return new PipelineTestCase<>("Any Match Lambda", anyMatch, anyMatchExpected); + } + + private PipelineTestCase anyMatchSpeedmentPredicate() { + SpeedmentPredicate predicate = Film$.title.startsWith("A"); + + final Pipeline anyMatch = createPipeline( + tof.createAnyMatch(predicate), + iof.createLimit(100) + ); + + final Pipeline anyMatchExpected = createPipeline( + tof.createAnyMatch(s -> true), + iof.createLimit(100), + iof.createFilter(predicate), + iof.createLimit(1) + ); + + return new PipelineTestCase<>("Any Match Speedment Predicate", anyMatch, anyMatchExpected); + } + +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/FindAnyTest.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/FindAnyTest.java new file mode 100644 index 000000000..22772a85a --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/FindAnyTest.java @@ -0,0 +1,57 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal; + +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film$; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class FindAnyTest extends StandardTerminalOperationModifierTest { + + @Override + Class getEntityClass() { + return Film.class; + } + + @Override + protected Stream> pipelines() { + return Stream.of( + noFindAny(), + findAny() + ); + } + + private PipelineTestCase noFindAny() { + final Pipeline noFindAny = createPipeline( + tof.createAllMatch(Film$.title.startsWith("A")), + iof.createLimit(100) + ); + + final Pipeline noFindAnyExpected = createPipeline( + tof.createAllMatch(Film$.title.startsWith("A")), + iof.createLimit(100) + ); + + return new PipelineTestCase<>("No Find Any", noFindAny, noFindAnyExpected); + } + + private PipelineTestCase findAny() { + final Predicate p = f -> f.getTitle().startsWith("A"); + + final Pipeline findAny = createPipeline( + tof.acquireFindAny(), + iof.createFilter(p), + iof.createLimit(100) + ); + + final Pipeline findAnyExpected = createPipeline( + tof.acquireFindAny(), + iof.createFilter(p), + iof.createLimit(100), + iof.createLimit(1) + ); + + return new PipelineTestCase<>("Find Any", findAny, findAnyExpected); + } +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/FindFirstTest.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/FindFirstTest.java new file mode 100644 index 000000000..2fb273414 --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/FindFirstTest.java @@ -0,0 +1,58 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal; + +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film$; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class FindFirstTest extends StandardTerminalOperationModifierTest { + + @Override + Class getEntityClass() { + return Film.class; + } + + @Override + protected Stream> pipelines() { + return Stream.of( + noFindFirst(), + findFirst() + ); + } + + private PipelineTestCase noFindFirst() { + final Pipeline noFindFirst = createPipeline( + tof.createAllMatch(Film$.title.startsWith("A")), + iof.createLimit(100) + ); + + final Pipeline noFindFirstExpected = createPipeline( + tof.createAllMatch(Film$.title.startsWith("A")), + iof.createLimit(100) + ); + + return new PipelineTestCase<>("No Find First", noFindFirst, noFindFirstExpected); + } + + private PipelineTestCase findFirst() { + final Predicate p = f -> f.getTitle().startsWith("A"); + + final Pipeline findFirst = createPipeline( + tof.acquireFindFirst(), + iof.createFilter(p), + iof.createLimit(100) + ); + + final Pipeline findFirstExpected = createPipeline( + tof.acquireFindFirst(), + iof.createFilter(p), + iof.createLimit(100), + iof.createLimit(1) + ); + + return new PipelineTestCase<>("Find First", findFirst, findFirstExpected); + } + +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/NoneMatchTest.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/NoneMatchTest.java new file mode 100644 index 000000000..87f29aa84 --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/NoneMatchTest.java @@ -0,0 +1,78 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal; + + +import com.speedment.jpastreamer.field.predicate.SpeedmentPredicate; +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film; +import com.speedment.jpastreamer.termopmodifier.standard.internal.model.Film$; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +public class NoneMatchTest extends StandardTerminalOperationModifierTest{ + + @Override + Class getEntityClass() { + return Film.class; + } + + @Override + protected Stream> pipelines() { + return Stream.of( + noNoneMatch(), + noneMatchLambda(), + noneMatchSpeedmentPredicate() + ); + } + + private PipelineTestCase noNoneMatch() { + final Predicate p = f -> f.getTitle().startsWith("A"); + + final Pipeline noAnyMatch = createPipeline( + tof.createAllMatch(p), + iof.createLimit(100) + ); + + final Pipeline noAnyMatchExpected = createPipeline( + tof.createAllMatch(p), + iof.createLimit(100) + ); + + return new PipelineTestCase<>("No None Match", noAnyMatch, noAnyMatchExpected); + } + + private PipelineTestCase noneMatchLambda() { + final Predicate p = f -> f.getTitle().startsWith("A"); + + final Pipeline anyMatch = createPipeline( + tof.createNoneMatch(p), + iof.createLimit(100) + ); + + final Pipeline anyMatchExpected = createPipeline( + tof.createNoneMatch(p), + iof.createLimit(100) + ); + + return new PipelineTestCase<>("None Match Lambda", anyMatch, anyMatchExpected); + } + + private PipelineTestCase noneMatchSpeedmentPredicate() { + SpeedmentPredicate p = Film$.title.startsWith("A"); + + final Pipeline anyMatch = createPipeline( + tof.createNoneMatch(p), + iof.createLimit(100) + ); + + final Pipeline anyMatchExpected = createPipeline( + tof.createNoneMatch(s -> true), + iof.createLimit(100), + iof.createFilter(p), + iof.createLimit(1) + ); + + return new PipelineTestCase<>("None Match Speedment Predicate", anyMatch, anyMatchExpected); + } + +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperationModifierTest.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperationModifierTest.java new file mode 100644 index 000000000..b1f730e8d --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/StandardTerminalOperationModifierTest.java @@ -0,0 +1,105 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal; + +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.pipeline.PipelineFactory; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperation; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationFactory; +import com.speedment.jpastreamer.rootfactory.RootFactory; +import com.speedment.jpastreamer.termopmodifier.TerminalOperationModifier; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +abstract class StandardTerminalOperationModifierTest { + + protected final PipelineFactory pipelineFactory = RootFactory.getOrThrow(PipelineFactory.class, ServiceLoader::load); + protected final IntermediateOperationFactory iof = RootFactory.getOrThrow(IntermediateOperationFactory.class, ServiceLoader::load); + protected final TerminalOperationFactory tof = RootFactory.getOrThrow(TerminalOperationFactory.class, ServiceLoader::load); + protected final TerminalOperationModifier modifier = new StandardTerminalOperatorModifier(); + + abstract Class getEntityClass(); + + @TestFactory + Stream modify() { + return pipelines().map(testCase -> dynamicTest(testCase.getName(), () -> { + this.modifier.modify(testCase.getPipeline()); + + assertTestCase(testCase); + })); + } + + protected abstract Stream> pipelines(); + + protected Pipeline createPipeline(final TerminalOperation terminalOperation, final IntermediateOperation... operations) { + final Pipeline pipeline = pipelineFactory.createPipeline(getEntityClass()); + + for (IntermediateOperation operation : operations) { + pipeline.intermediateOperations().add(operation); + } + + pipeline.terminatingOperation(terminalOperation); + + return pipeline; + } + + private void assertTestCase(final PipelineTestCase testCase) { + final Pipeline unoptimized = testCase.getPipeline(); + final Pipeline optimized = testCase.getExpectedPipeline(); + + final List> unoptimizedIntermediateOperations = unoptimized.intermediateOperations(); + final List> optimizedIntermediateOperations = optimized.intermediateOperations(); + + assertEquals(optimizedIntermediateOperations.size(), unoptimizedIntermediateOperations.size()); + + for (int i = 0; i < unoptimizedIntermediateOperations.size(); i++) { + final IntermediateOperation unoptimizedOperation = unoptimizedIntermediateOperations.get(i); + final IntermediateOperation optimizedOperation = optimizedIntermediateOperations.get(i); + + assertEquals(optimizedOperation.type(), unoptimizedOperation.type()); + assertArguments(optimizedOperation.arguments(), unoptimizedOperation.arguments()); + } + } + + protected void assertArguments(final Object[] expected, final Object[] actual) { + assertArrayEquals(expected, actual); + } + + protected static final class PipelineTestCase { + + private final String name; + private final Pipeline pipeline; + private final Pipeline expectedPipeline; + + protected PipelineTestCase( + final String name, + final Pipeline pipeline, + final Pipeline expectedPipeline + ) { + this.name = name; + this.pipeline = pipeline; + this.expectedPipeline = expectedPipeline; + } + + public String getName() { + return name; + } + + public Pipeline getPipeline() { + return pipeline; + } + + public Pipeline getExpectedPipeline() { + return expectedPipeline; + } + } + +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/model/Film$.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/model/Film$.java new file mode 100644 index 000000000..a32e055bc --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/model/Film$.java @@ -0,0 +1,103 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal.model; + +import com.speedment.jpastreamer.field.ComparableField; +import com.speedment.jpastreamer.field.ShortField; +import com.speedment.jpastreamer.field.StringField; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +public class Film$ { + + /** + * This Field corresponds to the {@link Film} field "length". + */ + public static final ComparableField length = ComparableField.create( + Film.class, + "length", + Film::getLength, + false + ); + /** + * This Field corresponds to the {@link Film} field "filmId". + */ + public static final ShortField filmId = ShortField.create( + Film.class, + "filmId", + Film::getFilmId, + false + ); + /** + * This Field corresponds to the {@link Film} field "replacementCost". + */ + public static final ComparableField replacementCost = ComparableField.create( + Film.class, + "replacementCost", + Film::getReplacementCost, + false + ); + + /** + * This Field corresponds to the {@link Film} field "rating". + */ + public static final StringField rating = StringField.create( + Film.class, + "rating", + Film::getRating, + false + ); + /** + * This Field corresponds to the {@link Film} field "title". + */ + public static final StringField title = StringField.create( + Film.class, + "title", + Film::getTitle, + false + ); + /** + * This Field corresponds to the {@link Film} field "languageId". + */ + public static final ShortField languageId = ShortField.create( + Film.class, + "languageId", + Film::getLanguageId, + false + ); + /** + * This Field corresponds to the {@link Film} field "description". + */ + public static final StringField description = StringField.create( + Film.class, + "description", + Film::getDescription, + false + ); + /** + * This Field corresponds to the {@link Film} field "rentalRate". + */ + public static final ComparableField rentalRate = ComparableField.create( + Film.class, + "rentalRate", + Film::getRentalRate, + false + ); + /** + * This Field corresponds to the {@link Film} field "rentalDuration". + */ + public static final ShortField rentalDuration = ShortField.create( + Film.class, + "rentalDuration", + Film::getRentalDuration, + false + ); + /** + * This Field corresponds to the {@link Film} field "lastUpdate". + */ + public static final ComparableField lastUpdate = ComparableField.create( + Film.class, + "lastUpdate", + Film::getLastUpdate, + false + ); +} diff --git a/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/model/Film.java b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/model/Film.java new file mode 100644 index 000000000..dfe68e0d8 --- /dev/null +++ b/provider/termopmodifier-standard/src/test/java/com/speedment/jpastreamer/termopmodifier/standard/internal/model/Film.java @@ -0,0 +1,198 @@ +package com.speedment.jpastreamer.termopmodifier.standard.internal.model; + +import jakarta.persistence.*; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Entity +@Table(name = "film", schema = "sakila") +public class Film { + public Film() { + } + + public Film(short filmId) { + this.filmId = filmId; + } + + public Film(String description) { + this.description = description; + } + + public Film(short filmId, String title, String description) { + this.filmId = filmId; + this.title = title; + this.description = description; + } + + public Film(short filmId, String description) { + this.filmId = filmId; + this.description = description; + } + + @Id + @Column(name = "film_id", columnDefinition = "SMALLINT UNSIGNED") + private short filmId; + + @Basic + @Column(name = "title", columnDefinition = "VARCHAR(255)") + private String title; + + @Basic + @Column(name = "description", columnDefinition = "TEXT") + private String description; + @Basic + @Column(name = "rental_duration", columnDefinition = "YEAR") + private short rentalDuration; + @Basic + @Column(name = "language_id", columnDefinition = "SMALLINT UNSIGNED") + private short languageId; + @Basic + @Column(name = "rental_rate", columnDefinition = "DECIMAL(4,2)") + private BigDecimal rentalRate; + + @Basic + @Column(name = "length", columnDefinition = "SMALL UNSIGNED") + private Short length; + + @Basic + @Column(name = "rating", columnDefinition = "enum('G','PG','PG-13','R','NC-17')") + private String rating; + + @Basic + @Column(name = "replacement_cost") + private BigDecimal replacementCost; + + @Basic + @Column(name = "last_update") + private Timestamp lastUpdate; + + public short getFilmId() { + return filmId; + } + + public void setFilmId(short filmId) { + this.filmId = filmId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public short getLanguageId() { + return languageId; + } + + public void setLanguageId(short languageId) { + this.languageId = languageId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public short getRentalDuration() { + return rentalDuration; + } + + public void setRentalDuration(short rentalDuration) { + this.rentalDuration = rentalDuration; + } + + public String getRating() { + return rating; + } + + public void setRating(String rating) { + this.rating = rating; + } + + public BigDecimal getRentalRate() { + return rentalRate; + } + + public void setRentalRate(BigDecimal rentalRate) { + this.rentalRate = rentalRate; + } + + public Short getLength() { + return length; + } + + public void setLength(Short length) { + this.length = length; + } + + public BigDecimal getReplacementCost() { + return replacementCost; + } + + public void setReplacementCost(BigDecimal replacementCost) { + this.replacementCost = replacementCost; + } + + public Timestamp getLastUpdate() { + return lastUpdate; + } + + public void setLastUpdate(Timestamp lastUpdate) { + this.lastUpdate = lastUpdate; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Film film = (Film) o; + + if (filmId != film.filmId) return false; + if (rentalDuration != film.rentalDuration) return false; + if (title != null ? !title.equals(film.title) : film.title != null) return false; + if (description != null ? !description.equals(film.description) : film.description != null) return false; + if (rentalRate != null ? !rentalRate.equals(film.rentalRate) : film.rentalRate != null) return false; + if (length != null ? !length.equals(film.length) : film.length != null) return false; + if (replacementCost != null ? !replacementCost.equals(film.replacementCost) : film.replacementCost != null) + return false; + if (lastUpdate != null ? !lastUpdate.equals(film.lastUpdate) : film.lastUpdate != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) filmId; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + (int) rentalDuration; + result = 31 * result + (rentalRate != null ? rentalRate.hashCode() : 0); + result = 31 * result + (length != null ? length.hashCode() : 0); + result = 31 * result + (replacementCost != null ? replacementCost.hashCode() : 0); + result = 31 * result + (lastUpdate != null ? lastUpdate.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Film{" + + "filmId=" + filmId + + ", title='" + title + '\'' + + ", description='" + description + '\'' + + ", rentalDuration=" + rentalDuration + + ", languageId=" + languageId + + ", rentalRate=" + rentalRate + + ", length=" + length + + ", rating='" + rating + '\'' + + ", replacementCost=" + replacementCost + + ", lastUpdate=" + lastUpdate + + '}'; + } +} diff --git a/provider/termopoptimizer-standard/pom.xml b/provider/termopoptimizer-standard/pom.xml index ff0b339ff..401e2c523 100644 --- a/provider/termopoptimizer-standard/pom.xml +++ b/provider/termopoptimizer-standard/pom.xml @@ -39,6 +39,7 @@ com.speedment.jpastreamer termopoptimizer + diff --git a/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java b/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java index 3fd766c4f..47d1bd897 100644 --- a/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java +++ b/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java @@ -12,18 +12,20 @@ */ package com.speedment.jpastreamer.termopoptimizer.standard.internal; -import static java.util.Objects.requireNonNull; - import com.speedment.jpastreamer.pipeline.Pipeline; import com.speedment.jpastreamer.termopoptimizer.TerminalOperationOptimizer; -final class StandardTerminalOperationOptimizer implements TerminalOperationOptimizer { +import static java.util.Objects.requireNonNull; +final class StandardTerminalOperationOptimizer implements TerminalOperationOptimizer { + @Override public Pipeline optimize(Pipeline pipeline) { requireNonNull(pipeline); - // For now, just return whatever we get. + + // For now, just return whatever we get. + return pipeline; } - + }