newDestinationPackageName) {
+
rewriter = new TokenStreamRewriter(tokens);
sketchName = newSketchName;
tabSize = newTabSize;
+ destinationPackageName = newDestinationPackageName;
pdeParseTreeErrorListenerMaybe = Optional.empty();
+
+ final char[] indentChars = new char[newTabSize];
+ Arrays.fill(indentChars, ' ');
+ indent1 = new String(indentChars);
+ indent2 = indent1 + indent1;
+ indent3 = indent2 + indent1;
}
+ /*
+ * ===============================================
+ * === Public interface for client code usage. ===
+ * ===============================================
+ */
+
/**
* Indicate imports for code folders given those imports' fully qualified names.
*
@@ -254,7 +275,11 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
);
}
- // --------------------------------------------------- listener impl
+ /*
+ * =========================================================
+ * === Implementation of the ANTLR 4 listener functions. ===
+ * =========================================================
+ */
/**
* Endpoint for ANTLR to call when having finished parsing a processing sketch.
@@ -263,12 +288,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
*/
public void exitProcessingSketch(ProcessingParser.ProcessingSketchContext ctx) {
// header
- RewriteParams rewriteParams = createRewriteParams();
-
- RewriterCodeGenerator codeGen = new RewriterCodeGenerator(tabSize);
-
- headerResult = codeGen.writeHeader(rewriter, rewriteParams);
-
+ headerResult = prepareHeader(rewriter);
lineOffset = headerResult.getLineOffset();
// footer
@@ -276,7 +296,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
int tokens = tokenStream.size();
int length = tokenStream.get(tokens-1).getStopIndex();
- footerResult = codeGen.writeFooter(rewriter, rewriteParams, length);
+ footerResult = prepareFooter(rewriter, length);
}
/**
@@ -292,105 +312,6 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
}
}
- /**
- * Manage parsing out a size or fullscreen call.
- *
- * @param ctx The context of the call.
- */
- private void handleSizeCall(ParserRuleContext ctx) {
- ParserRuleContext testCtx = ctx.getParent()
- .getParent()
- .getParent()
- .getParent();
-
- boolean isInGlobal =
- testCtx instanceof ProcessingParser.StaticProcessingSketchContext;
-
- boolean isInSetup;
- if (!isInGlobal) {
- ParserRuleContext methodDeclaration = testCtx.getParent()
- .getParent();
-
- isInSetup = isMethodSetup(methodDeclaration);
- } else {
- isInSetup = false;
- }
-
- ParseTree argsContext = ctx.getChild(2);
-
- boolean thisRequiresRewrite = false;
-
- boolean isSize = ctx.getChild(0).getText().equals(SIZE_METHOD_NAME);
- boolean isFullscreen = ctx.getChild(0).getText().equals(FULLSCREEN_METHOD_NAME);
-
- if (isInGlobal || isInSetup) {
- thisRequiresRewrite = true;
-
- if (isSize && argsContext.getChildCount() > 2) {
- sketchWidth = argsContext.getChild(0).getText();
- if (PApplet.parseInt(sketchWidth, -1) == -1 &&
- !sketchWidth.equals("displayWidth")) {
- thisRequiresRewrite = false;
- }
-
- sketchHeight = argsContext.getChild(2).getText();
- if (PApplet.parseInt(sketchHeight, -1) == -1 &&
- !sketchHeight.equals("displayHeight")) {
- thisRequiresRewrite = false;
- }
-
- if (argsContext.getChildCount() > 3) {
- sketchRenderer = argsContext.getChild(4).getText();
- if (!(sketchRenderer.equals("P2D") ||
- sketchRenderer.equals("P3D") ||
- sketchRenderer.equals("OPENGL") ||
- sketchRenderer.equals("JAVA2D") ||
- sketchRenderer.equals("FX2D"))) {
- thisRequiresRewrite = false;
- }
- }
- }
-
- if (isFullscreen) {
- sketchWidth = "displayWidth";
- sketchWidth = "displayHeight";
-
- thisRequiresRewrite = true;
- sizeIsFullscreen = true;
-
- if (argsContext.getChildCount() > 0) {
- sketchRenderer = argsContext.getChild(0).getText();
- if (!(sketchRenderer.equals("P2D") ||
- sketchRenderer.equals("P3D") ||
- sketchRenderer.equals("OPENGL") ||
- sketchRenderer.equals("JAVA2D") ||
- sketchRenderer.equals("FX2D"))) {
- thisRequiresRewrite = false;
- }
- }
- }
- }
-
- if (thisRequiresRewrite) {
- createDelete(ctx.start, ctx.stop);
- createInsertAfter(ctx.stop, "/* size commented out by preprocessor */");
- sizeRequiresRewrite = true;
- }
- }
-
- /**
- * Determine if a method declaration is for setup.
- *
- * @param declaration The method declaration to parse.
- * @return True if setup and false otherwise.
- */
- private boolean isMethodSetup(ParserRuleContext declaration) {
- if (declaration.getChildCount() < 2) {
- return false;
- }
- return declaration.getChild(1).getText().equals("setup");
- }
-
/**
* Endpoint for ANTLR to call when finished parsing an import declaration.
*
@@ -452,7 +373,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
foundImports.add(ImportStatement.parse(importStringNoSemi));
- createDelete(ctx.start, ctx.stop);
+ delete(ctx.start, ctx.stop);
}
/**
@@ -468,7 +389,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
public void exitFloatLiteral(ProcessingParser.FloatLiteralContext ctx) {
String cTxt = ctx.getText().toLowerCase();
if (!cTxt.endsWith("f") && !cTxt.endsWith("d")) {
- createInsertAfter(ctx.stop, "f");
+ insertAfter(ctx.stop, "f");
}
}
@@ -562,7 +483,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
hasVisibilityModifier = hasVisibilityModifier || childIsVisibility;
boolean isModifier = child instanceof ProcessingParser.ModifierContext;
- if (isModifier && isAnnoation((ProcessingParser.ModifierContext) child)) {
+ if (isModifier && isAnnotation((ProcessingParser.ModifierContext) child)) {
annoationPoint = (ParserRuleContext) child;
}
}
@@ -570,9 +491,9 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
// Insert at start of method or after annoation
if (!hasVisibilityModifier) {
if (annoationPoint == null) {
- createInsertBefore(possibleModifiers.getStart(), " public ");
+ insertBefore(possibleModifiers.getStart(), " public ");
} else {
- createInsertAfter(annoationPoint.getStop(), " public ");
+ insertAfter(annoationPoint.getStop(), " public ");
}
}
@@ -585,6 +506,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
}
/**
+<<<<<<< HEAD
* Check if this contains an annation.
*
* @param context The modifier context to check.
@@ -606,6 +528,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
}
/**
+=======
+>>>>>>> master
* Endpoint for ANTLR to call after parsing a primitive type name.
*
*
@@ -621,8 +545,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
String fn = ctx.getChild(0).getText();
if (!fn.equals("color")) {
fn = "PApplet.parse" + fn.substring(0,1).toUpperCase() + fn.substring(1);
- createInsertBefore(ctx.start, fn);
- createDelete(ctx.start);
+ insertBefore(ctx.start, fn);
+ delete(ctx.start);
}
}
@@ -638,8 +562,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
*/
public void exitColorPrimitiveType(ProcessingParser.ColorPrimitiveTypeContext ctx) {
if (ctx.getText().equals("color")) {
- createInsertBefore(ctx.start, "int");
- createDelete(ctx.start, ctx.stop);
+ insertBefore(ctx.start, "int");
+ delete(ctx.start, ctx.stop);
}
}
@@ -650,21 +574,153 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
*/
public void exitHexColorLiteral(ProcessingParser.HexColorLiteralContext ctx) {
if (ctx.getText().length() == 7) {
- createInsertBefore(
+ insertBefore(
ctx.start,
ctx.getText().toUpperCase().replace("#","0xFF")
);
} else {
- createInsertBefore(
+ insertBefore(
ctx.start,
ctx.getText().toUpperCase().replace("#", "0x")
);
}
- createDelete(ctx.start, ctx.stop);
+ delete(ctx.start, ctx.stop);
}
- // -- Wrappers around CodeEditOperationUtil --
+ /* ===========================================================
+ * === Helper functions to parse and manage tree listeners ===
+ * ===========================================================
+ */
+
+ /**
+ * Manage parsing out a size or fullscreen call.
+ *
+ * @param ctx The context of the call.
+ */
+ protected void handleSizeCall(ParserRuleContext ctx) {
+ ParserRuleContext testCtx = ctx.getParent()
+ .getParent()
+ .getParent()
+ .getParent();
+
+ boolean isInGlobal =
+ testCtx instanceof ProcessingParser.StaticProcessingSketchContext;
+
+ boolean isInSetup;
+ if (!isInGlobal) {
+ ParserRuleContext methodDeclaration = testCtx.getParent()
+ .getParent();
+
+ isInSetup = isMethodSetup(methodDeclaration);
+ } else {
+ isInSetup = false;
+ }
+
+ ParseTree argsContext = ctx.getChild(2);
+
+ boolean thisRequiresRewrite = false;
+
+ boolean isSize = ctx.getChild(0).getText().equals(SIZE_METHOD_NAME);
+ boolean isFullscreen = ctx.getChild(0).getText().equals(FULLSCREEN_METHOD_NAME);
+
+ if (isInGlobal || isInSetup) {
+ thisRequiresRewrite = true;
+
+ if (isSize && argsContext.getChildCount() > 2) {
+ sketchWidth = argsContext.getChild(0).getText();
+ if (PApplet.parseInt(sketchWidth, -1) == -1 &&
+ !sketchWidth.equals("displayWidth")) {
+ thisRequiresRewrite = false;
+ }
+
+ sketchHeight = argsContext.getChild(2).getText();
+ if (PApplet.parseInt(sketchHeight, -1) == -1 &&
+ !sketchHeight.equals("displayHeight")) {
+ thisRequiresRewrite = false;
+ }
+
+ if (argsContext.getChildCount() > 3) {
+ sketchRenderer = argsContext.getChild(4).getText();
+ if (!(sketchRenderer.equals("P2D") ||
+ sketchRenderer.equals("P3D") ||
+ sketchRenderer.equals("OPENGL") ||
+ sketchRenderer.equals("JAVA2D") ||
+ sketchRenderer.equals("FX2D"))) {
+ thisRequiresRewrite = false;
+ }
+ }
+ }
+
+ if (isFullscreen) {
+ sketchWidth = "displayWidth";
+ sketchWidth = "displayHeight";
+
+ thisRequiresRewrite = true;
+ sizeIsFullscreen = true;
+
+ if (argsContext.getChildCount() > 0) {
+ sketchRenderer = argsContext.getChild(0).getText();
+ if (!(sketchRenderer.equals("P2D") ||
+ sketchRenderer.equals("P3D") ||
+ sketchRenderer.equals("OPENGL") ||
+ sketchRenderer.equals("JAVA2D") ||
+ sketchRenderer.equals("FX2D"))) {
+ thisRequiresRewrite = false;
+ }
+ }
+ }
+ }
+
+ if (thisRequiresRewrite) {
+ delete(ctx.start, ctx.stop);
+ insertAfter(ctx.stop, "/* size commented out by preprocessor */");
+ sizeRequiresRewrite = true;
+ }
+ }
+
+ /**
+ * Determine if a method declaration is for setup.
+ *
+ * @param declaration The method declaration to parse.
+ * @return True if setup and false otherwise.
+ */
+ protected boolean isMethodSetup(ParserRuleContext declaration) {
+ if (declaration.getChildCount() < 2) {
+ return false;
+ }
+ return declaration.getChild(1).getText().equals("setup");
+ }
+
+ /**
+ * Check if this contains an annation.
+ *
+ * @param context The modifier context to check.
+ * @return True if annotation. False otherwise
+ */
+ protected boolean isAnnotation(ProcessingParser.ModifierContext context) {
+ if (context.getChildCount() == 0) {
+ return false;
+ }
+
+ ProcessingParser.ClassOrInterfaceModifierContext classModifierCtx;
+ if (!(context.getChild(0) instanceof ProcessingParser.ClassOrInterfaceModifierContext)) {
+ return false;
+ }
+
+ classModifierCtx = (ProcessingParser.ClassOrInterfaceModifierContext) context.getChild(0);
+
+ return classModifierCtx.getChild(0) instanceof ProcessingParser.AnnotationContext;
+ }
+
+ /* ====================================================================================
+ * === Utility functions to perform code edit operations and generate rewrite info. ===
+ * ====================================================================================
+ *
+ * Utility functions to generate and perform code edit operations, performing the edit immediately
+ * within a ANTLR rewriter but also generating a {TextTransform.Edit} for use with the JDT. Some
+ * of these are left protected for subclasses of PdeParseTreeListener access.
+ */
/**
* Insert text before a token.
@@ -672,8 +728,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
* @param location The token before which code should be added.
* @param text The text to add.
*/
- private void createInsertBefore(Token location, String text) {
- edits.add(CodeEditOperationUtil.createInsertBefore(location, text, rewriter));
+ protected void insertBefore(Token location, String text) {
+ edits.add(createInsertBefore(location, text, rewriter));
}
/**
@@ -683,13 +739,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
* @param locationOffset
* @param text Text to add.
*/
- private void createInsertBefore(int locationToken, int locationOffset, String text) {
- edits.add(CodeEditOperationUtil.createInsertBefore(
- locationToken,
- locationOffset,
- text,
- rewriter
- ));
+ protected void insertBefore(int locationToken, int locationOffset, String text) {
+ edits.add(createInsertBefore(locationToken, locationOffset, text, rewriter));
}
/**
@@ -698,8 +749,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
* @param location The token after which to insert code.
* @param text The text to insert.
*/
- private void createInsertAfter(Token location, String text) {
- edits.add(CodeEditOperationUtil.createInsertAfter(location, text, rewriter));
+ protected void insertAfter(Token location, String text) {
+ edits.add(createInsertAfter(location, text, rewriter));
}
/**
@@ -708,8 +759,8 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
* @param start First token to delete.
* @param stop Last token to delete.
*/
- private void createDelete(Token start, Token stop) {
- edits.add(CodeEditOperationUtil.createDelete(start, stop, rewriter));
+ protected void delete(Token start, Token stop) {
+ edits.add(createDelete(start, stop, rewriter));
}
/**
@@ -717,36 +768,549 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
*
* @param location Token to delete.
*/
- private void createDelete(Token location) {
- edits.add(CodeEditOperationUtil.createDelete(location, rewriter));
+ protected void delete(Token location) {
+ edits.add(createDelete(location, rewriter));
+ }
+
+ /*
+ * =========================================
+ * === Code generation utility functions ===
+ * =========================================
+ */
+
+ /**
+ * Prepare preface code to wrap sketch code so that it is contained within a proper Java
+ * definition.
+ *
+ * @param headerWriter The writer into which the header should be written.
+ * @return Information about the completed rewrite.
+ */
+ protected RewriteResult prepareHeader(TokenStreamRewriter headerWriter) {
+
+ RewriteResultBuilder resultBuilder = new RewriteResultBuilder();
+
+ PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen(
+ headerWriter,
+ resultBuilder,
+ 0,
+ true
+ );
+
+ writeHeaderContents(decoratedWriter, resultBuilder);
+
+ decoratedWriter.finish();
+
+ return resultBuilder.build();
}
/**
- * Create parameters required by the RewriterCodeGenerator.
+ * Prepare the footer for a sketch (finishes the constructs introduced in header like class def).
*
- * @return Newly created rewrite params.
+ * @param footerWriter The writer through which the footer should be introduced.
+ * @param insertPoint The loction at which the footer should be written.
+ * @return Information about the completed rewrite.
*/
- private RewriteParams createRewriteParams() {
- RewriteParamsBuilder builder = new RewriteParamsBuilder(VERSION_STR);
+ protected RewriteResult prepareFooter(TokenStreamRewriter footerWriter, int insertPoint) {
- builder.setSketchName(sketchName);
- builder.setisTesting(isTesting);
- builder.setRewriter(rewriter);
- builder.setMode(mode);
- builder.setFoundMain(foundMain);
- builder.setLineOffset(lineOffset);
- builder.setSketchWidth(sketchWidth);
- builder.setSketchHeight(sketchHeight);
- builder.setSketchRenderer(sketchRenderer);
- builder.setIsSizeValidInGlobal(sizeRequiresRewrite);
- builder.setIsSizeFullscreen(sizeIsFullscreen);
+ RewriteResultBuilder resultBuilder = new RewriteResultBuilder();
- builder.addCoreImports(coreImports);
- builder.addDefaultImports(defaultImports);
- builder.addCodeFolderImports(codeFolderImports);
- builder.addFoundImports(foundImports);
+ PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen(
+ footerWriter,
+ resultBuilder,
+ insertPoint,
+ false
+ );
- return builder.build();
+ writeFooterContents(decoratedWriter, resultBuilder);
+
+ decoratedWriter.finish();
+
+ return resultBuilder.build();
+ }
+
+ /**
+ * Write the contents of the header using a prebuilt print writer.
+ *
+ * @param decoratedWriter he writer though which the comment should be introduced.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeHeaderContents(PrintWriterWithEditGen decoratedWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ if (destinationPackageName.isPresent()) {
+ decoratedWriter.addCodeLine("package " + destinationPackageName.get() + ";");
+ decoratedWriter.addEmptyLine();
+ }
+
+ if (!isTesting) {
+ writePreprocessorComment(decoratedWriter, resultBuilder);
+ }
+
+ writeImports(decoratedWriter, resultBuilder);
+
+ boolean requiresClassHeader = mode == PdePreprocessor.Mode.STATIC;
+ requiresClassHeader = requiresClassHeader || mode == PdePreprocessor.Mode.ACTIVE;
+
+ boolean requiresStaticSketchHeader = mode == PdePreprocessor.Mode.STATIC;
+
+ if (requiresClassHeader) {
+ writeClassHeader(decoratedWriter, resultBuilder);
+ }
+
+ if (requiresStaticSketchHeader) {
+ writeStaticSketchHeader(decoratedWriter, resultBuilder);
+ }
+ }
+
+ /**
+ * Write the contents of the footer using a prebuilt print writer.
+ *
+ * @param decoratedWriter he writer though which the comment should be introduced.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeFooterContents(PrintWriterWithEditGen decoratedWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ decoratedWriter.addEmptyLine();
+
+ boolean requiresStaticSketchFooter = mode == PdePreprocessor.Mode.STATIC;
+ boolean requiresClassWrap = mode == PdePreprocessor.Mode.STATIC;
+ requiresClassWrap = requiresClassWrap || mode == PdePreprocessor.Mode.ACTIVE;
+
+ if (requiresStaticSketchFooter) {
+ writeStaticSketchFooter(decoratedWriter, resultBuilder);
+ }
+
+ if (requiresClassWrap) {
+ writeExtraFieldsAndMethods(decoratedWriter, resultBuilder);
+ if (!foundMain) {
+ writeMain(decoratedWriter, resultBuilder);
+ }
+ writeClassFooter(decoratedWriter, resultBuilder);
+ }
+ }
+
+ /**
+ * Comment out sketch code before it is moved elsewhere in resulting Java.
+ *
+ * @param headerWriter The writer though which the comment should be introduced.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writePreprocessorComment(PrintWriterWithEditGen headerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ String dateStr = new SimpleDateFormat("YYYY-MM-dd").format(new Date());
+
+ String newCode = String.format(
+ "/* autogenerated by Processing preprocessor v%s on %s */",
+ VERSION_STR,
+ dateStr
+ );
+
+ headerWriter.addCodeLine(newCode);
+ }
+
+ /**
+ * Add imports as part of conversion from processing sketch to Java code.
+ *
+ * @param headerWriter The writer though which the imports should be introduced.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeImports(PrintWriterWithEditGen headerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ writeImportList(headerWriter, coreImports, resultBuilder);
+ writeImportList(headerWriter, codeFolderImports, resultBuilder);
+ writeImportList(headerWriter, foundImports, resultBuilder);
+ writeImportList(headerWriter, defaultImports, resultBuilder);
+ }
+
+ /**
+ * Write a list of imports.
+ *
+ * @param headerWriter The writer though which the imports should be introduced.
+ * @param imports Collection of imports to introduce.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeImportList(PrintWriterWithEditGen headerWriter, List imports,
+ RewriteResultBuilder resultBuilder) {
+
+ writeImportList(headerWriter, imports.toArray(new ImportStatement[0]), resultBuilder);
+ }
+
+ /**
+ * Write a list of imports.
+ *
+ * @param headerWriter The writer though which the imports should be introduced.
+ * @param imports Collection of imports to introduce.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeImportList(PrintWriterWithEditGen headerWriter, ImportStatement[] imports,
+ RewriteResultBuilder resultBuilder) {
+
+ for (ImportStatement importDecl : imports) {
+ headerWriter.addCodeLine(importDecl.getFullSourceLine());
+ }
+ if (imports.length > 0) {
+ headerWriter.addEmptyLine();
+ }
+ }
+
+ /**
+ * Write the prefix which defines the enclosing class for the sketch.
+ *
+ * @param headerWriter The writer through which the header should be introduced.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeClassHeader(PrintWriterWithEditGen headerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ headerWriter.addCodeLine("public class " + sketchName + " extends PApplet {");
+
+ headerWriter.addEmptyLine();
+ }
+
+ /**
+ * Write the header for a static sketch (no methods).
+ *
+ * @param headerWriter The writer through which the header should be introduced.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeStaticSketchHeader(PrintWriterWithEditGen headerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ headerWriter.addCodeLine(indent1 + "public void setup() {");
+ }
+
+ /**
+ * Write the bottom of the sketch code for static mode.
+ *
+ * @param footerWriter The footer into which the text should be written.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeStaticSketchFooter(PrintWriterWithEditGen footerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ footerWriter.addCodeLine(indent2 + "noLoop();");
+ footerWriter.addCodeLine(indent1 + "}");
+ }
+
+ /**
+ * Write code supporting special functions like size.
+ *
+ * @param classBodyWriter The writer into which the code should be written. Should be for class
+ * body.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeExtraFieldsAndMethods(PrintWriterWithEditGen classBodyWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ if (!sizeRequiresRewrite) {
+ return;
+ }
+
+ String settingsOuterTemplate = indent1 + "public void settings() { %s }";
+
+ String settingsInner;
+ if (sizeIsFullscreen) {
+ String fullscreenInner = sketchRenderer == null ? "" : sketchRenderer;
+ settingsInner = String.format("fullScreen(%s);", fullscreenInner);
+ } else {
+
+ if (sketchWidth.isEmpty() || sketchHeight.isEmpty()) {
+ return;
+ }
+
+ StringJoiner argJoiner = new StringJoiner(",");
+ argJoiner.add(sketchWidth);
+ argJoiner.add(sketchHeight);
+
+ if (sketchRenderer != null) {
+ argJoiner.add(sketchRenderer);
+ }
+
+ settingsInner = String.format("size(%s);", argJoiner.toString());
+ }
+
+
+ String newCode = String.format(settingsOuterTemplate, settingsInner);
+
+ classBodyWriter.addEmptyLine();
+ classBodyWriter.addCodeLine(newCode);
+ }
+
+ /**
+ * Write the main method.
+ *
+ * @param footerWriter The writer into which the footer should be written.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeMain(PrintWriterWithEditGen footerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ footerWriter.addEmptyLine();
+ footerWriter.addCodeLine(indent1 + "static public void main(String[] passedArgs) {");
+ footerWriter.addCode(indent2 + "String[] appletArgs = new String[] { ");
+
+ { // assemble line with applet args
+ if (Preferences.getBoolean("export.application.fullscreen")) {
+ footerWriter.addCode("\"" + PApplet.ARGS_FULL_SCREEN + "\", ");
+
+ String bgColor = Preferences.get("run.present.bgcolor");
+ footerWriter.addCode("\"" + PApplet.ARGS_BGCOLOR + "=" + bgColor + "\", ");
+
+ if (Preferences.getBoolean("export.application.stop")) {
+ String stopColor = Preferences.get("run.present.stop.color");
+ footerWriter.addCode("\"" + PApplet.ARGS_STOP_COLOR + "=" + stopColor + "\", ");
+ } else {
+ footerWriter.addCode("\"" + PApplet.ARGS_HIDE_STOP + "\", ");
+ }
+ }
+ footerWriter.addCode("\"" + sketchName + "\"");
+ }
+
+ footerWriter.addCodeLine(" };");
+
+ footerWriter.addCodeLine(indent2 + "if (passedArgs != null) {");
+ footerWriter.addCodeLine(indent3 + "PApplet.main(concat(appletArgs, passedArgs));");
+ footerWriter.addCodeLine(indent2 + "} else {");
+ footerWriter.addCodeLine(indent3 + "PApplet.main(appletArgs);");
+ footerWriter.addCodeLine(indent2 + "}");
+ footerWriter.addCodeLine(indent1 + "}");
+ }
+
+ /**
+ * Write the end of the class body for the footer.
+ *
+ * @param footerWriter The writer into which the footer should be written.
+ * @param resultBuilder Builder for reporting out results to the caller.
+ */
+ protected void writeClassFooter(PrintWriterWithEditGen footerWriter,
+ RewriteResultBuilder resultBuilder) {
+
+ footerWriter.addCodeLine("}");
+ }
+
+ /*
+ * =========================
+ * === Supporting types. ===
+ * =========================
+ */
+
+ /**
+ * Listener for issues encountered while processing a valid pde parse tree.
+ */
+ public 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);
+ }
+
+ /**
+ * Decorator around a {TokenStreamRewriter}.
+ *
+ *
+ * Decorator around a {TokenStreamRewriter} which converts input commands into something that the
+ * rewriter can understand but also generates edits saved to an input RewriteResultBuilder.
+ * Requires a call to finish() after completion of preprocessing.
+ *
+ */
+ public static class PrintWriterWithEditGen {
+
+ private final TokenStreamRewriter writer;
+ private final RewriteResultBuilder rewriteResultBuilder;
+ private final int insertPoint;
+ private final StringBuilder editBuilder;
+ private final boolean before;
+
+ /**
+ * Create a new edit generator decorator.
+ *
+ * @param writer The writer to which edits should be immediately made.
+ * @param newRewriteResultBuilder The builder to which edits should be saved.
+ * @param newInsertPoint The point at which new values should be inserted.
+ * @param newBefore If true, the values will be inserted before the given insert point. If false,
+ * will, insert after the insertion point.
+ */
+ public PrintWriterWithEditGen(TokenStreamRewriter writer,
+ RewriteResultBuilder newRewriteResultBuilder, int newInsertPoint, boolean newBefore) {
+
+ this.writer = writer;
+ rewriteResultBuilder = newRewriteResultBuilder;
+ insertPoint = newInsertPoint;
+ editBuilder = new StringBuilder();
+ before = newBefore;
+ }
+
+ /**
+ * Add an empty line into the code.
+ */
+ public void addEmptyLine() {
+ addCode("\n");
+ }
+
+ /**
+ * Add code with a newline automatically appended.
+ *
+ * @param newCode The code to add.
+ */
+ public void addCodeLine(String newCode) {
+ addCode(newCode + "\n");
+ }
+
+ /**
+ * Add code without a new line.
+ *
+ * @param newCode The code to add.
+ */
+ public void addCode(String newCode) {
+ editBuilder.append(newCode);
+ }
+
+ /**
+ * Finalize edits made through this decorator.
+ */
+ public void finish() {
+ String newCode = editBuilder.toString();
+
+ if (before) {
+ rewriteResultBuilder.addEdit(PdeParseTreeListener.createInsertBefore(
+ insertPoint,
+ insertPoint,
+ newCode,
+ writer
+ ));
+ } else {
+ rewriteResultBuilder.addEdit(PdeParseTreeListener.insertAfter(
+ insertPoint,
+ newCode,
+ writer
+ ));
+ }
+
+ rewriteResultBuilder.addOffset(SyntaxUtil.getCount(newCode, "\n"));
+ }
+
+ }
+
+ /*
+ * ===========================================================
+ * === Utility functions to generate code edit operations. ===
+ * ===========================================================
+ */
+
+ /**
+ * Delete tokens between a start end end token inclusive.
+ *
+ * @param start The token to be deleted.
+ * @param stop The final token to be deleted.
+ * @param rewriter The rewriter with which to make the edit.
+ * @return The {TextTransform.Edit} corresponding to this change.
+ */
+ protected static TextTransform.Edit createDelete(Token start, Token stop,
+ TokenStreamRewriter rewriter) {
+
+ rewriter.delete(start, stop);
+
+ int startIndex = start.getStartIndex();
+ int length = stop.getStopIndex() - startIndex + 1;
+
+ return TextTransform.Edit.delete(
+ startIndex,
+ length
+ );
+ }
+
+ /**
+ * Insert text after a token.
+ *
+ * @param start The token after which the text should be inserted.
+ * @param text The text to insert.
+ * @param rewriter The rewriter with which to make the edit.
+ * @return The {TextTransform.Edit} corresponding to this change.
+ */
+ protected static TextTransform.Edit createInsertAfter(Token start, String text,
+ TokenStreamRewriter rewriter) {
+
+ rewriter.insertAfter(start, text);
+
+ return TextTransform.Edit.insert(
+ start.getStopIndex() + 1,
+ text
+ );
+ }
+
+ /**
+ * Insert text before a token.
+ *
+ * @param before Token before which the text should be inserted.
+ * @param text The text to insert.
+ * @param rewriter The rewriter with which to make the edit.
+ * @return The {TextTransform.Edit} corresponding to this change.
+ */
+ protected static TextTransform.Edit createInsertBefore(Token before, String text,
+ TokenStreamRewriter rewriter) {
+
+ rewriter.insertBefore(before, text);
+
+ return TextTransform.Edit.insert(
+ before.getStartIndex(),
+ text
+ );
+ }
+
+ /**
+ * Insert text before a position in code.
+ *
+ * @param before The location before which to insert the text in tokens.
+ * @param beforeOffset THe location before which to insert the text in chars.
+ * @param text The text to insert.
+ * @param rewriter The rewriter with which to make the edit.
+ * @return The {TextTransform.Edit} corresponding to this change.
+ */
+ protected static TextTransform.Edit createInsertBefore(int before, int beforeOffset, String text,
+ TokenStreamRewriter rewriter) {
+
+ rewriter.insertBefore(before, text);
+
+ return TextTransform.Edit.insert(
+ beforeOffset,
+ text
+ );
+ }
+
+ /**
+ * Insert text after a token.
+ *
+ * @param start The position after which the text should be inserted.
+ * @param text The text to insert.
+ * @param rewriter The rewriter with which to make the edit.
+ * @return The {TextTransform.Edit} corresponding to this change.
+ */
+ protected static TextTransform.Edit insertAfter(int start, String text,
+ TokenStreamRewriter rewriter) {
+ rewriter.insertAfter(start, text);
+
+ return TextTransform.Edit.insert(
+ start + 1,
+ text
+ );
+ }
+
+ /**
+ * Delete a single token.
+ *
+ * @param start The token to be deleted.
+ * @param rewriter The rewriter with which to make the edit.
+ * @return The {TextTransform.Edit} corresponding to this change.
+ */
+ protected static TextTransform.Edit createDelete(Token start, TokenStreamRewriter rewriter) {
+ rewriter.delete(start);
+ return TextTransform.Edit.delete(start.getStartIndex(), start.getText().length());
}
private List createPlainImportStatementInfos(List fullyQualifiedNames) {
@@ -756,4 +1320,5 @@ public class PdeParseTreeListener extends ProcessingBaseListener {
private ImportStatement createPlainImportStatementInfo(String fullyQualifiedName) {
return ImportStatement.parse(fullyQualifiedName);
}
+
}
diff --git a/java/src/processing/mode/java/preproc/PdePreprocessor.java b/java/src/processing/mode/java/preproc/PdePreprocessor.java
index 44cfd2e5c..6b2b4c526 100644
--- a/java/src/processing/mode/java/preproc/PdePreprocessor.java
+++ b/java/src/processing/mode/java/preproc/PdePreprocessor.java
@@ -24,7 +24,9 @@ package processing.mode.java.preproc;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
@@ -32,58 +34,92 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker;
import processing.app.Preferences;
import processing.app.SketchException;
-import processing.mode.java.preproc.code.ImportUtil;
import processing.mode.java.preproc.issue.PdeIssueEmitter;
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 final List defaultImports;
+ private final List coreImports;
+ private final Optional destinationPackage;
+
+ 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 can 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.
+ * @param newDefaultImports Imports provided for user convenience.
+ * @param newCoreImports Imports required for core or processing itself.
*/
- public PdePreprocessor(final String sketchName, final int tabSize, boolean isTesting) {
- this.sketchName = sketchName;
- this.tabSize = tabSize;
- this.isTesting = isTesting;
+ public PdePreprocessor(final String newSketchName, final int newTabSize, boolean newIsTesting,
+ final ParseTreeListenerFactory newFactory, List newDefaultImports,
+ List newCoreImports, Optional newDestinationPackage) {
+
+ sketchName = newSketchName;
+ tabSize = newTabSize;
+ isTesting = newIsTesting;
+ listenerFactory = newFactory;
+ defaultImports = newDefaultImports;
+ coreImports = newCoreImports;
+ destinationPackage = newDestinationPackage;
}
/**
@@ -151,10 +187,15 @@ 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,
+ destinationPackage
+ );
listener.setTesting(isTesting);
- listener.setCoreImports(ImportUtil.getCoreImports());
- listener.setDefaultImports(ImportUtil.getDefaultImports());
+ listener.setCoreImports(coreImports);
+ listener.setDefaultImports(defaultImports);
listener.setCodeFolderImports(codeFolderImports);
listener.setTreeErrorListener((x) -> { treeIssues.add(x); });
@@ -188,7 +229,7 @@ public class PdePreprocessor {
PrintWriter outPrintWriter = new PrintWriter(outWriter);
outPrintWriter.print(outputProgram);
- hasMain = listener.foundMain();
+ foundMain = listener.foundMain();
return listener.getResult();
}
@@ -199,20 +240,249 @@ public class PdePreprocessor {
* @return True if a main method was found. False otherwise.
*/
public boolean hasMain() {
- return hasMain;
+ return foundMain;
}
/**
- * Factory function to create a {PdeParseTreeListener} for use in preprocessing
+ * Get the more or processing-required imports that this preprocessor is using.
*
- * @param tokens The token stream for which the listener needs to be created.
- * @param sketchName The name of the sketch being preprocessed.
- * @return Newly created listener suitable for use in this {PdePreprocessor}.
+ * @return List of imports required by processing or this mode.
*/
- private PdeParseTreeListener createListener(CommonTokenStream tokens, String sketchName) {
- return new PdeParseTreeListener(tokens, sketchName, tabSize);
+ public List getCoreImports() {
+ return coreImports;
}
+ /**
+ * Get convenience imports provided on the user's behalf.
+ *
+ * @return Imports included by default but not required by processing or the mode.
+ */
+ public List getDefaultImports() {
+ return defaultImports;
+ }
+
+ /* ========================
+ * === 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 Optional> defaultImports;
+ private Optional> coreImports;
+ private Optional destinationPackage;
+
+ /**
+ * The imports required for the Java processing mode.
+ *
+ *
+ * The set of imports required by processing itself (in java mode) that are made public so that
+ * client code (like in modes) can modify and re-use this list.
+ *
+ */
+ public static final String[] BASE_CORE_IMPORTS = {
+ "processing.core.*",
+ "processing.data.*",
+ "processing.event.*",
+ "processing.opengl.*"
+ };
+
+ /**
+ * The imports provided as a convenience for the user.
+ *
+ *
+ * The imports that are not strictly required by processing sketches but that are included on
+ * behalf of the user that are made public so that client code (like in modes) can modify and
+ * re-use this list.
+ *
+ */
+ public static final String[] BASE_DEFAULT_IMPORTS = {
+ "java.util.HashMap",
+ "java.util.ArrayList",
+ "java.io.File",
+ "java.io.BufferedReader",
+ "java.io.PrintWriter",
+ "java.io.InputStream",
+ "java.io.OutputStream",
+ "java.io.IOException"
+ };
+
+ /**
+ * Create a new preprocessor builder.
+ *
+ *
+ * Create a new preprocessor builder which will use default values unless overridden prior to
+ * calling build. Note that client code should use {PdePreprocessor.builderFor} instead of
+ * this constructor.
+ *
+ *
+ * @param newSketchName The name of the sketch.
+ */
+ private PdePreprocessorBuilder(String newSketchName) {
+ sketchName = newSketchName;
+ tabSize = Optional.empty();
+ isTesting = Optional.empty();
+ parseTreeFactory = Optional.empty();
+ defaultImports = Optional.empty();
+ coreImports = Optional.empty();
+ destinationPackage = 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 setParseTreeListenerFactory(ParseTreeListenerFactory newFactory) {
+ parseTreeFactory = Optional.of(newFactory);
+ return this;
+ }
+
+ /**
+ * Indicate which imports are provided on behalf of the user for convenience.
+ *
+ * @param newDefaultImports The new set of default imports.
+ * @return This builder for method chaining.
+ */
+ public PdePreprocessorBuilder setDefaultImports(List newDefaultImports) {
+ defaultImports = Optional.of(newDefaultImports);
+ return this;
+ }
+
+ /**
+ * Indicate which imports are required by processing or the mode itself.
+ *
+ * @param newCoreImports The new set of core imports.
+ * @return This builder for method chaining.
+ */
+ public PdePreprocessorBuilder setCoreImports(List newCoreImports) {
+ coreImports = Optional.of(newCoreImports);
+ return this;
+ }
+
+ /**
+ * Specify to which package generated code should be assigned.
+ *
+ * @param newDestinationPackage The package to which output code should be assigned.
+ * @return This builder for method chaining.
+ */
+ public PdePreprocessorBuilder setDestinationPackage(String newDestinationPackage) {
+ destinationPackage = Optional.of(newDestinationPackage);
+ 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
+ );
+
+ List effectiveDefaultImports = defaultImports.orElseGet(
+ () -> Arrays.asList(BASE_DEFAULT_IMPORTS)
+ );
+
+ List effectiveCoreImports = coreImports.orElseGet(
+ () -> Arrays.asList(BASE_CORE_IMPORTS)
+ );
+
+ return new PdePreprocessor(
+ sketchName,
+ effectiveTabSize,
+ effectiveIsTesting,
+ effectiveFactory,
+ effectiveDefaultImports,
+ effectiveCoreImports,
+ destinationPackage
+ );
+ }
+
+ }
+
+ /**
+ * 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.
+ * @param packageName The optional package name for generated code.
+ * @return The newly created listener.
+ */
+ PdeParseTreeListener build(CommonTokenStream tokens, String sketchName, int tabSize,
+ Optional packageName);
+
+ }
+
+
+ /* ==================================
+ * === Internal Utility Functions ===
+ * ==================================
+ */
+
/**
* Utility function to substitute non ascii characters for escaped unicode character sequences.
*
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/code/CodeEditOperationUtil.java b/java/src/processing/mode/java/preproc/code/CodeEditOperationUtil.java
deleted file mode 100644
index 0d5e13825..000000000
--- a/java/src/processing/mode/java/preproc/code/CodeEditOperationUtil.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package processing.mode.java.preproc.code;
-
-import org.antlr.v4.runtime.Token;
-import org.antlr.v4.runtime.TokenStreamRewriter;
-import processing.mode.java.pdex.TextTransform;
-
-
-/**
- * Utility which generates and performs code edit operations.
- *
- *
- * Utility which generates and performs code edit operations, performing the edit immediately
- * within a ANTLR rewriter but also generating a {TextTransform.Edit} for use with the JDT.
- *
- */
-public class CodeEditOperationUtil {
-
- /**
- * Delete a single token.
- *
- * @param start The token to be deleted.
- * @param rewriter The rewriter in which to immediately edit.
- * @return The {TextTransform.Edit} corresponding to this change.
- */
- public static TextTransform.Edit createDelete(Token start, TokenStreamRewriter rewriter) {
- rewriter.delete(start);
- return TextTransform.Edit.delete(start.getStartIndex(), start.getText().length());
- }
-
- /**
- * Delete tokens between a start end end token inclusive.
- *
- * @param start The token to be deleted.
- * @param stop The final token to be deleted.
- * @param rewriter The rewriter in which to immediately edit.
- * @return The {TextTransform.Edit} corresponding to this change.
- */
- public static TextTransform.Edit createDelete(Token start, Token stop,
- TokenStreamRewriter rewriter) {
-
- rewriter.delete(start, stop);
-
- int startIndex = start.getStartIndex();
- int length = stop.getStopIndex() - startIndex + 1;
-
- return TextTransform.Edit.delete(
- startIndex,
- length
- );
- }
-
- /**
- * Insert text after a token.
- *
- * @param start The position after which the text should be inserted.
- * @param text The text to insert.
- * @param rewriter The rewriter in which to immediately edit.
- * @return The {TextTransform.Edit} corresponding to this change.
- */
- public static TextTransform.Edit createInsertAfter(int start, String text,
- TokenStreamRewriter rewriter) {
-
- rewriter.insertAfter(start, text);
-
- return TextTransform.Edit.insert(
- start + 1,
- text
- );
- }
-
- /**
- * Insert text after a token.
- *
- * @param start The token after which the text should be inserted.
- * @param text The text to insert.
- * @param rewriter The rewriter in which to immediately edit.
- * @return The {TextTransform.Edit} corresponding to this change.
- */
- public static TextTransform.Edit createInsertAfter(Token start, String text,
- TokenStreamRewriter rewriter) {
-
- rewriter.insertAfter(start, text);
-
- return TextTransform.Edit.insert(
- start.getStopIndex() + 1,
- text
- );
- }
-
- /**
- * Insert text before a token.
- *
- * @param before Token before which the text should be inserted.
- * @param text The text to insert.
- * @param rewriter The rewriter in which to immediately edit.
- * @return The {TextTransform.Edit} corresponding to this change.
- */
- public static TextTransform.Edit createInsertBefore(Token before, String text,
- TokenStreamRewriter rewriter) {
-
- rewriter.insertBefore(before, text);
-
- return TextTransform.Edit.insert(
- before.getStartIndex(),
- text
- );
- }
-
- /**
- * Insert text before a position in code.
- *
- * @param before The location before which to insert the text in tokens.
- * @param beforeOffset THe location before which to insert the text in chars.
- * @param text The text to insert.
- * @param rewriter The rewriter in which to immediately edit.
- * @return The {TextTransform.Edit} corresponding to this change.
- */
- public static TextTransform.Edit createInsertBefore(int before, int beforeOffset, String text,
- TokenStreamRewriter rewriter) {
-
- rewriter.insertBefore(before, text);
-
- return TextTransform.Edit.insert(
- beforeOffset,
- text
- );
- }
-
-}
diff --git a/java/src/processing/mode/java/preproc/code/ImportUtil.java b/java/src/processing/mode/java/preproc/code/ImportUtil.java
deleted file mode 100644
index 6b814914d..000000000
--- a/java/src/processing/mode/java/preproc/code/ImportUtil.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package processing.mode.java.preproc.code;
-
-
-/**
- * Utility to assist with preprocessing imports.
- */
-public class ImportUtil {
-
- /**
- * Get the imports required by processing itself.
- *
- * @return List of imports required by processing itself.
- */
- public static String[] getCoreImports() {
- return new String[] {
- "processing.core.*",
- "processing.data.*",
- "processing.event.*",
- "processing.opengl.*"
- };
- }
-
- /**
- * Get the list of imports included by default on behalf of the user.
- *
- * @return List of "default" imports not required for processing but included for user
- * convenience.
- */
- public static String[] getDefaultImports() {
- // These may change in-between (if the prefs panel adds this option)
- //String prefsLine = Preferences.get("preproc.imports");
- //return PApplet.splitTokens(prefsLine, ", ");
- return new String[] {
- "java.util.HashMap",
- "java.util.ArrayList",
- "java.io.File",
- "java.io.BufferedReader",
- "java.io.PrintWriter",
- "java.io.InputStream",
- "java.io.OutputStream",
- "java.io.IOException"
- };
- }
-
-}
diff --git a/java/src/processing/mode/java/preproc/code/PrintWriterWithEditGen.java b/java/src/processing/mode/java/preproc/code/PrintWriterWithEditGen.java
index 502356e11..6e855c3be 100644
--- a/java/src/processing/mode/java/preproc/code/PrintWriterWithEditGen.java
+++ b/java/src/processing/mode/java/preproc/code/PrintWriterWithEditGen.java
@@ -1,91 +1,7 @@
package processing.mode.java.preproc.code;
import org.antlr.v4.runtime.TokenStreamRewriter;
+import processing.mode.java.preproc.PdeParseTreeListener;
-/**
- * Decorator around a {TokenStreamRewriter}.
- *
- *
- * Decorator around a {TokenStreamRewriter} which converts input commands into something that the
- * rewriter can understand but also generates edits saved to an input RewriteResultBuilder.
- * Requires a call to finish() after completion of preprocessing.
- *
- */
-public class PrintWriterWithEditGen {
- private final TokenStreamRewriter writer;
- private final RewriteResultBuilder rewriteResultBuilder;
- private final int insertPoint;
- private final StringBuilder editBuilder;
- private final boolean before;
-
- /**
- * Create a new edit generator decorator.
- *
- * @param writer The writer to which edits should be immediately made.
- * @param newRewriteResultBuilder The builder to which edits should be saved.
- * @param newInsertPoint The point at which new values should be inserted.
- * @param newBefore If true, the values will be inserted before the given insert point. If false,
- * will, insert after the insertion point.
- */
- public PrintWriterWithEditGen(TokenStreamRewriter writer,
- RewriteResultBuilder newRewriteResultBuilder, int newInsertPoint, boolean newBefore) {
-
- this.writer = writer;
- rewriteResultBuilder = newRewriteResultBuilder;
- insertPoint = newInsertPoint;
- editBuilder = new StringBuilder();
- before = newBefore;
- }
-
- /**
- * Add an empty line into the code.
- */
- public void addEmptyLine() {
- addCode("\n");
- }
-
- /**
- * Add code with a newline automatically appended.
- *
- * @param newCode The code to add.
- */
- public void addCodeLine(String newCode) {
- addCode(newCode + "\n");
- }
-
- /**
- * Add code without a new line.
- *
- * @param newCode The code to add.
- */
- public void addCode(String newCode) {
- editBuilder.append(newCode);
- }
-
- /**
- * Finalize edits made through this decorator.
- */
- public void finish() {
- String newCode = editBuilder.toString();
-
- if (before) {
- rewriteResultBuilder.addEdit(CodeEditOperationUtil.createInsertBefore(
- insertPoint,
- insertPoint,
- newCode,
- writer
- ));
- } else {
- rewriteResultBuilder.addEdit(CodeEditOperationUtil.createInsertAfter(
- insertPoint,
- newCode,
- writer
- ));
- }
-
- rewriteResultBuilder.addOffset(SyntaxUtil.getCount(newCode, "\n"));
- }
-
-}
diff --git a/java/src/processing/mode/java/preproc/code/RewriteParams.java b/java/src/processing/mode/java/preproc/code/RewriteParams.java
deleted file mode 100644
index 295a4879c..000000000
--- a/java/src/processing/mode/java/preproc/code/RewriteParams.java
+++ /dev/null
@@ -1,229 +0,0 @@
-package processing.mode.java.preproc.code;
-
-import org.antlr.v4.runtime.TokenStreamRewriter;
-import processing.mode.java.pdex.ImportStatement;
-import processing.mode.java.preproc.PdePreprocessor;
-
-import java.util.List;
-import java.util.Optional;
-
-
-/**
- * Set of parameters required for re-writing as part of sketch preprocessing.
- */
-public class RewriteParams {
-
- private final String version;
- private final String sketchName;
- private final boolean isTesting;
- private final TokenStreamRewriter rewriter;
- private final PdePreprocessor.Mode mode;
- private final boolean foundMain;
- private final int lineOffset;
- private final List coreImports;
- private final List defaultImports;
- private final List codeFolderImports;
- private final List foundImports;
- private final Optional sketchWidth;
- private final Optional sketchHeight;
- private final Optional sketchRenderer;
- private final boolean isSizeValidInGlobal;
- private final boolean isSizeFullscreen;
-
- /**
- * Create a new set of parameters.
- *
- * @param newVersion The version of the preprocessor.
- * @param newSketchName The name of the sketch.
- * @param newisTesting Flag indicating if this is being run as part of automated testing.
- * @param newRewriter The rewriter into which edits should be made.
- * @param newMode The mode (like STATIC) in which processing is being run.
- * @param newFoundMain Flag indicating if a user-provided main method was found in preprocessing.
- * @param newLineOffset The line offset of the preprocessor prior to rewrite.
- * @param newCoreImports The set of imports to include that are required for processing.
- * @param newDefaultImports The set of imports included for user convenience.
- * @param newCodeFolderImports The imports required to include other code in the code folder.
- * @param newFoundImports The imports included by the user.
- * @param newSketchWidth The width of the sketch or code used to generate it. If not included,
- * call to size will not be made.
- * @param newSketchHeight The height of the sketch or code used to generate it. If not included,
- * call to size will not be made.
- * @param newSketchRenderer The renderer like P2D.
- * @param newIsSizeValidInGlobal Flag indicating if a call to size is valid when that call to size
- * is made from sketch global context.
- * @param newSizeIsFullscreen Indicate if in fullscreen mode.
- */
- public RewriteParams(String newVersion, String newSketchName, boolean newisTesting,
- TokenStreamRewriter newRewriter, PdePreprocessor.Mode newMode,
- boolean newFoundMain, int newLineOffset, List newCoreImports,
- List newDefaultImports, List newCodeFolderImports,
- List newFoundImports, Optional newSketchWidth,
- Optional newSketchHeight, Optional newSketchRenderer,
- boolean newIsSizeValidInGlobal, boolean newSizeIsFullscreen) {
-
- version = newVersion;
- sketchName = newSketchName;
- isTesting = newisTesting;
- rewriter = newRewriter;
- mode = newMode;
- foundMain = newFoundMain;
- lineOffset = newLineOffset;
- coreImports = newCoreImports;
- defaultImports = newDefaultImports;
- codeFolderImports = newCodeFolderImports;
- foundImports = newFoundImports;
- sketchWidth = newSketchWidth;
- sketchHeight = newSketchHeight;
- sketchRenderer = newSketchRenderer;
- isSizeValidInGlobal = newIsSizeValidInGlobal;
- isSizeFullscreen = newSizeIsFullscreen;
- }
-
- /**
- * Get the version of the preprocessor.
- *
- * @return The version of the preprocessor.
- */
- public String getVersion() {
- return version;
- }
-
- /**
- * The user provided or automated name of the sketch.
- *
- * @return The name of the sketch.
- */
- public String getSketchName() {
- return sketchName;
- }
-
- /**
- * Determine if this code is being exercised in automated test.
- *
- * @return Flag indicating if this is being run as part of automated testing.
- */
- public boolean getisTesting() {
- return isTesting;
- }
-
- /**
- * Get the rewriter to be used in rewriting.
- *
- * @return The rewriter into which edits should be made.
- */
- public TokenStreamRewriter getRewriter() {
- return rewriter;
- }
-
- /**
- * Get the mode in which processing is being run.
- *
- * @return The mode (like STATIC) in which processing is being run.
- */
- public PdePreprocessor.Mode getMode() {
- return mode;
- }
-
- /**
- * Determine if the user provided their own main method.
- *
- * @return Flag indicating if a user-provided main method was found in preprocessing.
- */
- public boolean getFoundMain() {
- return foundMain;
- }
-
- /**
- * Determine the line offset of the preprocessor prior to rewrite.
- *
- * @return The line offset of the preprocessor prior to rewrite.
- */
- public int getLineOffset() {
- return lineOffset;
- }
-
- /**
- * Get imports required for processing.
- *
- * @return The set of imports to include that are required for processing.
- */
- public List getCoreImports() {
- return coreImports;
- }
-
- /**
- * Get the imports added for user convenience.
- *
- * @return The set of imports included for user convenience.
- */
- public List getDefaultImports() {
- return defaultImports;
- }
-
- /**
- * The imports required to access other code in the code folder.
- *
- * @return The imports required to include other code in the code folder.
- */
- public List getCodeFolderImports() {
- return codeFolderImports;
- }
-
- /**
- * Get the users included by the user.
- *
- * @return The imports included by the user.
- */
- public List getFoundImports() {
- return foundImports;
- }
-
- /**
- * Get the code used to determine sketch width if given.
- *
- * @return The width of the sketch or code used to generate it. If not included, call to size will
- * not be made. Not included means it is an empty optional.
- */
- public Optional getSketchWidth() {
- return sketchWidth;
- }
-
- /**
- * Get the code used to determine sketch height if given.
- *
- * @return The height of the sketch or code used to generate it. If not included, call to size
- * will not be made. Not included means it is an empty optional.
- */
- public Optional getSketchHeight() {
- return sketchHeight;
- }
-
- /**
- * Get the user provided renderer or an empty optional if user has not provided renderer.
- *
- * @return The renderer like P2D if given.
- */
- public Optional getSketchRenderer() {
- return sketchRenderer;
- }
-
- /**
- * Determine if a call to size has been made in sketch global context.
- *
- * @return Flag indicating if a call to size is valid when that call to size is made from sketch
- * global context.
- */
- public boolean getIsSizeValidInGlobal() {
- return isSizeValidInGlobal;
- }
-
- /**
- * Determine if running in fullscreen.
- *
- * @return Flag indicating if in running in fullscreen.
- */
- public boolean getIsSizeFullscreen() {
- return isSizeFullscreen;
- }
-
-}
diff --git a/java/src/processing/mode/java/preproc/code/RewriteParamsBuilder.java b/java/src/processing/mode/java/preproc/code/RewriteParamsBuilder.java
deleted file mode 100644
index 8297756ba..000000000
--- a/java/src/processing/mode/java/preproc/code/RewriteParamsBuilder.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package processing.mode.java.preproc.code;
-
-import org.antlr.v4.runtime.TokenStreamRewriter;
-import processing.mode.java.pdex.ImportStatement;
-import processing.mode.java.preproc.PdePreprocessor;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Optional;
-
-
-/**
- * Builder to help generate a {RewriteParams}.
- */
-public class RewriteParamsBuilder {
-
- private final String version;
-
- private Optional sketchName;
- private Optional isTesting;
- private Optional rewriter;
- private Optional mode;
- private Optional foundMain;
- private Optional lineOffset;
- private Optional sketchWidth;
- private Optional sketchHeight;
- private Optional sketchRenderer;
- private Optional isSizeValidInGlobal;
- private Optional isSizeFullscreen;
-
- private ArrayList coreImports;
- private ArrayList defaultImports;
- private ArrayList codeFolderImports;
- private ArrayList foundImports;
-
- /**
- * Create a new params build.
- *
- * @param newVersion The version to include in generated RewriteParams.
- */
- public RewriteParamsBuilder(String newVersion) {
- version = newVersion;
-
- coreImports = new ArrayList<>();
- defaultImports = new ArrayList<>();
- codeFolderImports = new ArrayList<>();
- foundImports = new ArrayList<>();
-
- sketchName = Optional.empty();
- isTesting = Optional.empty();
- rewriter = Optional.empty();
- mode = Optional.empty();
- foundMain = Optional.empty();
- lineOffset = Optional.empty();
- sketchWidth = Optional.empty();
- sketchHeight = Optional.empty();
- sketchRenderer = Optional.empty();
- isSizeValidInGlobal = Optional.empty();
- isSizeFullscreen = Optional.empty();
- }
-
- /**
- * Specify the name of the sketch.
- *
- * @param newSketchName The name of the sketch.
- */
- public void setSketchName(String newSketchName) {
- sketchName = Optional.ofNullable(newSketchName);
- }
-
- /**
- * Specify if this is being run as part of automated testing.
- *
- * @param newisTesting Flag indicating if this is being run as part of automated testing.
- */
- public void setisTesting(boolean newisTesting) {
- isTesting = Optional.of(newisTesting);
- }
-
- /**
- * Specify rewriter into which edits should be made.
- *
- * @param newRewriter The rewriter into which edits should be made.
- */
- public void setRewriter(TokenStreamRewriter newRewriter) {
- rewriter = Optional.ofNullable(newRewriter);
- }
-
- /**
- * Specify mode (like STATIC) in which processing is being run.
- *
- * @param newMode The mode (like STATIC) in which processing is being run.
- */
- public void setMode(PdePreprocessor.Mode newMode) {
- mode = Optional.ofNullable(newMode);
- }
-
- /**
- * Specify if a user-provided main method was found in preprocessing.
- *
- * @param newFoundMain Flag indicating if a user-provided main method was found in preprocessing.
- */
- public void setFoundMain(boolean newFoundMain) {
- foundMain = Optional.of(newFoundMain);
- }
-
- /**
- * Specify line offset of the preprocessor prior to rewrite.
- *
- * @param newLineOffset The line offset of the preprocessor prior to rewrite.
- */
- public void setLineOffset(int newLineOffset) {
- lineOffset = Optional.of(newLineOffset);
- }
-
- /**
- * Specify width of the sketch.
- *
- * @param newSketchWidth The width of the sketch or code used to generate it. If not included,
- * call to size will not be made.
- */
- public void setSketchWidth(String newSketchWidth) {
- sketchWidth = Optional.ofNullable(newSketchWidth);
- }
-
- /**
- * Specify height of the sketch.
- *
- * @param newSketchHeight The height of the sketch or code used to generate it. If not included,
- * call to size will not be made.
- */
- public void setSketchHeight(String newSketchHeight) {
- sketchHeight = Optional.ofNullable(newSketchHeight);
- }
-
- /**
- * Specify renderer like P2D.
- *
- * @param newSketchRenderer The renderer like P2D.
- */
- public void setSketchRenderer(String newSketchRenderer) {
- sketchRenderer = Optional.ofNullable(newSketchRenderer);
- }
-
- /**
- * Specify if the user made a valid call to size in sketch global context.
- *
- * @param newIsSizeValidInGlobal Flag indicating if a call to size is valid when that call to size
- * is made from sketch global context.
- */
- public void setIsSizeValidInGlobal(boolean newIsSizeValidInGlobal) {
- isSizeValidInGlobal = Optional.of(newIsSizeValidInGlobal);
- }
-
- /**
- * Specify if running in fullscreen.
- *
- * @param newIsSizeFullscreen Flag indicating if running in fullscreen.
- */
- public void setIsSizeFullscreen(boolean newIsSizeFullscreen) {
- isSizeFullscreen = Optional.of(newIsSizeFullscreen);
- }
-
- /**
- * Add imports required for processing to function.
- *
- * @param newImports The set of imports to include that are required for processing.
- */
- public void addCoreImports(Collection newImports) {
- coreImports.addAll(newImports);
- }
-
- /**
- * Add imports that are included ahead of time for the user.
- *
- * @param newImports The set of imports included for user convenience.
- */
- public void addDefaultImports(Collection newImports) {
- defaultImports.addAll(newImports);
- }
-
- /**
- * Add imports required for the sketch to reach code in its own code folder.
- *
- * @param newImports The imports required to include other code in the code folder.
- */
- public void addCodeFolderImports(Collection newImports) {
- codeFolderImports.addAll(newImports);
- }
-
- /**
- * Add imports included manually by the user.
- *
- * @param newImports The imports included by the user.
- */
- public void addFoundImports(Collection newImports) {
- foundImports.addAll(newImports);
- }
-
- /**
- * Build a new set of rewrite parameters.
- *
- * @return Parameters required to execute {RewriterCodeGenerator};
- */
- public RewriteParams build() {
- if (sketchName.isEmpty()) {
- throw new RuntimeException("Expected sketchName to be set");
- }
-
- if (isTesting.isEmpty()) {
- throw new RuntimeException("Expected isTesting to be set");
- }
-
- if (rewriter.isEmpty()) {
- throw new RuntimeException("Expected rewriter to be set");
- }
-
- if (mode.isEmpty()) {
- throw new RuntimeException("Expected mode to be set");
- }
-
- if (foundMain.isEmpty()) {
- throw new RuntimeException("Expected foundMain to be set");
- }
-
- if (lineOffset.isEmpty()) {
- throw new RuntimeException("Expected lineOffset to be set");
- }
-
- if (isSizeValidInGlobal.isEmpty()) {
- throw new RuntimeException("Expected isSizeValidInGlobal to be set");
- }
-
- if (isSizeFullscreen.isEmpty()) {
- throw new RuntimeException("Expected isSizeFullscreen to be set");
- }
-
- return new RewriteParams(
- version,
- sketchName.get(),
- isTesting.get(),
- rewriter.get(),
- mode.get(),
- foundMain.get(),
- lineOffset.get(),
- coreImports,
- defaultImports,
- codeFolderImports,
- foundImports,
- sketchWidth,
- sketchHeight,
- sketchRenderer,
- isSizeValidInGlobal.get(),
- isSizeFullscreen.get()
- );
- }
-}
diff --git a/java/src/processing/mode/java/preproc/code/RewriterCodeGenerator.java b/java/src/processing/mode/java/preproc/code/RewriterCodeGenerator.java
deleted file mode 100644
index 923d7aeea..000000000
--- a/java/src/processing/mode/java/preproc/code/RewriterCodeGenerator.java
+++ /dev/null
@@ -1,333 +0,0 @@
-package processing.mode.java.preproc.code;
-
-import org.antlr.v4.runtime.TokenStreamRewriter;
-import processing.app.Preferences;
-import processing.core.PApplet;
-import processing.mode.java.pdex.ImportStatement;
-import processing.mode.java.preproc.PdePreprocessor;
-
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.StringJoiner;
-
-
-/**
- * Utility to rewrite code as part of preprocessing.
- */
-public class RewriterCodeGenerator {
-
- private final String indent1;
- private final String indent2;
- private final String indent3;
-
- /**
- * Create a new rewriter.
- *
- * @param indentSize Number of spaces in the indent.
- */
- public RewriterCodeGenerator(int indentSize) {
- final char[] indentChars = new char[indentSize];
- Arrays.fill(indentChars, ' ');
- indent1 = new String(indentChars);
- indent2 = indent1 + indent1;
- indent3 = indent2 + indent1;
- }
-
- /**
- * Write preface code to wrap sketch code so that it is contained within a proper Java definition.
- *
- * @param headerWriter The writer into which the header should be written.
- * @param params The parameters for the rewrite.
- * @return Information about the completed rewrite.
- */
- public RewriteResult writeHeader(TokenStreamRewriter headerWriter, RewriteParams params) {
-
- RewriteResultBuilder resultBuilder = new RewriteResultBuilder();
-
- PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen(
- headerWriter,
- resultBuilder,
- 0,
- true
- );
-
- if (!params.getisTesting()) writePreprocessorComment(decoratedWriter, params, resultBuilder);
- writeImports(decoratedWriter, params, resultBuilder);
-
- PdePreprocessor.Mode mode = params.getMode();
-
- boolean requiresClassHeader = mode == PdePreprocessor.Mode.STATIC;
- requiresClassHeader = requiresClassHeader || mode == PdePreprocessor.Mode.ACTIVE;
-
- boolean requiresStaticSketchHeader = mode == PdePreprocessor.Mode.STATIC;
-
- if (requiresClassHeader) {
- writeClassHeader(decoratedWriter, params, resultBuilder);
- }
-
- if (requiresStaticSketchHeader) {
- writeStaticSketchHeader(decoratedWriter, params, resultBuilder);
- }
-
- decoratedWriter.finish();
-
- return resultBuilder.build();
- }
-
- /**
- * Write the footer for a sketch (finishes the constructs introduced in header like class def).
- *
- * @param footerWriter The writer through which the footer should be introduced.
- * @param params The parameters for the rewrite.
- * @param insertPoint The loction at which the footer should be written.
- * @return Information about the completed rewrite.
- */
- public RewriteResult writeFooter(TokenStreamRewriter footerWriter, RewriteParams params,
- int insertPoint) {
-
- RewriteResultBuilder resultBuilder = new RewriteResultBuilder();
-
- PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen(
- footerWriter,
- resultBuilder,
- insertPoint,
- false
- );
-
- decoratedWriter.addEmptyLine();
-
- PdePreprocessor.Mode mode = params.getMode();
-
- boolean requiresStaticSketchFooter = mode == PdePreprocessor.Mode.STATIC;
- boolean requiresClassWrap = mode == PdePreprocessor.Mode.STATIC;
- requiresClassWrap = requiresClassWrap || mode == PdePreprocessor.Mode.ACTIVE;
-
- if (requiresStaticSketchFooter) {
- writeStaticSketchFooter(decoratedWriter, params, resultBuilder);
- }
-
- if (requiresClassWrap) {
- writeExtraFieldsAndMethods(decoratedWriter, params, resultBuilder);
- if (!params.getFoundMain()) writeMain(decoratedWriter, params, resultBuilder);
- writeClassFooter(decoratedWriter, params, resultBuilder);
- }
-
- decoratedWriter.finish();
-
- return resultBuilder.build();
- }
-
- /**
- * Comment out sketch code before it is moved elsewhere in resulting Java.
- *
- * @param headerWriter The writer though which the comment should be introduced.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writePreprocessorComment(PrintWriterWithEditGen headerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- String dateStr = new SimpleDateFormat("YYYY-MM-dd").format(new Date());
-
- String newCode = String.format(
- "/* autogenerated by Processing preprocessor v%s on %s */",
- params.getVersion(),
- dateStr
- );
-
- headerWriter.addCodeLine(newCode);
- }
-
- /**
- * Add imports as part of conversion from processing sketch to Java code.
- *
- * @param headerWriter The writer though which the imports should be introduced.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeImports(PrintWriterWithEditGen headerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- writeImportList(headerWriter, params.getCoreImports(), params, resultBuilder);
- writeImportList(headerWriter, params.getCodeFolderImports(), params, resultBuilder);
- writeImportList(headerWriter, params.getFoundImports(), params, resultBuilder);
- writeImportList(headerWriter, params.getDefaultImports(), params, resultBuilder);
- }
-
- /**
- * Write a list of imports.
- *
- * @param headerWriter The writer though which the imports should be introduced.
- * @param imports Collection of imports to introduce.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeImportList(PrintWriterWithEditGen headerWriter, List imports, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- writeImportList(headerWriter, imports.toArray(new ImportStatement[0]), params, resultBuilder);
- }
-
- /**
- * Write a list of imports.
- *
- * @param headerWriter The writer though which the imports should be introduced.
- * @param imports Collection of imports to introduce.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeImportList(PrintWriterWithEditGen headerWriter, ImportStatement[] imports, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- for (ImportStatement importDecl : imports) {
- headerWriter.addCodeLine(importDecl.getFullSourceLine());
- }
- if (imports.length > 0) {
- headerWriter.addEmptyLine();
- }
- }
-
- /**
- * Write the prefix which defines the enclosing class for the sketch.
- *
- * @param headerWriter The writer through which the header should be introduced.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeClassHeader(PrintWriterWithEditGen headerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- headerWriter.addCodeLine("public class " + params.getSketchName() + " extends PApplet {");
-
- headerWriter.addEmptyLine();
- }
-
- /**
- * Write the header for a static sketch (no methods).
- *
- * @param headerWriter The writer through which the header should be introduced.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeStaticSketchHeader(PrintWriterWithEditGen headerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- headerWriter.addCodeLine(indent1 + "public void setup() {");
- }
-
- /**
- * Write the bottom of the sketch code for static mode.
- *
- * @param footerWriter The footer into which the text should be written.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeStaticSketchFooter(PrintWriterWithEditGen footerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- footerWriter.addCodeLine(indent2 + "noLoop();");
- footerWriter.addCodeLine(indent1 + "}");
- }
-
- /**
- * Write code supporting speical functions like size.
- *
- * @param classBodyWriter The writer into which the code should be written. Should be for class
- * body.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeExtraFieldsAndMethods(PrintWriterWithEditGen classBodyWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- if (!params.getIsSizeValidInGlobal()) {
- return;
- }
-
- String settingsOuterTemplate = indent1 + "public void settings() { %s }";
-
- String settingsInner;
- if (params.getIsSizeFullscreen()) {
- String fullscreenInner = params.getSketchRenderer().orElse("");
- settingsInner = String.format("fullScreen(%s);", fullscreenInner);
- } else {
-
- if (params.getSketchWidth().isEmpty() || params.getSketchHeight().isEmpty()) {
- return;
- }
-
- StringJoiner argJoiner = new StringJoiner(",");
- argJoiner.add(params.getSketchWidth().get());
- argJoiner.add(params.getSketchHeight().get());
-
- if (params.getSketchRenderer().isPresent()) {
- argJoiner.add(params.getSketchRenderer().get());
- }
-
- settingsInner = String.format("size(%s);", argJoiner.toString());
- }
-
-
- String newCode = String.format(settingsOuterTemplate, settingsInner);
-
- classBodyWriter.addEmptyLine();
- classBodyWriter.addCodeLine(newCode);
- }
-
- /**
- * Write the main method.
- *
- * @param footerWriter The writer into which the footer should be written.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeMain(PrintWriterWithEditGen footerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- footerWriter.addEmptyLine();
- footerWriter.addCodeLine(indent1 + "static public void main(String[] passedArgs) {");
- footerWriter.addCode(indent2 + "String[] appletArgs = new String[] { ");
-
- { // assemble line with applet args
- if (Preferences.getBoolean("export.application.fullscreen")) {
- footerWriter.addCode("\"" + PApplet.ARGS_FULL_SCREEN + "\", ");
-
- String bgColor = Preferences.get("run.present.bgcolor");
- footerWriter.addCode("\"" + PApplet.ARGS_BGCOLOR + "=" + bgColor + "\", ");
-
- if (Preferences.getBoolean("export.application.stop")) {
- String stopColor = Preferences.get("run.present.stop.color");
- footerWriter.addCode("\"" + PApplet.ARGS_STOP_COLOR + "=" + stopColor + "\", ");
- } else {
- footerWriter.addCode("\"" + PApplet.ARGS_HIDE_STOP + "\", ");
- }
- }
- footerWriter.addCode("\"" + params.getSketchName() + "\"");
- }
-
- footerWriter.addCodeLine(" };");
-
- footerWriter.addCodeLine(indent2 + "if (passedArgs != null) {");
- footerWriter.addCodeLine(indent3 + "PApplet.main(concat(appletArgs, passedArgs));");
- footerWriter.addCodeLine(indent2 + "} else {");
- footerWriter.addCodeLine(indent3 + "PApplet.main(appletArgs);");
- footerWriter.addCodeLine(indent2 + "}");
- footerWriter.addCodeLine(indent1 + "}");
- }
-
- /**
- * Write the end of the class body for the footer.
- *
- * @param footerWriter The writer into which the footer should be written.
- * @param params The parameters for the rewrite.
- * @param resultBuilder Builder for reporting out results to the caller.
- */
- private void writeClassFooter(PrintWriterWithEditGen footerWriter, RewriteParams params,
- RewriteResultBuilder resultBuilder) {
-
- footerWriter.addCodeLine("}");
- }
-
-}
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/src/processing/mode/java/runner/Runner.java b/java/src/processing/mode/java/runner/Runner.java
index b19d6c030..04897afa4 100644
--- a/java/src/processing/mode/java/runner/Runner.java
+++ b/java/src/processing/mode/java/runner/Runner.java
@@ -662,10 +662,8 @@ public class Runner implements MessageConsumer {
protected Connector findConnector(String connectorName) {
-// List connectors =
-// com.sun.jdi.Bootstrap.virtualMachineManager().allConnectors();
List connectors =
- org.eclipse.jdi.Bootstrap.virtualMachineManager().allConnectors();
+ com.sun.jdi.Bootstrap.virtualMachineManager().allConnectors();
// // debug: code to list available connectors
// Iterator iter2 = connectors.iterator();
diff --git a/java/src/processing/mode/java/tweak/Handle.java b/java/src/processing/mode/java/tweak/Handle.java
index 049397ae8..72dfa5ed3 100644
--- a/java/src/processing/mode/java/tweak/Handle.java
+++ b/java/src/processing/mode/java/tweak/Handle.java
@@ -23,6 +23,7 @@ package processing.mode.java.tweak;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.Comparator;
import java.util.Locale;
@@ -171,7 +172,7 @@ public class Handle {
} else if ("float".equals(type)) {
BigDecimal bd = new BigDecimal(value.floatValue());
- bd = bd.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP);
+ bd = bd.setScale(decimalPlaces, RoundingMode.HALF_UP);
newValue = bd.floatValue();
strNewValue = String.format(Locale.US, textFormat, newValue.floatValue());
}
diff --git a/java/test/processing/mode/java/ParserTests.java b/java/test/processing/mode/java/ParserTests.java
index 29ba684a7..8fac2bcd6 100644
--- a/java/test/processing/mode/java/ParserTests.java
+++ b/java/test/processing/mode/java/ParserTests.java
@@ -88,8 +88,12 @@ public class ParserTests {
}
static void expectGood(final String id, boolean ignoreWhitespace) {
+ expectGood(id, ignoreWhitespace, Optional.empty());
+ }
+
+ static void expectGood(final String id, boolean ignoreWhitespace, Optional packageName) {
try {
- final String program = preprocess(id, res(id + ".pde"));
+ final String program = preprocess(id, res(id + ".pde"), packageName);
boolean successful = compile(id, program);
if (!successful) {
System.err.println("----------------------------");
@@ -369,6 +373,11 @@ public class ParserTests {
expectGood("typeinference");
}
+ @Test
+ public void testPackage() {
+ expectGood("packageTest", true, Optional.of("test.subtest"));
+ }
+
private static boolean compile(String id, String program) {
// Create compilable AST to get syntax problems
CompilationUnit compilableCU = JdtCompilerUtil.makeAST(
diff --git a/java/test/processing/mode/java/ProcessingTestUtil.java b/java/test/processing/mode/java/ProcessingTestUtil.java
index 6e113e9d2..97deb4147 100644
--- a/java/test/processing/mode/java/ProcessingTestUtil.java
+++ b/java/test/processing/mode/java/ProcessingTestUtil.java
@@ -5,6 +5,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
+import java.util.Optional;
+
import processing.app.Preferences;
import processing.app.SketchException;
import processing.mode.java.preproc.PdePreprocessor;
@@ -37,9 +39,25 @@ public class ProcessingTestUtil {
static String preprocess(final String name, final File resource)
throws SketchException {
+ return preprocess(name, resource, Optional.empty());
+ }
+ static String preprocess(final String name, final File resource, Optional optionalPackage)
+ throws SketchException {
+
final String program = read(resource);
final StringWriter out = new StringWriter();
- PreprocessorResult result = new PdePreprocessor(name, 4, true).write(out, program);
+
+ PdePreprocessor.PdePreprocessorBuilder builder = PdePreprocessor.builderFor(name);
+ builder.setTabSize(4);
+ builder.setIsTesting(true);
+
+ if (optionalPackage.isPresent()) {
+ builder.setDestinationPackage(optionalPackage.get());
+ }
+
+ PdePreprocessor preprocessor = builder.build();
+
+ PreprocessorResult result = preprocessor.write(out, program);
if (result.getPreprocessIssues().size() > 0) {
throw new PdePreprocessIssueException(result.getPreprocessIssues().get(0));
diff --git a/java/test/processing/mode/java/preproc/code/CodeEditOperationUtilTest.java b/java/test/processing/mode/java/preproc/code/CodeEditOperationUtilTest.java
deleted file mode 100644
index 1f7e85ce7..000000000
--- a/java/test/processing/mode/java/preproc/code/CodeEditOperationUtilTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package processing.mode.java.preproc.code;
-
-import org.antlr.v4.runtime.BufferedTokenStream;
-import org.antlr.v4.runtime.Token;
-import org.antlr.v4.runtime.TokenStream;
-import org.antlr.v4.runtime.TokenStreamRewriter;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import processing.mode.java.pdex.TextTransform;
-
-import static org.junit.Assert.*;
-
-
-public class CodeEditOperationUtilTest {
-
- private TokenStreamRewriter tokenStreamRewriter;
- private Token sampleStart;
- private Token sampleEnd;
-
- @Before
- public void setUp() {
- tokenStreamRewriter = Mockito.mock(TokenStreamRewriter.class);
-
- sampleStart = Mockito.mock(Token.class);
- Mockito.when(sampleStart.getStartIndex()).thenReturn(5);
- Mockito.when(sampleStart.getText()).thenReturn("test");
-
- sampleEnd = Mockito.mock(Token.class);
- Mockito.when(sampleEnd.getStartIndex()).thenReturn(10);
- Mockito.when(sampleEnd.getText()).thenReturn("testing");
- }
-
- @Test
- public void createDeleteSingle() {
- TextTransform.Edit edit = CodeEditOperationUtil.createDelete(sampleStart, tokenStreamRewriter);
- Assert.assertNotNull(edit);
- Mockito.verify(tokenStreamRewriter).delete(sampleStart);
- }
-
- @Test
- public void createDeleteRange() {
- TextTransform.Edit edit = CodeEditOperationUtil.createDelete(
- sampleStart,
- sampleEnd,
- tokenStreamRewriter
- );
-
- Assert.assertNotNull(edit);
- Mockito.verify(tokenStreamRewriter).delete(sampleStart, sampleEnd);
- }
-
- @Test
- public void createInsertAfterLocation() {
- TextTransform.Edit edit = CodeEditOperationUtil.createInsertAfter(
- 5,
- "text",
- tokenStreamRewriter
- );
-
- Assert.assertNotNull(edit);
- Mockito.verify(tokenStreamRewriter).insertAfter(5, "text");
- }
-
- @Test
- public void createInsertAfterToken() {
- TextTransform.Edit edit = CodeEditOperationUtil.createInsertAfter(
- sampleStart,
- "text",
- tokenStreamRewriter
- );
-
- Assert.assertNotNull(edit);
- Mockito.verify(tokenStreamRewriter).insertAfter(sampleStart, "text");
- }
-
- @Test
- public void createInsertBeforeToken() {
- TextTransform.Edit edit = CodeEditOperationUtil.createInsertBefore(
- sampleStart,
- "text",
- tokenStreamRewriter
- );
-
- Assert.assertNotNull(edit);
- Mockito.verify(tokenStreamRewriter).insertBefore(sampleStart, "text");
- }
-
- @Test
- public void createInsertBeforeLocation() {
- TextTransform.Edit edit = CodeEditOperationUtil.createInsertBefore(
- 5,
- 5,
- "text",
- tokenStreamRewriter
- );
-
- Assert.assertNotNull(edit);
- Mockito.verify(tokenStreamRewriter).insertBefore(5, "text");
- }
-
-}
\ No newline at end of file
diff --git a/java/test/processing/mode/java/preproc/code/PrintWriterWithEditGenTest.java b/java/test/processing/mode/java/preproc/code/PrintWriterWithEditGenTest.java
index 33e57b5a9..f7fb89eae 100644
--- a/java/test/processing/mode/java/preproc/code/PrintWriterWithEditGenTest.java
+++ b/java/test/processing/mode/java/preproc/code/PrintWriterWithEditGenTest.java
@@ -6,6 +6,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import processing.mode.java.pdex.TextTransform;
+import processing.mode.java.preproc.PdeParseTreeListener;
import java.util.List;
@@ -24,7 +25,7 @@ public class PrintWriterWithEditGenTest {
@Test
public void addEmptyLineBefore() {
- PrintWriterWithEditGen editGen = createGen(true);
+ PdeParseTreeListener.PrintWriterWithEditGen editGen = createGen(true);
editGen.addEmptyLine();
editGen.finish();
@@ -36,7 +37,7 @@ public class PrintWriterWithEditGenTest {
@Test
public void addCodeLineBefore() {
- PrintWriterWithEditGen editGen = createGen(true);
+ PdeParseTreeListener.PrintWriterWithEditGen editGen = createGen(true);
editGen.addCodeLine("test");
editGen.finish();
@@ -48,7 +49,7 @@ public class PrintWriterWithEditGenTest {
@Test
public void addCodeBefore() {
- PrintWriterWithEditGen editGen = createGen(true);
+ PdeParseTreeListener.PrintWriterWithEditGen editGen = createGen(true);
editGen.addCode("test");
editGen.finish();
@@ -60,7 +61,7 @@ public class PrintWriterWithEditGenTest {
@Test
public void addEmptyLineAfter() {
- PrintWriterWithEditGen editGen = createGen(false);
+ PdeParseTreeListener.PrintWriterWithEditGen editGen = createGen(false);
editGen.addEmptyLine();
editGen.finish();
@@ -72,7 +73,7 @@ public class PrintWriterWithEditGenTest {
@Test
public void addCodeLineAfter() {
- PrintWriterWithEditGen editGen = createGen(false);
+ PdeParseTreeListener.PrintWriterWithEditGen editGen = createGen(false);
editGen.addCodeLine("test");
editGen.finish();
@@ -84,7 +85,7 @@ public class PrintWriterWithEditGenTest {
@Test
public void addCodeAfter() {
- PrintWriterWithEditGen editGen = createGen(false);
+ PdeParseTreeListener.PrintWriterWithEditGen editGen = createGen(false);
editGen.addCode("test");
editGen.finish();
@@ -94,8 +95,8 @@ public class PrintWriterWithEditGenTest {
Mockito.verify(tokenStreamRewriter).insertAfter(5, "test");
}
- private PrintWriterWithEditGen createGen(boolean before) {
- return new PrintWriterWithEditGen(
+ private PdeParseTreeListener.PrintWriterWithEditGen createGen(boolean before) {
+ return new PdeParseTreeListener.PrintWriterWithEditGen(
tokenStreamRewriter,
rewriteResultBuilder,
5,
diff --git a/java/test/resources/packageTest.expected b/java/test/resources/packageTest.expected
new file mode 100644
index 000000000..7bf7cb07e
--- /dev/null
+++ b/java/test/resources/packageTest.expected
@@ -0,0 +1,33 @@
+package test.subtest;
+
+import processing.core.*;
+import processing.data.*;
+import processing.event.*;
+import processing.opengl.*;
+
+import java.util.*;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class packageTest extends PApplet {
+
+ public void setup() {
+List test = new ArrayList<>();
+ noLoop();
+ }
+
+ static public void main(String[] passedArgs) {
+ String[] appletArgs = new String[] { "packageTest" };
+ if (passedArgs != null) {
+ PApplet.main(concat(appletArgs, passedArgs));
+ } else {
+ PApplet.main(appletArgs);
+ }
+ }
+}
diff --git a/java/test/resources/packageTest.pde b/java/test/resources/packageTest.pde
new file mode 100644
index 000000000..8102a81bf
--- /dev/null
+++ b/java/test/resources/packageTest.pde
@@ -0,0 +1,3 @@
+import java.util.*;
+
+List test = new ArrayList<>();
diff --git a/todo.txt b/todo.txt
index 2611c1545..30c696c02 100755
--- a/todo.txt
+++ b/todo.txt
@@ -5,18 +5,31 @@ X fix potential highlighting issue that wasn't selecting portions of text
X update AppBundler to use newer SDK, recompile
X edit build.xml files and appbundler to preserve more attributes
+after the JDK 11 update
+X use a new pref for the sketchbook folder location for 4.x
+X finish porting ThinkDifferent to use Desktop APIs
+X http://openjdk.java.net/jeps/272
+X also roll it into MacPlatform, since less platform-specific code needed
+_ Implement reliable getLibraryFolder() and getDocumentsFolder() methods in MacPlatform
+_ https://github.com/processing/processing4/issues/9
+
major updates for JDK 11 et al
X https://github.com/processing/processing4/pull/1
_ go through the comment w/ the various bugs that can be closed
_ remove jdk.hash from build.xml
_ need to make sure the downloader isn't relying on it
_ clean up naming for build/macosx/jdk-0u1.tgz and build/macosx/jdk-11.0.1+13/
+_ fix "WARNING: Illegal reflective access by processing.app.ui.Toolkit to field sun.awt.CGraphicsDevice.scale" warning on startup
contribs
X tweak mode not working
X https://github.com/processing/processing/issues/5805
X https://github.com/processing/processing/pull/5909
+sampottinger
+X Fix JDK naming and cleanup in ant build
+X https://github.com/processing/processing4/pull/12
+
_ selecting a sketch in the Sketch menu no longer opens its window
_ https://github.com/processing/processing/issues/5882