diff --git a/java/src/processing/mode/java/JavaBuild.java b/java/src/processing/mode/java/JavaBuild.java
index 5c3e79016..9df5a6b94 100644
--- a/java/src/processing/mode/java/JavaBuild.java
+++ b/java/src/processing/mode/java/JavaBuild.java
@@ -139,7 +139,8 @@ public class JavaBuild {
* @return null if compilation failed, main class name if not
*/
public String preprocess(File srcFolder, boolean sizeWarning) throws SketchException {
- return preprocess(srcFolder, null, new PdePreprocessor(sketch.getName()), sizeWarning);
+ PdePreprocessor preprocessor = PdePreprocessor.builderFor(sketch.getName()).build();
+ return preprocess(srcFolder, null, preprocessor, sizeWarning);
}
diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java
index 61fd15144..e181601f3 100644
--- a/java/src/processing/mode/java/JavaEditor.java
+++ b/java/src/processing/mode/java/JavaEditor.java
@@ -125,7 +125,7 @@ public class JavaEditor extends Editor {
public PdePreprocessor createPreprocessor(final String sketchName) {
- return new PdePreprocessor(sketchName);
+ return PdePreprocessor.builderFor(sketchName).build();
}
diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeErrorListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeErrorListener.java
deleted file mode 100644
index 2662ada7b..000000000
--- a/java/src/processing/mode/java/preproc/PdeParseTreeErrorListener.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
-Part of the Processing project - http://processing.org
-
-Copyright (c) 2012-19 The Processing Foundation
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software Foundation,
-Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-package processing.mode.java.preproc;
-
-import processing.mode.java.preproc.issue.PdePreprocessIssue;
-
-
-/**
- * Listener for issues encountered while processing a valid pde parse tree.
- */
-interface PdeParseTreeErrorListener {
-
- /**
- * Callback to invoke when an issue is encountered while processing a valid PDE parse tree.
- *
- * @param issue The issue reported.
- */
- void onError(PdePreprocessIssue issue);
-
-}
diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java
index c156e73b0..5c859ac88 100644
--- a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java
+++ b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java
@@ -705,4 +705,18 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
return builder.build();
}
+
+ /**
+ * Listener for issues encountered while processing a valid pde parse tree.
+ */
+ static interface PdeParseTreeErrorListener {
+
+ /**
+ * Callback to invoke when an issue is encountered while processing a valid PDE parse tree.
+ *
+ * @param issue The issue reported.
+ */
+ void onError(PdePreprocessIssue issue);
+
+ }
}
diff --git a/java/src/processing/mode/java/preproc/PdePreprocessor.java b/java/src/processing/mode/java/preproc/PdePreprocessor.java
index 44cfd2e5c..c4a04499c 100644
--- a/java/src/processing/mode/java/preproc/PdePreprocessor.java
+++ b/java/src/processing/mode/java/preproc/PdePreprocessor.java
@@ -25,6 +25,7 @@ import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
@@ -38,52 +39,78 @@ import processing.mode.java.preproc.issue.PdePreprocessIssue;
/**
- * Utility to preprocess sketches prior to comilation.
+ * Utility to preprocess sketches prior to compilation.
+ *
+ *
+ * This preprocessor assists with
+ *
*/
public class PdePreprocessor {
+ /**
+ * The mode that the sketch uses to run.
+ */
public static enum Mode {
- STATIC, ACTIVE, JAVA
+ /**
+ * Sketch without draw, setup, or settings functions where code is run as if the body of a
+ * method without any enclosing types. This code will not define its enclosing class or method.
+ */
+ STATIC,
+
+ /**
+ * Sketch using draw, setup, and / or settings where the code is run as if defining the body
+ * of a class. This code will not define its enclosing class but it will define its enclosing
+ * method.
+ */
+ ACTIVE,
+
+ /**
+ * Sketch written like typical Java where the code is run such that it defines the enclosing
+ * classes itself.
+ */
+ JAVA
}
- private String sketchName;
- private int tabSize;
-
- private boolean hasMain;
-
+ private final String sketchName;
+ private final int tabSize;
private final boolean isTesting;
+ private final ParseTreeListenerFactory listenerFactory;
+
+ private boolean foundMain;
/**
- * Create a new preprocessor.
+ * Create a new PdePreprocessorBuilder for a sketch of the given name.
*
- * @param sketchName The name of the sketch.
+ * Create a new builder to help instantiate a preprocessor for a sketch of the given name. Use
+ * this builder to configure settings of the preprocessor before building.
+ *
+ * @param sketchName The name of the sketch for which a preprocessor will be built.
+ * @return Builder to create a preprocessor for the sketch of the given name.
*/
- public PdePreprocessor(final String sketchName) {
- this(sketchName, Preferences.getInteger("editor.tabs.size"), false);
+ public static PdePreprocessorBuilder builderFor(String sketchName) {
+ return new PdePreprocessorBuilder(sketchName);
}
/**
* Create a new preprocessor.
*
- * @param sketchName The name of the sketch.
- * @param tabSize The number of tabs.
- */
- public PdePreprocessor(final String sketchName, final int tabSize) {
- this(sketchName, tabSize, false);
- }
-
- /**
- * Create a new preprocessor.
+ * Create a new preprocessor that will use the following set of configuration values to process
+ * a parse tree. This object should be instantiated by calling {builderFor}.
*
- * @param sketchName The name of the sketch.
- * @param tabSize The number of tabs.
- * @param isTesting Flag indicating if this is running in unit tests (true) or in production
+ * @param newSketchName The name of the sketch.
+ * @param newTabSize The number of spaces within a tab.
+ * @param newIsTesting Flag indicating if this is running in unit tests (true) or in production
* (false).
+ * @param newFactory The factory to use for building the ANTLR tree traversal listener where
+ * preprocessing edits should be made.
*/
- public PdePreprocessor(final String sketchName, final int tabSize, boolean isTesting) {
- this.sketchName = sketchName;
- this.tabSize = tabSize;
- this.isTesting = isTesting;
+ private PdePreprocessor(final String newSketchName, final int newTabSize, boolean newIsTesting,
+ final ParseTreeListenerFactory newFactory) {
+
+ sketchName = newSketchName;
+ tabSize = newTabSize;
+ isTesting = newIsTesting;
+ listenerFactory = newFactory;
}
/**
@@ -151,7 +178,7 @@ public class PdePreprocessor {
// Parser
final List preprocessIssues = new ArrayList<>();
final List treeIssues = new ArrayList<>();
- PdeParseTreeListener listener = createListener(tokens, sketchName);
+ PdeParseTreeListener listener = listenerFactory.build(tokens, sketchName, tabSize);
listener.setTesting(isTesting);
listener.setCoreImports(ImportUtil.getCoreImports());
listener.setDefaultImports(ImportUtil.getDefaultImports());
@@ -188,7 +215,7 @@ public class PdePreprocessor {
PrintWriter outPrintWriter = new PrintWriter(outWriter);
outPrintWriter.print(outputProgram);
- hasMain = listener.foundMain();
+ foundMain = listener.foundMain();
return listener.getResult();
}
@@ -199,9 +226,133 @@ public class PdePreprocessor {
* @return True if a main method was found. False otherwise.
*/
public boolean hasMain() {
- return hasMain;
+ return foundMain;
}
+ /* ========================
+ * === Type Definitions ===
+ * ========================
+ *
+ * The preprocessor has a sizable number of parameters, including those that can modify its
+ * internal behavior. These supporting types help initialize this object and provide hooks for
+ * behavior modifications.
+ */
+
+ /**
+ * Builder to help instantiate a PdePreprocessor.
+ *
+ * The PdePreprocessor includes a number of parameters, including some behavioral parameters that
+ * change how the parse tree is processed. This builder helps instantiate this object by providing
+ * reasonable defaults for most values but allowing client to (especially modes) to override those
+ * defaults.
+ */
+ public static class PdePreprocessorBuilder {
+
+ private final String sketchName;
+ private Optional tabSize;
+ private Optional isTesting;
+ private Optional parseTreeFactory;
+
+ private PdePreprocessorBuilder(String newSketchName) {
+ sketchName = newSketchName;
+ tabSize = Optional.empty();
+ isTesting = Optional.empty();
+ parseTreeFactory = Optional.empty();
+ }
+
+ /**
+ * Set how large the tabs should be.
+ *
+ * @param newTabSize The number of spaces in a tab.
+ * @return This builder for method chaining.
+ */
+ public PdePreprocessorBuilder setTabSize(int newTabSize) {
+ tabSize = Optional.of(newTabSize);
+ return this;
+ }
+
+ /**
+ * Indicate if this preprocessor is running within unittests.
+ *
+ * @param newIsTesting Flag that, if true, will configure the preprocessor to run safely within
+ * a unit testing environment.
+ * @return This builder for method chaining.
+ */
+ public PdePreprocessorBuilder setIsTesting(boolean newIsTesting) {
+ isTesting = Optional.of(newIsTesting);
+ return this;
+ }
+
+ /**
+ * Specify how the parse tree listener should be built.
+ *
+ * The ANTLR parse tree listener is where the preprocessing edits are generated and some client
+ * code (like modes) may need to override some of the preprocessing edit behavior. Specifying
+ * this factory allows client code to replace the default PdeParseTreeListener that is used
+ * during preprocessing.
+ *
+ * @param newFactory The factory to use in building a parse tree listener.
+ * @return This builder for method chaining.
+ */
+ public PdePreprocessorBuilder setParseTreeFactory(ParseTreeListenerFactory newFactory) {
+ parseTreeFactory = Optional.of(newFactory);
+ return this;
+ }
+
+ /**
+ * Build the preprocessor.
+ *
+ * @return Newly built preproceesor.
+ */
+ public PdePreprocessor build() {
+ final int effectiveTabSize = tabSize.orElseGet(
+ () -> Preferences.getInteger("editor.tabs.size")
+ );
+
+ final boolean effectiveIsTesting = isTesting.orElse(false);
+
+ ParseTreeListenerFactory effectiveFactory = parseTreeFactory.orElse(
+ PdeParseTreeListener::new
+ );
+
+ return new PdePreprocessor(
+ sketchName,
+ effectiveTabSize,
+ effectiveIsTesting,
+ effectiveFactory
+ );
+ }
+
+ }
+
+ /**
+ * Factory which creates parse tree traversal listeners.
+ *
+ * The ANTLR parse tree listener is where the preprocessing edits are generated and some client
+ * code (like modes) may need to override some of the preprocessing edit behavior. Specifying
+ * this factory allows client code to replace the default PdeParseTreeListener that is used
+ * during preprocessing.
+ */
+ public static interface ParseTreeListenerFactory {
+
+ /**
+ * Create a new processing listener.
+ *
+ * @param tokens The token stream with sketch code contents.
+ * @param sketchName The name of the sketch that will be preprocessed.
+ * @param tabSize The size (number of spaces) of the tabs.
+ * @return The newly created listener.
+ */
+ PdeParseTreeListener build(CommonTokenStream tokens, String sketchName, int tabSize);
+
+ }
+
+
+ /* ==================================
+ * === Internal Utility Functions ===
+ * ==================================
+ */
+
/**
* Factory function to create a {PdeParseTreeListener} for use in preprocessing
*
diff --git a/java/src/processing/mode/java/preproc/SourceEmitter.java b/java/src/processing/mode/java/preproc/SourceEmitter.java
deleted file mode 100644
index 747a0106a..000000000
--- a/java/src/processing/mode/java/preproc/SourceEmitter.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
-Part of the Processing project - http://processing.org
-
-Copyright (c) 2012-19 The Processing Foundation
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software Foundation,
-Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-package processing.mode.java.preproc;
-
-
-/**
- * Simple interface for strategy which can emit the full body of a processing sketch.
- */
-public interface SourceEmitter {
-
- /**
- * Get the full body of the processing sketch.
- *
- * @return String processing sketch source code across all tabs.
- */
- String getSource();
-
-}
diff --git a/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java b/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java
index ad736ed4a..d7f3acc75 100644
--- a/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java
+++ b/java/src/processing/mode/java/preproc/issue/PdeIssueEmitter.java
@@ -25,8 +25,6 @@ import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
-import processing.mode.java.preproc.SourceEmitter;
-
import java.util.Optional;
@@ -100,4 +98,31 @@ public class PdeIssueEmitter extends BaseErrorListener {
));
}
+ /**
+ * Simple interface for strategy which can emit the full body of a processing sketch.
+ */
+ public static interface SourceEmitter {
+
+ /**
+ * Get the full body of the processing sketch.
+ *
+ * @return String processing sketch source code across all tabs.
+ */
+ String getSource();
+
+ }
+
+ /**
+ * Interface for listener that responds to issues reported by the preprocessor.
+ */
+ public static interface PdePreprocessIssueListener {
+
+ /**
+ * Callback to invoke when an issue is encountered in preprocesing.
+ *
+ * @param issue Description of the issue.
+ */
+ void onIssue(PdePreprocessIssue issue);
+
+ }
}
diff --git a/java/src/processing/mode/java/preproc/issue/PdePreprocessIssueListener.java b/java/src/processing/mode/java/preproc/issue/PdePreprocessIssueListener.java
deleted file mode 100644
index 2f0e46d65..000000000
--- a/java/src/processing/mode/java/preproc/issue/PdePreprocessIssueListener.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package processing.mode.java.preproc.issue;
-
-import processing.mode.java.preproc.issue.PdePreprocessIssue;
-
-public interface PdePreprocessIssueListener {
-
- void onIssue(PdePreprocessIssue issue);
-
-}
diff --git a/java/test/processing/mode/java/ProcessingTestUtil.java b/java/test/processing/mode/java/ProcessingTestUtil.java
index 6e113e9d2..723329c5b 100644
--- a/java/test/processing/mode/java/ProcessingTestUtil.java
+++ b/java/test/processing/mode/java/ProcessingTestUtil.java
@@ -39,7 +39,13 @@ public class ProcessingTestUtil {
throws SketchException {
final String program = read(resource);
final StringWriter out = new StringWriter();
- PreprocessorResult result = new PdePreprocessor(name, 4, true).write(out, program);
+
+ PdePreprocessor preprocessor = PdePreprocessor.builderFor(name)
+ .setTabSize(4)
+ .setIsTesting(true)
+ .build();
+
+ PreprocessorResult result = preprocessor.write(out, program);
if (result.getPreprocessIssues().size() > 0) {
throw new PdePreprocessIssueException(result.getPreprocessIssues().get(0));