This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/calcite.git
commit ac4920bb0ab52a25c3b2cf008f361ff0fbc7f82a Author: Julian Hyde <[email protected]> AuthorDate: Thu Jun 8 00:20:49 2023 -0700 In Puffin, add actions before and after each source and all sources --- .../main/java/org/apache/calcite/util/Puffin.java | 80 +++++++++++++++++++--- .../java/org/apache/calcite/test/PuffinTest.java | 74 ++++++++++++++++++-- 2 files changed, 140 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/util/Puffin.java b/core/src/main/java/org/apache/calcite/util/Puffin.java index f776d58030..3be201ea4e 100644 --- a/core/src/main/java/org/apache/calcite/util/Puffin.java +++ b/core/src/main/java/org/apache/calcite/util/Puffin.java @@ -78,7 +78,8 @@ public class Puffin { public static <G, F> Builder<G, F> builder(Supplier<G> globalStateFactory, Function<G, F> fileStateFactory) { return new BuilderImpl<>(globalStateFactory, fileStateFactory, - PairList.of(), new ArrayList<>()); + PairList.of(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), + new ArrayList<>()); } /** Creates a Builder with no state. */ @@ -92,9 +93,23 @@ public class Puffin { * @param <F> Type of state that is created when we start processing a file * @see Puffin#builder */ public interface Builder<G, F> { + /** Adds a predicate and action to be invoked on each line of a source. */ Builder<G, F> add(Predicate<Line<G, F>> linePredicate, Consumer<Line<G, F>> action); + + /** Adds an action to be called before each source. */ + Builder<G, F> beforeSource(Consumer<Context<G, F>> action); + + /** Adds an action to be called after each source. */ + Builder<G, F> afterSource(Consumer<Context<G, F>> action); + + /** Adds an action to be called before all sources. */ + Builder<G, F> before(Consumer<Context<G, F>> action); + + /** Adds an action to be called after all sources. */ Builder<G, F> after(Consumer<Context<G, F>> action); + + /** Builds the program. */ Program<G> build(); } @@ -239,8 +254,11 @@ public class Puffin { private static class ProgramImpl<G, F> implements Program<G> { private final Supplier<G> globalStateFactory; private final Function<G, F> fileStateFactory; - private final PairList<Predicate<Line<G, F>>, Consumer<Line<G, F>>> pairList; - private final ImmutableList<Consumer<Context<G, F>>> endList; + private final PairList<Predicate<Line<G, F>>, Consumer<Line<G, F>>> onLineList; + private final ImmutableList<Consumer<Context<G, F>>> beforeSourceList; + private final ImmutableList<Consumer<Context<G, F>>> afterSourceList; + private final ImmutableList<Consumer<Context<G, F>>> beforeList; + private final ImmutableList<Consumer<Context<G, F>>> afterList; @SuppressWarnings("Convert2MethodRef") private final LoadingCache<String, Pattern> patternCache0 = CacheBuilder.newBuilder() @@ -250,18 +268,31 @@ public class Puffin { private ProgramImpl(Supplier<G> globalStateFactory, Function<G, F> fileStateFactory, - PairList<Predicate<Line<G, F>>, Consumer<Line<G, F>>> pairList, - ImmutableList<Consumer<Context<G, F>>> endList) { + PairList<Predicate<Line<G, F>>, Consumer<Line<G, F>>> onLineList, + ImmutableList<Consumer<Context<G, F>>> beforeSourceList, + ImmutableList<Consumer<Context<G, F>>> afterSourceList, + ImmutableList<Consumer<Context<G, F>>> beforeList, + ImmutableList<Consumer<Context<G, F>>> afterList) { this.globalStateFactory = globalStateFactory; this.fileStateFactory = fileStateFactory; - this.pairList = pairList; - this.endList = endList; + this.onLineList = onLineList; + this.beforeSourceList = beforeSourceList; + this.afterSourceList = afterSourceList; + this.beforeList = beforeList; + this.afterList = afterList; } @Override public G execute(Stream<? extends Source> sources, PrintWriter out) { final G globalState = globalStateFactory.get(); + final Source source0 = Sources.of(""); + final F fileState0 = fileStateFactory.apply(globalState); + final ContextImpl<G, F> x0 = + new ContextImpl<G, F>(out, source0, patternCache, globalState, + fileState0); + beforeList.forEach(action -> action.accept(x0)); sources.forEach(source -> execute(globalState, source, out)); + afterList.forEach(action -> action.accept(x0)); return globalState; } @@ -272,20 +303,21 @@ public class Puffin { final ContextImpl<G, F> x = new ContextImpl<G, F>(out, source, patternCache, globalState, fileState); + beforeSourceList.forEach(action -> action.accept(x)); for (;;) { String lineText = br.readLine(); if (lineText == null) { - endList.forEach(end -> end.accept(x)); break; } ++x.fnr; x.line = lineText; - pairList.forEach((predicate, action) -> { + onLineList.forEach((predicate, action) -> { if (predicate.test(x)) { action.accept(x); } }); } + afterSourceList.forEach(action -> action.accept(x)); } catch (IOException e) { throw new RuntimeException(e); } @@ -301,15 +333,24 @@ public class Puffin { private final Supplier<G> globalStateFactory; private final Function<G, F> fileStateFactory; final PairList<Predicate<Line<G, F>>, Consumer<Line<G, F>>> onLineList; + final List<Consumer<Context<G, F>>> beforeSourceList; + final List<Consumer<Context<G, F>>> afterSourceList; + final List<Consumer<Context<G, F>>> beforeList; final List<Consumer<Context<G, F>>> afterList; private BuilderImpl(Supplier<G> globalStateFactory, Function<G, F> fileStateFactory, PairList<Predicate<Line<G, F>>, Consumer<Line<G, F>>> onLineList, + List<Consumer<Context<G, F>>> beforeSourceList, + List<Consumer<Context<G, F>>> afterSourceList, + List<Consumer<Context<G, F>>> beforeList, List<Consumer<Context<G, F>>> afterList) { this.globalStateFactory = globalStateFactory; this.fileStateFactory = fileStateFactory; this.onLineList = onLineList; + this.beforeSourceList = beforeSourceList; + this.afterSourceList = afterSourceList; + this.beforeList = beforeList; this.afterList = afterList; } @@ -319,6 +360,21 @@ public class Puffin { return this; } + @Override public Builder<G, F> beforeSource(Consumer<Context<G, F>> action) { + beforeSourceList.add(action); + return this; + } + + @Override public Builder<G, F> afterSource(Consumer<Context<G, F>> action) { + afterSourceList.add(action); + return this; + } + + @Override public Builder<G, F> before(Consumer<Context<G, F>> action) { + beforeList.add(action); + return this; + } + @Override public Builder<G, F> after(Consumer<Context<G, F>> action) { afterList.add(action); return this; @@ -326,7 +382,11 @@ public class Puffin { @Override public Program<G> build() { return new ProgramImpl<>(globalStateFactory, fileStateFactory, - onLineList.immutable(), ImmutableList.copyOf(afterList)); + onLineList.immutable(), + ImmutableList.copyOf(beforeSourceList), + ImmutableList.copyOf(afterSourceList), + ImmutableList.copyOf(beforeList), + ImmutableList.copyOf(afterList)); } } } diff --git a/core/src/test/java/org/apache/calcite/test/PuffinTest.java b/core/src/test/java/org/apache/calcite/test/PuffinTest.java index 604685e0eb..3cbebca6a8 100644 --- a/core/src/test/java/org/apache/calcite/test/PuffinTest.java +++ b/core/src/test/java/org/apache/calcite/test/PuffinTest.java @@ -26,14 +26,19 @@ import org.junit.jupiter.api.Test; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.stream.Stream; import static org.apache.calcite.test.Matchers.isLinux; +import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasToString; /** Tests {@link Puffin}. */ @@ -42,19 +47,71 @@ public class PuffinTest { new Fixture<>(Sources.of(""), Puffin.builder().build()); @Test void testPuffin() { - Puffin.Program<Unit> program = - Puffin.builder(() -> Unit.INSTANCE, u -> new AtomicInteger()) + Puffin.Program<AtomicInteger> program = + Puffin.builder(AtomicInteger::new, counter -> Unit.INSTANCE) .add(line -> !line.startsWith("#") && !line.matches(".*/\\*.*\\*/.*"), - line -> line.state().incrementAndGet()) + line -> line.globalState().incrementAndGet()) .after(context -> - context.println("counter: " + context.state().get())) + context.println("counter: " + context.globalState().get())) .build(); fixture().withDefaultInput() .withProgram(program) .generatesOutput(isLinux("counter: 2\n")); } + /** Tests Puffin with several sources, registers actions by calling + * {@link Puffin.Builder#beforeSource(Consumer)}, + * {@link Puffin.Builder#afterSource(Consumer)}, + * {@link Puffin.Builder#before(Consumer)}, and + * {@link Puffin.Builder#after(Consumer)}, and counts how many times each is + * called. */ + @Test void testSeveralSources() { + Puffin.Program<GlobalState> program = + Puffin.builder(GlobalState::new, u -> new AtomicInteger()) + .add(line -> true, + line -> line.state().incrementAndGet()) + .beforeSource(context -> { + final GlobalState g = context.globalState(); + g.beforeSourceCount.incrementAndGet(); + }) + .afterSource(context -> { + final GlobalState g = context.globalState(); + final AtomicInteger f = context.state(); + g.messages.add(f.intValue() + " lines"); + g.afterSourceCount.incrementAndGet(); + }) + .before(context -> { + final GlobalState g = context.globalState(); + g.beforeCount.incrementAndGet(); + }) + .after(context -> { + final GlobalState g = context.globalState(); + g.afterCount.incrementAndGet(); + g.messages.add(g.afterSourceCount + " after sources"); + g.messages.add(g.beforeSourceCount + " before sources"); + g.messages.add(g.beforeCount + " before"); + g.messages.add(g.afterCount + " after"); + }) + .build(); + final StringWriter sw = new StringWriter(); + GlobalState g = + program.execute( + Stream.of(Sources.of("a\nb\n"), + Sources.of("a\n"), + Sources.of("a\nb\nc\n")), + new PrintWriter(sw)); + assertThat(g.messages, hasSize(7)); + assertThat(g.messages, hasItem("3 lines")); + assertThat(g.messages, hasItem("2 lines")); + assertThat(g.messages, hasItem("1 lines")); + assertThat(g.messages, hasItem("3 after sources")); + assertThat(g.messages, hasItem("3 before sources")); + assertThat(g.messages, hasItem("1 before")); + assertThat(g.messages, hasItem("1 after")); + assertThat(sw, hasToString("")); + } + @Test void testEmptyProgram() { final Puffin.Program<Unit> program = Puffin.builder().build(); fixture().withDefaultInput() @@ -105,4 +162,13 @@ public class PuffinTest { return this; } } + + /** Global state. */ + private static class GlobalState { + final List<String> messages = new ArrayList<>(); + final AtomicInteger beforeSourceCount = new AtomicInteger(); + final AtomicInteger afterSourceCount = new AtomicInteger(); + final AtomicInteger beforeCount = new AtomicInteger(); + final AtomicInteger afterCount = new AtomicInteger(); + } }
