mirror of
https://github.com/processing/processing4.git
synced 2026-01-30 03:41:15 +01:00
Refactor within PdePreprocessor to allow for override of edits.
In response to https://github.com/processing/processing4/issues/11, allow client code to override preprocessing edit behabior by providing a subclass of PdeParseTreeListener. This does make the construction for PdePreprocessor.java a little bit messier but a builder may help and moving dependent types within enclosing classes can hopefully keep things coherent.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public class JavaEditor extends Editor {
|
||||
|
||||
|
||||
public PdePreprocessor createPreprocessor(final String sketchName) {
|
||||
return new PdePreprocessor(sketchName);
|
||||
return PdePreprocessor.builderFor(sketchName).build();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>
|
||||
* This preprocessor assists with
|
||||
* </p>
|
||||
*/
|
||||
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<PdePreprocessIssue> preprocessIssues = new ArrayList<>();
|
||||
final List<PdePreprocessIssue> 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<Integer> tabSize;
|
||||
private Optional<Boolean> isTesting;
|
||||
private Optional<ParseTreeListenerFactory> 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
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package processing.mode.java.preproc.issue;
|
||||
|
||||
import processing.mode.java.preproc.issue.PdePreprocessIssue;
|
||||
|
||||
public interface PdePreprocessIssueListener {
|
||||
|
||||
void onIssue(PdePreprocessIssue issue);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user