You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2023/06/08 23:28:04 UTC

[calcite] 04/06: In Puffin, add actions before and after each source and all sources

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 <jh...@apache.org>
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();
+  }
 }