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 extends TypeElement> annotations, RoundEnvironment
return false;
}
- roundEnv.getElementsAnnotatedWith(Entity.class).stream()
+ Set extends Element> 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 super T> predicate);
-
-
+
IntermediateOperation, Stream> createMap(Function super T, ? extends R> mapper);
IntermediateOperation, IntStream> createMapToInt(ToIntFunction super T> 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;
}
-
+
}