diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java index 99f73d9f3..6e4244c15 100644 --- a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java +++ b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java @@ -21,12 +21,14 @@ package processing.mode.java.preproc; +import java.text.SimpleDateFormat; import java.util.*; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.tree.ParseTree; +import processing.app.Preferences; import processing.core.PApplet; import processing.mode.java.pdex.TextTransform; import processing.mode.java.preproc.PdePreprocessor.Mode; @@ -46,18 +48,16 @@ import processing.mode.java.preproc.issue.PreprocessIssueMessageSimplifier; */ public class PdeParseTreeListener extends ProcessingBaseListener { - private final static String VERSION_STR = "3.0.0"; + private static final String VERSION_STR = "3.0.0"; private static final String SIZE_METHOD_NAME = "size"; private static final String FULLSCREEN_METHOD_NAME = "fullScreen"; private final int tabSize; - private int headerOffset; - private String sketchName; private boolean isTesting; private TokenStreamRewriter rewriter; - protected Mode mode = Mode.JAVA; + private Mode mode = Mode.JAVA; private boolean foundMain; private int lineOffset; @@ -78,6 +78,10 @@ public class PdeParseTreeListener extends ProcessingBaseListener { private RewriteResult footerResult; private RewriterCodeGenerator codeGen; + private String indent1; + private String indent2; + private String indent3; + private Optional pdeParseTreeErrorListenerMaybe; /** @@ -94,31 +98,19 @@ public class PdeParseTreeListener extends ProcessingBaseListener { codeGen = new RewriterCodeGenerator(tabSize); pdeParseTreeErrorListenerMaybe = Optional.empty(); + + final char[] indentChars = new char[newTabSize]; + Arrays.fill(indentChars, ' '); + indent1 = new String(indentChars); + indent2 = indent1 + indent1; + indent3 = indent2 + indent1; } - /** - * Create a new listener with a customized code generator. - * - *

- * A significant amount of code is generated via a {RewriterCodeGenerator} and this constructor - * allows client code to specify different generators as needed by their modes. - *

- * - * @param tokens The tokens over which to rewrite. - * @param newSketchName The name of the sketch being traversed. - * @param newTabSize Size of tab / indent. - * @param newCodeGen The new code generator to use within this listener. + /* + * =============================================== + * === Public interface for client code usage. === + * =============================================== */ - public PdeParseTreeListener(TokenStream tokens, String newSketchName, int newTabSize, - RewriterCodeGenerator newCodeGen) { - - rewriter = new TokenStreamRewriter(tokens); - sketchName = newSketchName; - tabSize = newTabSize; - codeGen = newCodeGen; - - pdeParseTreeErrorListenerMaybe = Optional.empty(); - } /** * Indicate imports for code folders. @@ -253,7 +245,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. @@ -264,7 +260,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener { RewriteParams rewriteParams = createRewriteParams(); // header - headerResult = codeGen.prepareHeader(rewriter, rewriteParams); + headerResult = prepareHeader(rewriter, rewriteParams); lineOffset = headerResult.getLineOffset(); // footer @@ -272,7 +268,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener { int tokens = tokenStream.size(); int length = tokenStream.get(tokens-1).getStopIndex(); - footerResult = codeGen.prepareFooter(rewriter, rewriteParams, length); + footerResult = prepareFooter(rewriter, rewriteParams, length); } /** @@ -288,105 +284,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. * @@ -432,7 +329,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener { String importStringNoSemi = importString.substring(0, importString.length() - 1); foundImports.add(importStringNoSemi); - createDelete(ctx.start, ctx.stop); + delete(ctx.start, ctx.stop); } /** @@ -448,7 +345,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"); } } @@ -542,7 +439,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; } } @@ -550,9 +447,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 "); } } @@ -564,27 +461,6 @@ public class PdeParseTreeListener extends ProcessingBaseListener { } } - /** - * Check if this contains an annation. - * - * @param child The modifier context to check. - * @return True if annotation. False otherwise - */ - private boolean isAnnoation(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; - } - /** * Endpoint for ANTLR to call after parsing a primitive type name. * @@ -601,8 +477,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); } } @@ -618,8 +494,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); } } @@ -630,21 +506,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. @@ -652,8 +660,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)); } /** @@ -663,13 +671,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)); } /** @@ -678,8 +681,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)); } /** @@ -688,8 +691,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)); } /** @@ -697,8 +700,8 @@ 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)); } /** @@ -706,7 +709,7 @@ public class PdeParseTreeListener extends ProcessingBaseListener { * * @return Newly created rewrite params. */ - private RewriteParams createRewriteParams() { + protected RewriteParams createRewriteParams() { RewriteParamsBuilder builder = new RewriteParamsBuilder(VERSION_STR); builder.setSketchName(sketchName); @@ -729,6 +732,340 @@ public class PdeParseTreeListener extends ProcessingBaseListener { return builder.build(); } + /* + * ========================================= + * === 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. + * @param params The parameters for the rewrite. + * @return Information about the completed rewrite. + */ + protected RewriteResult prepareHeader(TokenStreamRewriter headerWriter, RewriteParams params) { + + RewriteResultBuilder resultBuilder = new RewriteResultBuilder(); + + PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen( + headerWriter, + resultBuilder, + 0, + true + ); + + writeHeaderContents(decoratedWriter, params, resultBuilder); + + decoratedWriter.finish(); + + return resultBuilder.build(); + } + + /** + * Prepare 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. + */ + protected RewriteResult prepareFooter(TokenStreamRewriter footerWriter, RewriteParams params, + int insertPoint) { + + RewriteResultBuilder resultBuilder = new RewriteResultBuilder(); + + PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen( + footerWriter, + resultBuilder, + insertPoint, + false + ); + + writeFooterContents(decoratedWriter, params, 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 params The parameters for the rewrite. + * @param resultBuilder Builder for reporting out results to the caller. + */ + protected void writeHeaderContents(PrintWriterWithEditGen decoratedWriter, RewriteParams params, + RewriteResultBuilder resultBuilder) { + + 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); + } + } + + /** + * Write the contents of the footer using a prebuilt print writer. + * + * @param decoratedWriter he 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. + */ + protected void writeFooterContents(PrintWriterWithEditGen decoratedWriter, RewriteParams params, + RewriteResultBuilder resultBuilder) { + + 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); + } + } + + /** + * 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. + */ + protected 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. + */ + protected 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. + */ + protected void writeImportList(PrintWriterWithEditGen headerWriter, List imports, + RewriteParams params, RewriteResultBuilder resultBuilder) { + + writeImportList(headerWriter, imports.toArray(new String[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. + */ + protected void writeImportList(PrintWriterWithEditGen headerWriter, String[] imports, + RewriteParams params, RewriteResultBuilder resultBuilder) { + + for (String importDecl : imports) { + headerWriter.addCodeLine("import " + importDecl + ";"); + } + 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. + */ + protected 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. + */ + protected 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. + */ + protected void writeStaticSketchFooter(PrintWriterWithEditGen footerWriter, RewriteParams params, + 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 params The parameters for the rewrite. + * @param resultBuilder Builder for reporting out results to the caller. + */ + protected 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. + */ + protected 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. + */ + protected void writeClassFooter(PrintWriterWithEditGen footerWriter, RewriteParams params, + RewriteResultBuilder resultBuilder) { + + footerWriter.addCodeLine("}"); + } + + /* + * ========================= + * === Supporting types. === + * ========================= + */ + /** * Listener for issues encountered while processing a valid pde parse tree. */ @@ -740,7 +1077,209 @@ public class PdeParseTreeListener extends ProcessingBaseListener { * @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()); + } + } diff --git a/java/src/processing/mode/java/preproc/PdePreprocessor.java b/java/src/processing/mode/java/preproc/PdePreprocessor.java index 0fb2ca8c4..9f35a2e55 100644 --- a/java/src/processing/mode/java/preproc/PdePreprocessor.java +++ b/java/src/processing/mode/java/preproc/PdePreprocessor.java @@ -34,7 +34,6 @@ 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; @@ -281,6 +280,52 @@ public class PdePreprocessor { private Optional> defaultImports; private Optional> coreImports; + /** + * 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(); @@ -368,11 +413,11 @@ public class PdePreprocessor { ); List effectiveDefaultImports = defaultImports.orElseGet( - () -> Arrays.asList(ImportUtil.getDefaultImports()) + () -> Arrays.asList(BASE_DEFAULT_IMPORTS) ); List effectiveCoreImports = coreImports.orElseGet( - () -> Arrays.asList(ImportUtil.getCoreImports()) + () -> Arrays.asList(BASE_CORE_IMPORTS) ); return new PdePreprocessor( 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/RewriterCodeGenerator.java b/java/src/processing/mode/java/preproc/code/RewriterCodeGenerator.java index e5407c3da..1e785c075 100644 --- a/java/src/processing/mode/java/preproc/code/RewriterCodeGenerator.java +++ b/java/src/processing/mode/java/preproc/code/RewriterCodeGenerator.java @@ -34,326 +34,4 @@ public class RewriterCodeGenerator { indent3 = indent2 + indent1; } - /** - * 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. - * @param params The parameters for the rewrite. - * @return Information about the completed rewrite. - */ - public RewriteResult prepareHeader(TokenStreamRewriter headerWriter, RewriteParams params) { - - RewriteResultBuilder resultBuilder = new RewriteResultBuilder(); - - PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen( - headerWriter, - resultBuilder, - 0, - true - ); - - writeHeaderContents(decoratedWriter, params, resultBuilder); - - decoratedWriter.finish(); - - return resultBuilder.build(); - } - - /** - * Prepare 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 prepareFooter(TokenStreamRewriter footerWriter, RewriteParams params, - int insertPoint) { - - RewriteResultBuilder resultBuilder = new RewriteResultBuilder(); - - PrintWriterWithEditGen decoratedWriter = new PrintWriterWithEditGen( - footerWriter, - resultBuilder, - insertPoint, - false - ); - - writeFooterContents(decoratedWriter, params, 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 params The parameters for the rewrite. - * @param resultBuilder Builder for reporting out results to the caller. - */ - protected void writeHeaderContents(PrintWriterWithEditGen decoratedWriter, RewriteParams params, - RewriteResultBuilder resultBuilder) { - - 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); - } - } - - /** - * Write the contents of the footer using a prebuilt print writer. - * - * @param decoratedWriter he 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. - */ - protected void writeFooterContents(PrintWriterWithEditGen decoratedWriter, RewriteParams params, - RewriteResultBuilder resultBuilder) { - - 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); - } - } - - /** - * 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. - */ - protected 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. - */ - protected 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. - */ - protected void writeImportList(PrintWriterWithEditGen headerWriter, List imports, RewriteParams params, - RewriteResultBuilder resultBuilder) { - - writeImportList(headerWriter, imports.toArray(new String[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. - */ - protected void writeImportList(PrintWriterWithEditGen headerWriter, String[] imports, - RewriteParams params, RewriteResultBuilder resultBuilder) { - - for (String importDecl : imports) { - headerWriter.addCodeLine("import " + importDecl + ";"); - } - 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. - */ - protected 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. - */ - protected 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. - */ - protected void writeStaticSketchFooter(PrintWriterWithEditGen footerWriter, RewriteParams params, - 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 params The parameters for the rewrite. - * @param resultBuilder Builder for reporting out results to the caller. - */ - protected 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. - */ - protected 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. - */ - protected void writeClassFooter(PrintWriterWithEditGen footerWriter, RewriteParams params, - RewriteResultBuilder resultBuilder) { - - footerWriter.addCodeLine("}"); - } - } 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,