diff --git a/README.md b/README.md index 58de2892e..259aa164c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,15 @@ -This is an experimental fork to attempt the move to JDK 11. Because that's a major, API-breaking change, it would be Processing 4. +This is an experimental fork to attempt the move to JDK 11. Because that's a major, API-breaking change, it would be Processing 4. I'm working with Sam Pottinger to incorporate [his changes](https://github.com/sampottinger/processing) to see if that can be the basis for this new release. Getting things moved to OpenJDK 11 will help the longevity of the project. -**It's not clear if we'll ship an actual Processing 4.0**, since I have less free time than ever, and very little development help. If you'd like to help, contribute bug fixes. +**It's not clear if we'll ship an actual Processing 4.0**, since I have less free time than ever, and very little development help. If you'd like to help, contribute bug fixes. -Ben Fry, 4 October 2019 \ No newline at end of file +Ben Fry, 4 October 2019 + +--- + +## API changes + +As with all releases, I'll do everything possible to avoid breaking API. However, there will still be tweaks that have to be made. We'll try to keep them minor. + +* `Base.defaultFileMenu` is now `protected` instead of `static public` \ No newline at end of file diff --git a/app/.classpath b/app/.classpath index 96cab9ff6..4918c7ea7 100644 --- a/app/.classpath +++ b/app/.classpath @@ -11,7 +11,6 @@ - diff --git a/app/build.xml b/app/build.xml index bef3f06b7..2fdf8afe0 100644 --- a/app/build.xml +++ b/app/build.xml @@ -28,8 +28,8 @@ - ()); protected Editor activeEditor; /** A lone file menu to be used when all sketch windows are closed. */ - static public JMenu defaultFileMenu; + protected JMenu defaultFileMenu; /** * Starts with the last mode used with the environment, @@ -437,6 +437,49 @@ public class Base { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + /** + * Limited file menu to be used on OS X when no sketches are open. + */ + public JMenu initDefaultFileMenu() { + defaultFileMenu = new JMenu(Language.text("menu.file")); + + JMenuItem item = Toolkit.newJMenuItem(Language.text("menu.file.new"), 'N'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleNew(); + } + }); + defaultFileMenu.add(item); + + item = Toolkit.newJMenuItem(Language.text("menu.file.open"), 'O'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleOpenPrompt(); + } + }); + defaultFileMenu.add(item); + + item = Toolkit.newJMenuItemShift(Language.text("menu.file.sketchbook"), 'K'); + item.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + getNextMode().showSketchbookFrame(); + } + }); + defaultFileMenu.add(item); + + item = Toolkit.newJMenuItemShift(Language.text("menu.file.examples"), 'O'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + thinkDifferentExamples(); + } + }); + defaultFileMenu.add(item); + + return defaultFileMenu; + } + + void buildCoreModes() { Mode javaMode = ModeContribution.load(this, Platform.getContentFile("modes/java"), diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index 1b05083e1..c9349e855 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -404,17 +404,17 @@ public class Preferences { /** - * Check for a 3.0 sketchbook location, and if none exists, - * try to grab it from the 2.0 sketchbook location. + * Check for a 4.0 sketchbook location, and if none exists, + * try to grab it from the 3.0 sketchbook location. * @return true if a location was found and the pref didn't exist */ static protected boolean checkSketchbookPref() { - // If a 3.0 sketchbook location has never been inited + // If a 4.0 sketchbook location has never been inited if (getSketchbookPath() == null) { - String twoPath = get("sketchbook.path"); - // If they've run the 2.0 version, start with that location - if (twoPath != null) { - setSketchbookPath(twoPath); + String threePath = get("sketchbook.path.three"); //$NON-NLS-1$ + // If they've run the 3.0 version, start with that location + if (threePath != null) { + setSketchbookPath(threePath); return true; // save the sketchbook right away } // Otherwise it'll be null, and reset properly by Base @@ -424,16 +424,16 @@ public class Preferences { static public String getOldSketchbookPath() { - return get("sketchbook.path"); - } - - - static public String getSketchbookPath() { return get("sketchbook.path.three"); //$NON-NLS-1$ } + static public String getSketchbookPath() { + return get("sketchbook.path.four"); //$NON-NLS-1$ + } + + static protected void setSketchbookPath(String path) { - set("sketchbook.path.three", path); //$NON-NLS-1$ + set("sketchbook.path.four", path); //$NON-NLS-1$ } } diff --git a/app/src/processing/app/platform/DefaultPlatform.java b/app/src/processing/app/platform/DefaultPlatform.java index 58529b516..9cee2cbc4 100644 --- a/app/src/processing/app/platform/DefaultPlatform.java +++ b/app/src/processing/app/platform/DefaultPlatform.java @@ -129,7 +129,7 @@ public class DefaultPlatform { public interface CLibrary extends Library { - CLibrary INSTANCE = (CLibrary)Native.loadLibrary("c", CLibrary.class); + CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class); int setenv(String name, String value, int overwrite); String getenv(String name); int unsetenv(String name); diff --git a/app/src/processing/app/platform/LinuxPlatform.java b/app/src/processing/app/platform/LinuxPlatform.java index 56c7859de..af6d52506 100644 --- a/app/src/processing/app/platform/LinuxPlatform.java +++ b/app/src/processing/app/platform/LinuxPlatform.java @@ -27,7 +27,6 @@ import java.awt.Desktop; import java.awt.Toolkit; import processing.app.Base; -import processing.app.Messages; import processing.app.Preferences; import processing.app.platform.DefaultPlatform; import processing.core.PApplet; diff --git a/app/src/processing/app/platform/MacPlatform.java b/app/src/processing/app/platform/MacPlatform.java index b3fd404d9..64852fb91 100644 --- a/app/src/processing/app/platform/MacPlatform.java +++ b/app/src/processing/app/platform/MacPlatform.java @@ -28,29 +28,20 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; -import com.apple.eio.FileManager; +import javax.swing.JMenu; +import javax.swing.JMenuBar; import processing.app.Base; import processing.app.Messages; import processing.app.platform.DefaultPlatform; +import processing.app.ui.About; /** - * Platform handler for Mac OS X. + * Platform handler for macOS. */ public class MacPlatform extends DefaultPlatform { - // Removing for 2.0b8 because Quaqua doesn't have OS X 10.8 version. - /* - public void setLookAndFeel() throws Exception { - // Use the Quaqua L & F on OS X to make JFileChooser less awful - UIManager.setLookAndFeel("ch.randelshofer.quaqua.QuaquaLookAndFeel"); - // undo quaqua trying to fix the margins, since we've already - // hacked that in, bit by bit, over the years - UIManager.put("Component.visualMargin", new Insets(1, 1, 1, 1)); - } - */ - public void saveLanguage(String language) { String[] cmdarray = new String[]{ "defaults", "write", @@ -67,35 +58,42 @@ public class MacPlatform extends DefaultPlatform { public void initBase(Base base) { super.initBase(base); + + final Desktop desktop = Desktop.getDesktop(); + System.setProperty("apple.laf.useScreenMenuBar", "true"); - ThinkDifferent.init(base); - /* - try { - String name = "processing.app.macosx.ThinkDifferent"; - Class osxAdapter = ClassLoader.getSystemClassLoader().loadClass(name); - Class[] defArgs = { Base.class }; - Method registerMethod = osxAdapter.getDeclaredMethod("register", defArgs); - if (registerMethod != null) { - Object[] args = { this }; - registerMethod.invoke(osxAdapter, args); + // Set the menu bar to be used when nothing else is open. + JMenuBar defaultMenuBar = new JMenuBar(); + JMenu fileMenu = base.initDefaultFileMenu(); + defaultMenuBar.add(fileMenu); + desktop.setDefaultMenuBar(defaultMenuBar); + + desktop.setAboutHandler((event) -> { + new About(null); + }); + + desktop.setPreferencesHandler((event) -> { + base.handlePrefs(); + }); + + desktop.setOpenFileHandler((event) -> { + for (File file : event.getFiles()) { + base.handleOpen(file.getAbsolutePath()); } - } catch (NoClassDefFoundError e) { - // This will be thrown first if the OSXAdapter is loaded on a system without the EAWT - // because OSXAdapter extends ApplicationAdapter in its def - System.err.println("This version of Mac OS X does not support the Apple EAWT." + - "Application Menu handling has been disabled (" + e + ")"); + }); - } catch (ClassNotFoundException e) { - // This shouldn't be reached; if there's a problem with the OSXAdapter - // we should get the above NoClassDefFoundError first. - System.err.println("This version of Mac OS X does not support the Apple EAWT. " + - "Application Menu handling has been disabled (" + e + ")"); - } catch (Exception e) { - System.err.println("Exception while loading BaseOSX:"); - e.printStackTrace(); - } - */ + desktop.setPrintFileHandler((event) -> { + // TODO not yet implemented + }); + + desktop.setQuitHandler((event, quitResponse) -> { + if (base.handleQuit()) { + quitResponse.performQuit(); + } else { + quitResponse.cancelQuit(); + } + }); } @@ -106,30 +104,9 @@ public class MacPlatform extends DefaultPlatform { public File getDefaultSketchbookFolder() throws Exception { return new File(getDocumentsFolder(), "Processing"); - /* - // looking for /Users/blah/Documents/Processing - try { - Class clazz = Class.forName("processing.app.BaseMacOS"); - Method m = clazz.getMethod("getDocumentsFolder", new Class[] { }); - String documentsPath = (String) m.invoke(null, new Object[] { }); - sketchbookFolder = new File(documentsPath, "Processing"); - - } catch (Exception e) { - sketchbookFolder = promptSketchbookLocation(); - } - */ } -// /** -// * Moves the specified File object (which might be a file or folder) -// * to the trash. -// */ -// public boolean deleteFile(File file) throws IOException { -// return FileManager.moveToTrash(file); -// } - - public void openURL(String url) throws Exception { try { Desktop.getDesktop().browse(new URI(url)); @@ -145,64 +122,23 @@ public class MacPlatform extends DefaultPlatform { } - /* - public void openURL(String url) throws Exception { - if (PApplet.javaVersion < 1.6f) { - if (url.startsWith("http://")) { - // formerly com.apple.eio.FileManager.openURL(url); - // but due to deprecation, instead loading dynamically - try { - Class eieio = Class.forName("com.apple.eio.FileManager"); - Method openMethod = - eieio.getMethod("openURL", new Class[] { String.class }); - openMethod.invoke(null, new Object[] { url }); - } catch (Exception e) { - e.printStackTrace(); - } - } else { - // Assume this is a file instead, and just open it. - // Extension of http://dev.processing.org/bugs/show_bug.cgi?id=1010 - processing.core.PApplet.open(url); - } - } else { - try { - Class desktopClass = Class.forName("java.awt.Desktop"); - Method getMethod = desktopClass.getMethod("getDesktop"); - Object desktop = getMethod.invoke(null, new Object[] { }); - - // for Java 1.6, replacing with java.awt.Desktop.browse() - // and java.awt.Desktop.open() - if (url.startsWith("http://")) { // browse to a location - Method browseMethod = - desktopClass.getMethod("browse", new Class[] { URI.class }); - browseMethod.invoke(desktop, new Object[] { new URI(url) }); - } else { // open a file - Method openMethod = - desktopClass.getMethod("open", new Class[] { File.class }); - openMethod.invoke(desktop, new Object[] { new File(url) }); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - public boolean openFolderAvailable() { - return true; - } - - - public void openFolder(File file) throws Exception { - //openURL(file.getAbsolutePath()); // handles char replacement, etc - processing.core.PApplet.open(file.getAbsolutePath()); - } - */ - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + // TODO I suspect this won't work much longer, since access to the user's + // home directory seems verboten on more recent macOS versions [fry 191008] + protected String getLibraryFolder() throws FileNotFoundException { + return System.getProperty("user.home") + "/Library"; + } + + + // see notes on getLibraryFolder() + protected String getDocumentsFolder() throws FileNotFoundException { + return System.getProperty("user.home") + "/Documents"; + } + + + /* // Some of these are supposedly constants in com.apple.eio.FileManager, // however they don't seem to link properly from Eclipse. @@ -237,4 +173,5 @@ public class MacPlatform extends DefaultPlatform { protected String getDocumentsFolder() throws FileNotFoundException { return FileManager.findFolder(kUserDomain, kDocumentsFolderType); } + */ } diff --git a/app/src/processing/app/platform/ThinkDifferent.java b/app/src/processing/app/platform/ThinkDifferent.java deleted file mode 100644 index 01c9823bc..000000000 --- a/app/src/processing/app/platform/ThinkDifferent.java +++ /dev/null @@ -1,222 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2012-2014 The Processing Foundation - Copyright (c) 2007-2012 Ben Fry and Casey Reas - - 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.app.platform; - -import java.awt.event.*; -import java.io.File; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.List; - -import javax.swing.*; - -import com.apple.eawt.Application; - -import processing.app.*; -import processing.app.ui.About; -import processing.app.ui.Toolkit; - - -/** - * Deal with issues related to thinking differently. This handles the basic - * Mac OS X menu commands (and apple events) for open, about, prefs, etc. - * - * As of 0140, this code need not be built on platforms other than OS X, - * because of the new platform structure which isolates through reflection. - * - * Rewritten for 0232 to remove deprecation issues, per the message - * here. - * (We're able to do this now because we're dropping older Java versions.) - */ -public class ThinkDifferent { - - // pseudo-singleton model; no point in making multiple instances - // of the EAWT application or our adapter - private static ThinkDifferent adapter; - // http://developer.apple.com/documentation/Java/Reference/1.4.2/appledoc/api/com/apple/eawt/Application.html - private static Application application; - - // reference to the app where the existing quit, about, prefs code is - //private Base base; - - - static protected void init(final Base base) { - if (application == null) { - application = Application.getApplication(); - } - if (adapter == null) { - adapter = new ThinkDifferent(); //base); - } - - setHandler(application, "setAboutHandler", (proxy, method, args) -> { - new About(null); - return null; - }); - - setHandler(application, "setPreferencesHandler", (proxy, method, args) -> { - base.handlePrefs(); - return null; - }); - - setHandler(application, "setOpenFileHandler", (proxy, method, args) -> { - Method m = args[0].getClass().getMethod("getFiles"); - for (File file : (List) m.invoke(args[0])) { - base.handleOpen(file.getAbsolutePath()); - } - return null; - }); - - setHandler(application, "setPrintFileHandler", (proxy, method, args) -> { - // TODO not yet implemented - return null; - }); - - setHandler(application, "setQuitHandler", (proxy, method, args) -> { - if (base.handleQuit()) { - args[1].getClass().getMethod("performQuit").invoke(args[1]); - } else { - args[1].getClass().getMethod("cancelQuit").invoke(args[1]); - } - return null; - }); - - // Set the menubar to be used when nothing else is open. - JMenuBar defaultMenuBar = new JMenuBar(); - JMenu fileMenu = buildFileMenu(base); - defaultMenuBar.add(fileMenu); - // This is kind of a gross way to do this, but the alternatives? Hrm. - Base.defaultFileMenu = fileMenu; - -// if (PApplet.javaVersion <= 1.6f) { // doesn't work on Oracle's Java - try { - application.setDefaultMenuBar(defaultMenuBar); - - } catch (Exception e) { - e.printStackTrace(); // oh well, never mind - } -// } else { -// // The douchebags at Oracle didn't feel that a working f*king menubar -// // on OS X was important enough to make it into the 7u40 release. -// //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8007267 -// // It languished in the JDK 8 source and has been backported for 7u60: -// //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8022667 -// -// JFrame offscreen = new JFrame(); -// offscreen.setUndecorated(true); -// offscreen.setJMenuBar(defaultMenuBar); -// Dimension screen = Toolkit.getScreenSize(); -// offscreen.setLocation(screen.width, screen.height); -// offscreen.setVisible(true); -// } - } - - -// public ThinkDifferent(Base base) { -// this.base = base; -// } - - /** - * Sets a handler on an instance of {@link Application}, taking into account JVM version - * differences. - * - * @param app an instance of {@link Application} - * @param name the "set handler" method name - * @param handler the handler - */ - private static void setHandler(Application app, String name, InvocationHandler handler) { - // Determine which version of com.apple.eawt.Application to use and pass it a handler of the - // appropriate type - Method[] methods = app.getClass().getMethods(); - for (Method m : methods) { - if (!name.equals(m.getName())) { - continue; - } - if (m.getParameterCount() != 1) { - continue; - } - Class paramType = m.getParameterTypes()[0]; - try { - // Allow a null handler - Object proxy = null; - if (handler != null) { - proxy = Proxy.newProxyInstance( - paramType.getClassLoader(), new Class[] { paramType }, handler); - } - m.invoke(app, proxy); - } catch (IllegalArgumentException ex) { - // TODO: Print error?: method doesn't take an interface, etc. - } catch (IllegalAccessException ex) { - // TODO: Print error?: Other method invocation problem - } catch (InvocationTargetException ex) { - ex.getCause().printStackTrace(); - // TODO: Print ex.getCause() a different way? - } - break; - } - } - - /** - * Gimpy file menu to be used on OS X when no sketches are open. - */ - static protected JMenu buildFileMenu(final Base base) { - JMenuItem item; - JMenu fileMenu = new JMenu(Language.text("menu.file")); - - item = Toolkit.newJMenuItem(Language.text("menu.file.new"), 'N'); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - base.handleNew(); - } - }); - fileMenu.add(item); - - item = Toolkit.newJMenuItem(Language.text("menu.file.open"), 'O'); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - base.handleOpenPrompt(); - } - }); - fileMenu.add(item); - - item = Toolkit.newJMenuItemShift(Language.text("menu.file.sketchbook"), 'K'); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - base.getNextMode().showSketchbookFrame(); - } - }); - fileMenu.add(item); - - item = Toolkit.newJMenuItemShift(Language.text("menu.file.examples"), 'O'); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - base.thinkDifferentExamples(); - } - }); - fileMenu.add(item); - - return fileMenu; - } -} diff --git a/app/src/processing/app/platform/WindowsPlatform.java b/app/src/processing/app/platform/WindowsPlatform.java index eb4863555..58e01747c 100644 --- a/app/src/processing/app/platform/WindowsPlatform.java +++ b/app/src/processing/app/platform/WindowsPlatform.java @@ -560,7 +560,7 @@ public class WindowsPlatform extends DefaultPlatform { static WinLibC getLibC() { if (clib == null) { try { - clib = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class); + clib = Native.loadLibrary("msvcrt", WinLibC.class); } catch (UnsatisfiedLinkError ule) { Messages.showTrace("JNA Error", "JNA could not be loaded. Please report here:\n" + diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 811b25452..e71e1efeb 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -264,9 +264,8 @@ public abstract class Editor extends JFrame implements RunnerListener { textarea.setHorizontalOffset(JEditTextArea.leftHandGutter); { // Hack: add Numpad Slash as alternative shortcut for Comment/Uncomment - int modifiers = Toolkit.awtToolkit.getMenuShortcutKeyMask(); KeyStroke keyStroke = - KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, modifiers); + KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, Toolkit.SHORTCUT_KEY_MASK); final String ACTION_KEY = "COMMENT_UNCOMMENT_ALT"; textarea.getInputMap().put(keyStroke, ACTION_KEY); textarea.getActionMap().put(ACTION_KEY, new AbstractAction() { diff --git a/app/src/processing/app/ui/EditorButton.java b/app/src/processing/app/ui/EditorButton.java index f65f08139..538a3ddc0 100644 --- a/app/src/processing/app/ui/EditorButton.java +++ b/app/src/processing/app/ui/EditorButton.java @@ -43,7 +43,6 @@ implements MouseListener, MouseMotionListener, ActionListener { protected boolean pressed; protected boolean selected; protected boolean rollover; -// protected JLabel rolloverLabel; protected boolean shift; protected Image enabledImage; @@ -55,7 +54,6 @@ implements MouseListener, MouseMotionListener, ActionListener { protected Image gradient; protected EditorToolbar toolbar; -// protected Mode mode; public EditorButton(EditorToolbar parent, String name, String title) { @@ -128,50 +126,8 @@ implements MouseListener, MouseMotionListener, ActionListener { } -// public String toString() { -// switch (this) { -// case DISABLED: return "disabled"; -// case ENABLED: return "enabled"; -// case SELECTED: return "selected"; -// case ROLLOVER: return "rollover"; -// case PRESSED: return "pressed"; -// -//// for (State bs : State.values()) { -//// Image image = mode.loadImage(bs.getFilename(name)); -//// if (image != null) { -//// imageMap.put(bs, image); -//// } -//// } -//// -//// enabled = true; -//// //updateState(); -//// setState(State.ENABLED); -// } - - -// public void setReverse() { -// gradient = mode.makeGradient("reversed", DIM, DIM); -// } - - -// public void setGradient(Image gradient) { -// this.gradient = gradient; -// } - - -// public void setRolloverLabel(JLabel label) { -// rolloverLabel = label; -// } - - @Override - public void mouseClicked(MouseEvent e) { -// if (isEnabled()) { -// shift = e.isShiftDown(); -// actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, -// null, e.getModifiers())); -// } - } + public void mouseClicked(MouseEvent e) { } public boolean isShiftDown() { @@ -181,12 +137,17 @@ implements MouseListener, MouseMotionListener, ActionListener { @Override public void mousePressed(MouseEvent e) { - setPressed(true); - - // Need to fire here (or on mouse up) because mouseClicked() + // Using mousePressed() (or mouseReleased()) because mouseClicked() // won't be fired if the user nudges the mouse while clicking. // https://github.com/processing/processing/issues/3529 + setPressed(true); + shift = e.isShiftDown(); + + // It looks like ActionEvent expects old-style modifiers, + // so the e.getModifiers() call here may be correct. + // TODO Look into how this is getting used in Modes, + // and either update or add ignore to the deprecation [fry 191008] actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null, e.getModifiers())); } @@ -211,25 +172,6 @@ implements MouseListener, MouseMotionListener, ActionListener { } - /* - @Override - public void keyTyped(KeyEvent e) { } - - - @Override - public void keyReleased(KeyEvent e) { - updateRollover(e); - } - - - @Override - public void keyPressed(KeyEvent e) { - System.out.println(e); - updateRollover(e); - } - */ - - public String getRolloverText(InputEvent e) { if (e.isShiftDown()) { return titleShift; @@ -240,42 +182,15 @@ implements MouseListener, MouseMotionListener, ActionListener { } - /* - public void updateRollover(InputEvent e) { - if (rolloverLabel != null) { - if (e.isShiftDown()) { - rolloverLabel.setText(titleShift); - } else if (e.isAltDown()) { - rolloverLabel.setText(titleAlt); - } else { - rolloverLabel.setText(title); - } - } - } - */ - - @Override public void mouseEntered(MouseEvent e) { toolbar.setRollover(this, e); - /* - rollover = true; - updateRollover(e); - repaint(); - */ } @Override public void mouseExited(MouseEvent e) { toolbar.setRollover(null, e); - /* - rollover = false; - if (rolloverLabel != null) { - rolloverLabel.setText(""); - } - repaint(); - */ } @@ -289,11 +204,6 @@ implements MouseListener, MouseMotionListener, ActionListener { abstract public void actionPerformed(ActionEvent e); -// @Override -// public void actionPerformed(ActionEvent e) { -// // To be overridden by all subclasses -// } - @Override public Dimension getPreferredSize() { @@ -311,58 +221,4 @@ implements MouseListener, MouseMotionListener, ActionListener { public Dimension getMaximumSize() { return getPreferredSize(); } - - -// public Image getImage() { -// return imageMap.get(state); -// } -// -// -//// protected void updateState() { -//// state = ButtonState.ENABLED; -//// } -// -// -// public void setEnabled(boolean enabled) { -// this.enabled = enabled; -// if (enabled) { -// if (state == State.DISABLED) { -// setState(State.ENABLED); -// } -// } else { -// if (state == State.ENABLED) { -// setState(State.DISABLED); -// } -// } -// } -// -// -// public void setState(State state) { -// this.state = state; -// } - - -// public enum State { -// DISABLED, ENABLED, SELECTED, ROLLOVER, PRESSED; -// -// /** -// * @param name the root name -// * @return -// */ -// public String getFilename(String name) { -// final int res = Toolkit.highResDisplay() ? 2 : 1; -// return name + "-" + toString() + "-" + res + "x.png"; -// } -// -// public String toString() { -// switch (this) { -// case DISABLED: return "disabled"; -// case ENABLED: return "enabled"; -// case SELECTED: return "selected"; -// case ROLLOVER: return "rollover"; -// case PRESSED: return "pressed"; -// } -// return null; -// } -// } } \ No newline at end of file diff --git a/app/src/processing/app/ui/Toolkit.java b/app/src/processing/app/ui/Toolkit.java index 866190c6e..fc740353b 100644 --- a/app/src/processing/app/ui/Toolkit.java +++ b/app/src/processing/app/ui/Toolkit.java @@ -91,7 +91,7 @@ public class Toolkit { /** Command on Mac OS X, Ctrl on Windows and Linux */ static final int SHORTCUT_KEY_MASK = - awtToolkit.getMenuShortcutKeyMask(); + awtToolkit.getMenuShortcutKeyMaskEx(); /** Command-Option on Mac OS X, Ctrl-Alt on Windows and Linux */ static final int SHORTCUT_ALT_KEY_MASK = ActionEvent.ALT_MASK | SHORTCUT_KEY_MASK; @@ -178,8 +178,7 @@ public class Toolkit { */ static public JMenuItem newJMenuItem(String title, int what) { JMenuItem menuItem = new JMenuItem(title); - int modifiers = awtToolkit.getMenuShortcutKeyMask(); - menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(what, SHORTCUT_KEY_MASK)); return menuItem; } @@ -190,8 +189,7 @@ public class Toolkit { */ static public JMenuItem newJMenuItem(Action action, int what) { JMenuItem menuItem = new JMenuItem(action); - int modifiers = awtToolkit.getMenuShortcutKeyMask(); - menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(what, SHORTCUT_KEY_MASK)); return menuItem; } @@ -201,9 +199,7 @@ public class Toolkit { */ static public JMenuItem newJMenuItemShift(String title, int what) { JMenuItem menuItem = new JMenuItem(title); - int modifiers = awtToolkit.getMenuShortcutKeyMask(); - modifiers |= ActionEvent.SHIFT_MASK; - menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(what, SHORTCUT_SHIFT_KEY_MASK)); return menuItem; } @@ -213,9 +209,7 @@ public class Toolkit { */ static public JMenuItem newJMenuItemShift(Action action, int what) { JMenuItem menuItem = new JMenuItem(action); - int modifiers = awtToolkit.getMenuShortcutKeyMask(); - modifiers |= ActionEvent.SHIFT_MASK; - menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(what, SHORTCUT_SHIFT_KEY_MASK)); return menuItem; } @@ -235,8 +229,7 @@ public class Toolkit { static public JCheckBoxMenuItem newJCheckBoxMenuItem(String title, int what) { JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(title); - int modifiers = awtToolkit.getMenuShortcutKeyMask(); - menuItem.setAccelerator(KeyStroke.getKeyStroke(what, modifiers)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(what, SHORTCUT_KEY_MASK)); return menuItem; } @@ -326,7 +319,7 @@ public class Toolkit { if (fmTmp == null) return; // All null menuitems; would fail. final FontMetrics fm = fmTmp; // Hack for accessing variable in comparator. - final Comparator charComparator = new Comparator() { + final Comparator charComparator = new Comparator<>() { char[] baddies = "qypgjaeiouQAEIOU".toCharArray(); public int compare(Character ch1, Character ch2) { // Discriminates against descenders for readability, per MS @@ -699,8 +692,7 @@ public class Toolkit { root.registerKeyboardAction(disposer, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); - int modifiers = awtToolkit.getMenuShortcutKeyMask(); - stroke = KeyStroke.getKeyStroke('W', modifiers); + stroke = KeyStroke.getKeyStroke('W', SHORTCUT_KEY_MASK); root.registerKeyboardAction(disposer, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); } diff --git a/build/.gitignore b/build/.gitignore index bdfa9692c..7c4bf3a86 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,7 +1,12 @@ work javadoc -macosx/javafx-sdk-11.0.2 -macosx/jdk-0u4.tgz -macosx/jdk-11.0.4+11 -macosx/jfx-11.0.2.zip +macosx/javafx-sdk-* +macosx/jdk-11.* +macosx/jfx-11.* +windows/javafx-sdk-* +windows/jdk-11.* +windows/jfx-11.* +linux/javafx-sdk-* +linux/jdk-11.* +linux/jfx-11.* diff --git a/build/build.xml b/build/build.xml index 2500ded8a..bdc07d600 100644 --- a/build/build.xml +++ b/build/build.xml @@ -105,8 +105,6 @@ --> - - @@ -263,7 +261,7 @@ - + @@ -273,13 +271,13 @@ - + - + @@ -918,6 +916,17 @@ + + + + + + + + + + + - - - + + + + + + + + + + + + diff --git a/build/jre/build.xml b/build/jre/build.xml index 69c8109e8..414cbb1f8 100644 --- a/build/jre/build.xml +++ b/build/jre/build.xml @@ -22,8 +22,8 @@ - - diff --git a/core/.gitignore b/core/.gitignore index 84e7b5c83..bc2b2d6cf 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,2 +1,3 @@ bin +bin-test .idea diff --git a/core/apple.jar b/core/apple.jar deleted file mode 100644 index 6659a81c6..000000000 Binary files a/core/apple.jar and /dev/null differ diff --git a/core/build.xml b/core/build.xml index 3951c7ed5..6cd161376 100644 --- a/core/build.xml +++ b/core/build.xml @@ -18,8 +18,6 @@ - - @@ -61,10 +59,9 @@ - - - - + diff --git a/core/src/processing/awt/PSurfaceAWT.java b/core/src/processing/awt/PSurfaceAWT.java index 71a59e87f..8d897f44d 100644 --- a/core/src/processing/awt/PSurfaceAWT.java +++ b/core/src/processing/awt/PSurfaceAWT.java @@ -425,7 +425,7 @@ public class PSurfaceAWT extends PSurfaceNone { sketch.displayWidth = screenRect.width; sketch.displayHeight = screenRect.height; - windowScaleFactor = PApplet.platform == PConstants.MACOSX ? + windowScaleFactor = PApplet.platform == PConstants.MACOS ? 1 : sketch.pixelDensity; sketchWidth = sketch.sketchWidth() * windowScaleFactor; @@ -565,7 +565,7 @@ public class PSurfaceAWT extends PSurfaceNone { // Workaround for apparent Java bug on OS X? // https://github.com/processing/processing/issues/3472 if (cursorVisible && - (PApplet.platform == PConstants.MACOSX) && + (PApplet.platform == PConstants.MACOS) && (cursorType != PConstants.ARROW)) { hideCursor(); showCursor(); @@ -588,7 +588,7 @@ public class PSurfaceAWT extends PSurfaceNone { public void setIcon(PImage image) { Image awtImage = (Image) image.getNative(); - if (PApplet.platform != PConstants.MACOSX) { + if (PApplet.platform != PConstants.MACOS) { frame.setIconImage(awtImage); } else { @@ -623,12 +623,12 @@ public class PSurfaceAWT extends PSurfaceNone { protected void setProcessingIcon(Frame frame) { // On OS X, this only affects what shows up in the dock when minimized. // So replacing it is actually a step backwards. Brilliant. - if (PApplet.platform != PConstants.MACOSX) { + if (PApplet.platform != PConstants.MACOS) { //Image image = Toolkit.getDefaultToolkit().createImage(ICON_IMAGE); //frame.setIconImage(image); try { if (iconImages == null) { - iconImages = new ArrayList(); + iconImages = new ArrayList<>(); final int[] sizes = { 16, 32, 48, 64, 128, 256, 512 }; for (int sz : sizes) { @@ -1475,7 +1475,7 @@ public class PSurfaceAWT extends PSurfaceNone { public void setCursor(int kind) { // Swap the HAND cursor because MOVE doesn't seem to be available on OS X // https://github.com/processing/processing/issues/2358 - if (PApplet.platform == PConstants.MACOSX && kind == PConstants.MOVE) { + if (PApplet.platform == PConstants.MACOS && kind == PConstants.MOVE) { kind = PConstants.HAND; } canvas.setCursor(Cursor.getPredefinedCursor(kind)); diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 51e1bc928..e4208ca62 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -155,7 +155,7 @@ public class PApplet implements PConstants { String osname = System.getProperty("os.name"); if (osname.indexOf("Mac") != -1) { - platform = MACOSX; + platform = MACOS; } else if (osname.indexOf("Windows") != -1) { platform = WINDOWS; @@ -981,7 +981,7 @@ public class PApplet implements PConstants { // be called, conjuring up the demons of various rendering configurations. settings(); - if (display == SPAN && platform == MACOSX) { + if (display == SPAN && platform == MACOS) { // Make sure "Displays have separate Spaces" is unchecked // in System Preferences > Mission Control Process p = exec("defaults", "read", "com.apple.spaces", "spans-displays"); @@ -1158,7 +1158,7 @@ public class PApplet implements PConstants { * @param display the display number to check */ public int displayDensity(int display) { - if (PApplet.platform == PConstants.MACOSX) { + if (PApplet.platform == PConstants.MACOS) { // This should probably be reset each time there's a display change. // A 5-minute search didn't turn up any such event in the Java 7 API. // Also, should we use the Toolkit associated with the editor window? @@ -2696,7 +2696,7 @@ public class PApplet implements PConstants { int button = event.getButton(); // If running on Mac OS, allow ctrl-click as right mouse. - if (PApplet.platform == PConstants.MACOSX && event.getButton() == PConstants.LEFT) { + if (PApplet.platform == PConstants.MACOS && event.getButton() == PConstants.LEFT) { if (action == MouseEvent.PRESS && event.isControlDown()) { macosxLeftButtonWithCtrlPressed = true; } @@ -3037,8 +3037,8 @@ public class PApplet implements PConstants { // embedded inside an application that has its own close behavior. if (external && event.getKeyCode() == 'W' && - ((event.isMetaDown() && platform == MACOSX) || - (event.isControlDown() && platform != MACOSX))) { + ((event.isMetaDown() && platform == MACOS) || + (event.isControlDown() && platform != MACOS))) { // Can't use this native stuff b/c the native event might be NEWT // if (external && event.getNative() instanceof java.awt.event.KeyEvent && // ((java.awt.event.KeyEvent) event.getNative()).getModifiers() == @@ -3504,7 +3504,7 @@ public class PApplet implements PConstants { // in the user.dir part of the url params = new String[] { "cmd", "/c" }; - } else if (platform == MACOSX) { + } else if (platform == MACOS) { params = new String[] { "open" }; } else if (platform == LINUX) { @@ -3841,7 +3841,7 @@ public class PApplet implements PConstants { handleMethods("dispose"); } - if (platform == MACOSX) { + if (platform == MACOS) { try { final String td = "processing.core.ThinkDifferent"; final Class thinkDifferent = getClass().getClassLoader().loadClass(td); @@ -6678,7 +6678,7 @@ public class PApplet implements PConstants { (sketch.g instanceof PGraphicsOpenGL) && (platform == WINDOWS); if (hide) sketch.surface.setVisible(false); - if (platform == MACOSX && useNativeSelect != false) { + if (platform == MACOS && useNativeSelect != false) { FileDialog fileDialog = new FileDialog(parentFrame, prompt, FileDialog.LOAD); if (defaultSelection != null) { @@ -7967,7 +7967,7 @@ public class PApplet implements PConstants { // Workaround for bug in Java for OS X from Oracle (7u51) // https://github.com/processing/processing/issues/2181 - if (platform == MACOSX) { + if (platform == MACOS) { if (jarPath.contains("Contents/Java/")) { String appPath = jarPath.substring(0, jarPath.indexOf(".app") + 4); File containingFolder = new File(appPath).getParentFile(); @@ -9258,7 +9258,7 @@ public class PApplet implements PConstants { static Pattern matchPattern(String regexp) { Pattern p = null; if (matchPatterns == null) { - matchPatterns = new LinkedHashMap(16, 0.75f, true) { + matchPatterns = new LinkedHashMap<>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { // Limit the number of match patterns at 10 most recently used @@ -10857,7 +10857,7 @@ public class PApplet implements PConstants { } } - if (platform == MACOSX) { + if (platform == MACOS) { try { final String td = "processing.core.ThinkDifferent"; Class thinkDifferent = @@ -11077,7 +11077,7 @@ public class PApplet implements PConstants { /** Convenience method, should only be called by PSurface subclasses. */ static public void hideMenuBar() { - if (PApplet.platform == PConstants.MACOSX) { + if (PApplet.platform == PConstants.MACOS) { // Call some native code to remove the menu bar on OS X. Not necessary // on Linux and Windows, who are happy to make full screen windows. japplemenubar.JAppleMenuBar.hide(); diff --git a/core/src/processing/core/PFont.java b/core/src/processing/core/PFont.java index 33ca7eb40..9812063af 100644 --- a/core/src/processing/core/PFont.java +++ b/core/src/processing/core/PFont.java @@ -914,7 +914,7 @@ public class PFont implements PConstants { GraphicsEnvironment.getLocalGraphicsEnvironment(); fonts = ge.getAllFonts(); - if (PApplet.platform == PConstants.MACOSX) { + if (PApplet.platform == PConstants.MACOS) { fontDifferent = new HashMap<>(); for (Font font : fonts) { // No need to use getPSName() anymore because getName() @@ -936,7 +936,7 @@ public class PFont implements PConstants { * See: issue #5481 */ static public Font findFont(String name) { - if (PApplet.platform == PConstants.MACOSX) { + if (PApplet.platform == PConstants.MACOS) { loadFonts(); Font maybe = fontDifferent.get(name); if (maybe != null) { diff --git a/core/src/processing/core/ThinkDifferent.java b/core/src/processing/core/ThinkDifferent.java index 5179bab5e..4c6de03ed 100644 --- a/core/src/processing/core/ThinkDifferent.java +++ b/core/src/processing/core/ThinkDifferent.java @@ -22,11 +22,13 @@ package processing.core; -import java.awt.*; +import java.awt.Desktop; +import java.awt.Image; +import java.awt.Taskbar; /** - * Deal with issues related to Mac OS window behavior. + * Deal with issues related to macOS application behavior. * * We have to register a quit handler to safely shut down the sketch, * otherwise OS X will just kill the sketch when a user hits Cmd-Q. @@ -37,15 +39,15 @@ import java.awt.*; * 3301. */ public class ThinkDifferent { - - private static Desktop desktop; - private static Taskbar taskbar; + static private Desktop desktop; + static private Taskbar taskbar; // True if user has tried to quit once. Prevents us from canceling the quit // call if the sketch is held up for some reason, like an exception that's // managed to put the sketch in a bad state. static boolean attemptedQuit; + /** * Initialize the sketch with the quit handler. * @@ -70,6 +72,7 @@ public class ThinkDifferent { }); } + /** * Remove the quit handler. */ @@ -77,44 +80,36 @@ public class ThinkDifferent { getDesktop().setQuitHandler(null); } + /** * Called via reflection from PSurfaceAWT and others, set the dock icon image. - * * @param image The image to provide for Processing icon. */ static public void setIconImage(Image image) { getTaskbar().setIconImage(image); } + /** * Get the taskbar where OS visual settings can be provided. - * * @return Cached taskbar singleton instance. */ static private Taskbar getTaskbar() { if (taskbar == null) { taskbar = Taskbar.getTaskbar(); } - return taskbar; } + /** * Get the desktop where OS behavior can be provided. - * * @return Cached desktop singleton instance. */ static private Desktop getDesktop() { if (desktop == null) { desktop = Desktop.getDesktop(); } - return desktop; } - - - // Instead, just use Application.getApplication() inside your app -// static public Application getApplication() { -// return desktop; -// } } diff --git a/core/src/processing/javafx/PSurfaceFX.java b/core/src/processing/javafx/PSurfaceFX.java index c2905f08f..831ef4418 100644 --- a/core/src/processing/javafx/PSurfaceFX.java +++ b/core/src/processing/javafx/PSurfaceFX.java @@ -154,7 +154,7 @@ public class PSurfaceFX implements PSurface { //addEventHandler(eventType, eventHandler); - EventHandler mouseHandler = new EventHandler() { + EventHandler mouseHandler = new EventHandler<>() { public void handle(MouseEvent e) { fxMouseEvent(e); } @@ -175,7 +175,7 @@ public class PSurfaceFX implements PSurface { } }); - EventHandler keyHandler = new EventHandler() { + EventHandler keyHandler = new EventHandler<>() { public void handle(KeyEvent e) { fxKeyEvent(e); } @@ -248,7 +248,7 @@ public class PSurfaceFX implements PSurface { // See JEP 263 float renderScale = Screen.getMainScreen().getRecommendedOutputScaleX(); - if (PApplet.platform == PConstants.MACOSX) { + if (PApplet.platform == PConstants.MACOS) { for (Screen s : Screen.getScreens()) { renderScale = Math.max(renderScale, s.getRecommendedOutputScaleX()); } @@ -337,7 +337,7 @@ public class PSurfaceFX implements PSurface { // Workaround for https://bugs.openjdk.java.net/browse/JDK-8136495 // https://github.com/processing/processing/issues/3823 - if ((PApplet.platform == PConstants.MACOSX || + if ((PApplet.platform == PConstants.MACOS || PApplet.platform == PConstants.LINUX) && PApplet.javaVersionName.compareTo("1.8.0_60") >= 0 && PApplet.javaVersionName.compareTo("1.8.0_72") < 0) { diff --git a/core/todo.txt b/core/todo.txt index a5f4af95a..d18c7e6fd 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -3,6 +3,10 @@ X check for disabling FBO code (fixes Intel HD 3000) X https://github.com/processing/processing/issues/4104 X https://github.com/processing/processing/pull/5881 +_ make edits to core so that awt can be flagged off, and lwjgl can run +_ https://github.com/codeanticode/processing-openjdk/commit/ac9abc18655daaa538ef16945687177334f3596e +_ removing AWT from core +_ https://github.com/codeanticode/processing-lwjgl/wiki#making-awt-optional-in-papplet _ size() issues on Mojave? (3.4 works, 3.5.3 does not) _ https://github.com/processing/processing/issues/5791 diff --git a/java/.classpath b/java/.classpath index 84c6ef574..b1548185a 100644 --- a/java/.classpath +++ b/java/.classpath @@ -25,7 +25,5 @@ - - diff --git a/java/.gitignore b/java/.gitignore index 325dcf926..4eecb6de1 100644 --- a/java/.gitignore +++ b/java/.gitignore @@ -1,5 +1,5 @@ reference.zip bin +bin-test generated mode/JavaMode.jar - diff --git a/java/build.xml b/java/build.xml index 32893549b..a942695f7 100644 --- a/java/build.xml +++ b/java/build.xml @@ -54,13 +54,13 @@ + - - + @@ -104,8 +104,8 @@ - - - + - + - + - - - + diff --git a/java/libraries/net/build.xml b/java/libraries/net/build.xml index 40e22e7f7..064949012 100644 --- a/java/libraries/net/build.xml +++ b/java/libraries/net/build.xml @@ -13,8 +13,8 @@ - - - - buildCoreAndDefaultImports(PdePreprocessor p) { + private static List buildCoreAndDefaultImports(PdePreprocessor preprocessor) { List result = new ArrayList<>(); - for (String imp : ImportUtil.getCoreImports()) { + for (String imp : preprocessor.getCoreImports()) { result.add(ImportStatement.parse(imp)); } - for (String imp : ImportUtil.getDefaultImports()) { + for (String imp : preprocessor.getDefaultImports()) { result.add(ImportStatement.parse(imp)); } diff --git a/java/src/processing/mode/java/pdex/util/RuntimePathBuilder.java b/java/src/processing/mode/java/pdex/util/RuntimePathBuilder.java index f3bc609e0..368fe7405 100644 --- a/java/src/processing/mode/java/pdex/util/RuntimePathBuilder.java +++ b/java/src/processing/mode/java/pdex/util/RuntimePathBuilder.java @@ -20,22 +20,31 @@ along with this program; if not, write to the Free Software Foundation, Inc. package processing.mode.java.pdex.util; -import com.google.classpath.ClassPathFactory; -import processing.app.*; -import processing.mode.java.JavaMode; -import processing.mode.java.pdex.ImportStatement; -import processing.mode.java.pdex.PreprocessedSketch; - import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Paths; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.google.classpath.ClassPathFactory; + +import processing.app.Library; +import processing.app.Messages; +import processing.app.Sketch; +import processing.app.SketchException; +import processing.app.Util; +import processing.mode.java.JavaMode; +import processing.mode.java.pdex.ImportStatement; +import processing.mode.java.pdex.PreprocessedSketch; + /** * Builder which generates runtime paths using a series of caches. diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeErrorListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeErrorListener.java deleted file mode 100644 index 2662ada7b..000000000 --- a/java/src/processing/mode/java/preproc/PdeParseTreeErrorListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* -Part of the Processing project - http://processing.org - -Copyright (c) 2012-19 The Processing Foundation - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software Foundation, -Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package processing.mode.java.preproc; - -import processing.mode.java.preproc.issue.PdePreprocessIssue; - - -/** - * Listener for issues encountered while processing a valid pde parse tree. - */ -interface PdeParseTreeErrorListener { - - /** - * Callback to invoke when an issue is encountered while processing a valid PDE parse tree. - * - * @param issue The issue reported. - */ - void onError(PdePreprocessIssue issue); - -} diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java index 397fa89cb..aff5b4dbc 100644 --- a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java +++ b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java @@ -21,14 +21,15 @@ package processing.mode.java.preproc; +import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; 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.ImportStatement; import processing.mode.java.pdex.TextTransform; @@ -49,18 +50,17 @@ 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; + private Optional destinationPackageName; - protected Mode mode = Mode.JAVA; + private Mode mode = Mode.JAVA; private boolean foundMain; private int lineOffset; @@ -80,6 +80,10 @@ public class PdeParseTreeListener extends ProcessingBaseListener { private RewriteResult headerResult; private RewriteResult footerResult; + private String indent1; + private String indent2; + private String indent3; + private Optional pdeParseTreeErrorListenerMaybe; /** @@ -88,15 +92,32 @@ public class PdeParseTreeListener extends ProcessingBaseListener { * @param tokens The tokens over which to rewrite. * @param newSketchName The name of the sketch being traversed. * @param newTabSize Size of tab / indent. + * @param newDestinationPackageName The package to which generated code should be assigned (the + * package to which the sketch code java file should be assigned). */ - PdeParseTreeListener(TokenStream tokens, String newSketchName, int newTabSize) { + public PdeParseTreeListener(TokenStream tokens, String newSketchName, int newTabSize, + Optional 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