From a9fe617303f9726794451eaf2c517a8ec57f3faf Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Sun, 5 Jul 2009 21:47:24 +0200 Subject: Final touches on the installer: - some bug fixes for linux - renamed InstallerWindow to Installer - Apple-specific prettification added (dock icon, app name) - more documentation. Also, I started added an open source licence to all files. --- LICENCE | 19 + README | 2 +- build.xml | 23 +- src/lombok/installer/AppleNativeLook.java | 41 ++ src/lombok/installer/EclipseFinder.java | 132 ++++- src/lombok/installer/EclipseLocation.java | 116 ++++- src/lombok/installer/Installer.java | 832 ++++++++++++++++++++++++++++++ src/lombok/installer/InstallerWindow.java | 740 -------------------------- src/lombok/installer/lombok.svg | 5 + src/lombok/installer/package-info.java | 28 + 10 files changed, 1164 insertions(+), 774 deletions(-) create mode 100644 LICENCE create mode 100644 src/lombok/installer/AppleNativeLook.java create mode 100644 src/lombok/installer/Installer.java delete mode 100644 src/lombok/installer/InstallerWindow.java create mode 100644 src/lombok/installer/package-info.java diff --git a/LICENCE b/LICENCE new file mode 100644 index 00000000..c370acef --- /dev/null +++ b/LICENCE @@ -0,0 +1,19 @@ +Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README b/README index afb7c4bd..c900d849 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ projectlombok makes java a spicier language by adding 'handlers' that know how t Using lombok is made as simple as possible, and aims to work on all major java IDEs as well as javac itself. -projectlombok is open source under the Creative Commons - free to share, free to remix, provided you attribute the authors licence, as shown here: http://creativecommons.org/licenses/by/3.0/us/ +See LICENCE for the project lombok licence. Project Authors: diff --git a/build.xml b/build.xml index 588dd8af..f285fe7e 100644 --- a/build.xml +++ b/build.xml @@ -1,3 +1,24 @@ + @@ -98,7 +119,7 @@ - + diff --git a/src/lombok/installer/AppleNativeLook.java b/src/lombok/installer/AppleNativeLook.java new file mode 100644 index 00000000..1d3cfff7 --- /dev/null +++ b/src/lombok/installer/AppleNativeLook.java @@ -0,0 +1,41 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.installer; + +import java.io.IOException; + +import javax.imageio.ImageIO; + +import com.apple.eawt.Application; + +/** + * Mac OS X specific code to gussy up the GUI a little bit, mostly with a nice dock icon. Well, nicer than + * the standard icon, at any rate. + */ +class AppleNativeLook { + public static void go() throws IOException { + Application app = Application.getApplication(); + app.removeAboutMenuItem(); + app.removePreferencesMenuItem(); + app.setDockIconImage(ImageIO.read(AppleNativeLook.class.getResource("lombokIcon.png"))); + } +} diff --git a/src/lombok/installer/EclipseFinder.java b/src/lombok/installer/EclipseFinder.java index 1ca82440..3c966359 100644 --- a/src/lombok/installer/EclipseFinder.java +++ b/src/lombok/installer/EclipseFinder.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.installer; import static java.util.Arrays.asList; @@ -18,8 +39,17 @@ import java.util.regex.Pattern; import lombok.Lombok; -public class EclipseFinder { - public static File findOurJar() { +/** Utility class for doing various OS-specific operations related to finding eclipse installations. */ +class EclipseFinder { + private EclipseFinder() { + //Prevent instantiation. + } + + /** + * Returns a File object pointing to our own jar file. Will obviously fail if the installer was started via + * a jar that wasn't accessed via the file-system, or if its started via e.g. unpacking the jar. + */ + static File findOurJar() { try { URI uri = EclipseFinder.class.getResource("/" + EclipseFinder.class.getName().replace('.', '/') + ".class").toURI(); Pattern p = Pattern.compile("^jar:file:([^\\!]+)\\!.*\\.class$"); @@ -33,7 +63,15 @@ public class EclipseFinder { } } - public static List getDrivesOnWindows() throws IOException { + /** + * Returns all drive letters on windows, regardless of what kind of drive is represented. + * + * Relies on the 'fsutil.exe' program. I have no idea if you can call it without triggering a gazillion + * security warnings on Vista. I did a cursory test on an out-of-the-box Windows XP and that seems to work. + * + * @return A List of drive letters, such as ["A", "C", "D", "X"]. + */ + static List getDrivesOnWindows() throws IOException { ProcessBuilder builder = new ProcessBuilder("c:\\windows\\system32\\fsutil.exe", "fsinfo", "drives"); builder.redirectErrorStream(true); Process process = builder.start(); @@ -58,7 +96,12 @@ public class EclipseFinder { return drives; } - public static boolean isLocalDriveOnWindows(String driveLetter) { + /** + * @return true if the letter represents a local fixed disk, false if its a disk drive, optical drive, + * USB stick, network drive, or any other kind of drive. Substed (virtual) drives that are an alias to + * a directory on a local disk cause a 'return true', but this is intentional. + */ + static boolean isLocalDriveOnWindows(String driveLetter) { if ( driveLetter == null || driveLetter.length() == 0 ) return false; try { ProcessBuilder builder = new ProcessBuilder("c:\\windows\\system32\\fsutil.exe", "fsinfo", "drivetype", driveLetter + ":"); @@ -78,7 +121,19 @@ public class EclipseFinder { } } - public static List findEclipseOnWindows() { + /** + * Returns a list of paths of eclipse installations. + * Eclipse installations are found by checking for the existence of 'eclipse.exe' in the following locations: + * + * X:\*Program Files*\*Eclipse* + * X:\*Eclipse* + * + * Where 'X' is tried for all local disk drives, unless there's a problem calling fsutil, in which case only + * C: is tried. + * + * @return A List of directories that contain 'eclipse.exe'. + */ + static List findEclipseOnWindows() { List eclipses = new ArrayList(); List driveLetters = asList("C"); @@ -109,34 +164,71 @@ public class EclipseFinder { return eclipses; } + /** Checks if the provided directory contains 'eclipse.exe', and if so, returns the directory, otherwise null. */ private static String findEclipseOnWindows1(File dir) { - if ( new File(dir, "eclipse.exe").isFile() ) return dir.toString(); + if ( new File(dir, "eclipse.exe").isFile() ) return dir.getAbsolutePath(); return null; } - public static void main(String[] args) throws Exception { - System.out.println(findEclipses()); + /** + * Calls the OS-dependent 'find eclipse' routine. If the local OS doesn't have a routine written for it, + * null is returned. + * + * @return List of directories that contain the eclipse executable. + */ + static List findEclipses() { + switch ( getOS() ) { + case WINDOWS: + return findEclipseOnWindows(); + case MAC_OS_X: + return findEclipseOnMac(); + default: + case UNIX: + return null; + } } - public static List findEclipses() { - String prop = System.getProperty("os.name", ""); - if ( prop.equalsIgnoreCase("Mac OS X") ) return findEclipseOnMac(); - if ( prop.equalsIgnoreCase("Windows XP") ) return findEclipseOnWindows(); - - return null; + static enum OS { + MAC_OS_X, WINDOWS, UNIX; } - public static String getEclipseExecutableName() { - String prop = System.getProperty("os.name", ""); - if ( prop.equalsIgnoreCase("Mac OS X") ) return "Eclipse.app"; - if ( prop.equalsIgnoreCase("Windows XP") ) return "eclipse.exe"; + static OS getOS() { + String prop = System.getProperty("os.name", "").toLowerCase(); + if ( prop.matches("^.*\\bmac\\b.*$") ) return OS.MAC_OS_X; + if ( prop.matches("^.*\\bwin(dows)\\b.*$") ) return OS.WINDOWS; - return "eclipse"; + return OS.UNIX; } - public static List findEclipseOnMac() { + /** + * Returns the proper name of the executable for the local OS. + * + * @return 'Eclipse.app' on OS X, 'eclipse.exe' on Windows, and 'eclipse' on other OSes. + */ + static String getEclipseExecutableName() { + switch ( getOS() ) { + case WINDOWS: + return "eclipse.exe"; + case MAC_OS_X: + return "Eclipse.app"; + default: + case UNIX: + return "eclipse"; + } + } + + /** + * Scans /Applications for any folder named 'Eclipse' + */ + static List findEclipseOnMac() { List eclipses = new ArrayList(); for ( File dir : new File("/Applications").listFiles() ) { + if ( !dir.isDirectory() ) continue; + if ( dir.getName().toLowerCase().equals("eclipse.app") ) { + //This would be kind of an unorthodox eclipse installation, but if eclipse ever + //moves to this more maclike installation concept, our installer can still handle it. + eclipses.add("/Applications"); + } if ( dir.getName().toLowerCase().contains("eclipse") ) { if ( new File(dir, "Eclipse.app").exists() ) eclipses.add(dir.toString()); } diff --git a/src/lombok/installer/EclipseLocation.java b/src/lombok/installer/EclipseLocation.java index 400b5606..66bba5b8 100644 --- a/src/lombok/installer/EclipseLocation.java +++ b/src/lombok/installer/EclipseLocation.java @@ -1,3 +1,24 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.installer; import java.io.BufferedReader; @@ -7,6 +28,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.jar.JarFile; @@ -17,6 +39,11 @@ import java.util.zip.ZipEntry; import javax.swing.JFrame; import javax.swing.JOptionPane; +/** + * Represents an eclipse installation. + * An instance can figure out if an eclipse installation has been lombok-ified, and can + * install and uninstall lombok from the eclipse installation. + */ final class EclipseLocation { private static final String OS_NEWLINE; @@ -30,8 +57,14 @@ final class EclipseLocation { private final File path; private volatile boolean hasLombok; + + /** Toggling the 'selected' checkbox in the GUI is tracked via this boolean */ boolean selected = true; + /** + * Thrown when creating a new EclipseLocation with a path object that doesn't, in fact, + * point at an eclipse installation. + */ final class NotAnEclipseException extends Exception { private static final long serialVersionUID = 1L; @@ -39,11 +72,19 @@ final class EclipseLocation { super(message, cause); } - public void showDialog(JFrame appWindow) { + /** + * Renders a message dialog with information about what went wrong. + */ + void showDialog(JFrame appWindow) { JOptionPane.showMessageDialog(appWindow, getMessage(), "Cannot configure eclipse installation", JOptionPane.WARNING_MESSAGE); } } + /** + * Create a new EclipseLocation by pointing at either the directory contain the eclipse executable, or the executable itself. + * + * @throws NotAnEclipseException If this isn't an eclipse executable or a directory with an eclipse executable. + */ EclipseLocation(String path) throws NotAnEclipseException { if ( path == null ) throw new NullPointerException("path"); File p = new File(path); @@ -59,6 +100,10 @@ final class EclipseLocation { } } + if ( !p.exists() || !p.getName().equalsIgnoreCase(execName) ) { + throw new NotAnEclipseException("This path does not appear to contain an eclipse installation: " + p, null); + } + this.path = p; try { this.hasLombok = checkForLombok(); @@ -69,23 +114,35 @@ final class EclipseLocation { } } - public int hashCode() { + @Override public int hashCode() { return path.hashCode(); } - public boolean equals(Object o) { + @Override public boolean equals(Object o) { if ( !(o instanceof EclipseLocation) ) return false; return ((EclipseLocation)o).path.equals(path); } - public String getPath() { + /** + * Returns the absolute path to the eclipse executable. + * + * Executables: "eclipse.exe" (Windows), "Eclipse.app" (Mac OS X), "eclipse" (Linux and other unixes). + */ + String getPath() { return path.getAbsolutePath(); } - public boolean hasLombok() { + /** + * @return true if the eclipse installation has been instrumented with lombok. + */ + boolean hasLombok() { return hasLombok; } + /** + * Returns the various directories that can contain the 'eclipse.ini' file. + * Returns multiple directories because there are a few different ways eclipse is packaged. + */ private List getTargetDirs() { return Arrays.asList(path.getParentFile(), new File(new File(path, "Contents"), "MacOS")); } @@ -121,7 +178,8 @@ final class EclipseLocation { } } - public class UninstallException extends Exception { + /** Thrown when uninstalling lombok fails. */ + class UninstallException extends Exception { private static final long serialVersionUID = 1L; public UninstallException(String message, Throwable cause) { @@ -129,7 +187,15 @@ final class EclipseLocation { } } - public void uninstall() throws UninstallException { + /** + * Uninstalls lombok from this location. + * It's a no-op if lombok wasn't there in the first place, + * and it will remove a half-succeeded lombok installation as well. + * + * @throws UninstallException If there's an obvious I/O problem that is preventing installation. + * bugs in the uninstall code will probably throw other exceptions; this is intentional. + */ + void uninstall() throws UninstallException { for ( File dir : getTargetDirs() ) { File lombokJar = new File(dir, "lombok.jar"); if ( lombokJar.exists() ) { @@ -194,7 +260,8 @@ final class EclipseLocation { } } - public class InstallException extends Exception { + /** Thrown when installing lombok fails. */ + class InstallException extends Exception { private static final long serialVersionUID = 1L; public InstallException(String message, Throwable cause) { @@ -202,18 +269,31 @@ final class EclipseLocation { } } - public void install() throws InstallException { + /** + * Install lombok into the eclipse at this location. + * If lombok is already there, it is overwritten neatly (upgrade mode). + * + * @throws InstallException If there's an obvious I/O problem that is preventing installation. + * bugs in the install code will probably throw other exceptions; this is intentional. + */ + void install() throws InstallException { + List failedDirs = new ArrayList(); + + boolean installSucceeded = false; for ( File dir : getTargetDirs() ) { File iniFile = new File(dir, "eclipse.ini"); StringBuilder newContents = new StringBuilder(); - if ( iniFile.exists() ) { + if ( !iniFile.exists() ) failedDirs.add(dir); + else { + //If 'installSucceeded' is true here, something very weird is going on, but instrumenting all of them + //is no less bad than aborting, and this situation should be rare to the point of non-existence. + File lombokJar = new File(iniFile.getParentFile(), "lombok.jar"); File agentJar = new File(iniFile.getParentFile(), "lombok.eclipse.agent.jar"); File ourJar = EclipseFinder.findOurJar(); byte[] b = new byte[524288]; boolean readSucceeded = false; - boolean installSucceeded = false; try { JarFile jar = new JarFile(ourJar); @@ -297,7 +377,6 @@ final class EclipseLocation { fos.close(); } installSucceeded = true; - } catch ( IOException e ) { throw new InstallException("Cannot install lombok at " + path.getAbsolutePath() + " probably because this installer does not have the access rights to do so.", e); @@ -309,5 +388,18 @@ final class EclipseLocation { } } } + + if ( !installSucceeded ) { + throw new InstallException("I can't find the eclipse.ini file. Is this a real eclipse installation?", null); + } + + for ( File dir : failedDirs ) { + //If we're updating the old installation might have worked by putting the lombok jars in a different place. + //We'll delete these old files. + try { + new File(dir, "lombok.jar").delete(); + new File(dir, "lombok.eclipse.agent.jar").delete(); + } catch ( Throwable ignore ) {} + } } } diff --git a/src/lombok/installer/Installer.java b/src/lombok/installer/Installer.java new file mode 100644 index 00000000..5d644868 --- /dev/null +++ b/src/lombok/installer/Installer.java @@ -0,0 +1,832 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.installer; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.HeadlessException; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.font.TextAttribute; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.Scrollable; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.filechooser.FileFilter; + +import lombok.core.Version; +import lombok.installer.EclipseFinder.OS; +import lombok.installer.EclipseLocation.InstallException; +import lombok.installer.EclipseLocation.NotAnEclipseException; +import lombok.installer.EclipseLocation.UninstallException; + +/** + * The lombok installer proper. + * Uses swing to show a simple GUI that can add and remove the java agent to eclipse installations. + * Also offers info on what this installer does in case people want to instrument their eclipse manually, + * and looks in some common places on Mac OS X and Windows. + */ +public class Installer { + private static final URI ABOUT_LOMBOK_URL = URI.create("http://projectlombok.org"); + + private JFrame appWindow; + + private JComponent loadingExpl; + + private Component javacArea; + private Component eclipseArea; + private Component uninstallArea; + private Component howIWorkArea; + + private Box uninstallBox; + private List toUninstall; + + private JHyperLink uninstallButton; + private JButton installButton; + + public static void main(String[] args) { + if ( EclipseFinder.getOS() == OS.MAC_OS_X ) { + System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Lombok Installer"); + System.setProperty("com.apple.macos.use-file-dialog-packages", "true"); + } + + try { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch ( Exception ignore ) {} + + new Installer().show(); + } catch ( HeadlessException e ) { + printHeadlessInfo(); + } + } + }); + } catch ( HeadlessException e ) { + printHeadlessInfo(); + } + } + + /** + * If run in headless mode, the installer can't show its fancy GUI. There's little point in running + * the installer without a GUI environment, as eclipse doesn't run in headless mode either, so + * we'll make do with showing some basic info on Lombok as well as instructions for using lombok with javac. + */ + private static void printHeadlessInfo() { + System.out.printf("About lombok v%s\n" + + "Lombok makes java better by providing very spicy additions to the Java programming language," + + "such as using @Getter to automatically generate a getter method for any field.\n\n" + + "Browse to %s for more information. To install lombok on eclipse, re-run this jar file on a " + + "graphical computer system - this message is being shown because your terminal is not graphics capable." + + "If you are just using 'javac' or a tool that calls on javac, no installation is neccessary; just " + + "make sure lombok.jar is in the classpath when you compile. Example:\n\n" + + " java -cp lombok.jar MyCode.java", Version.getVersion(), ABOUT_LOMBOK_URL); + } + + /** + * Creates a new installer that starts out invisible. + * Call the {@see #show()} method on a freshly created installer to render it. + */ + public Installer() { + appWindow = new JFrame(String.format("Project Lombok v%s - Installer", Version.getVersion())); + + appWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + appWindow.setResizable(false); + + try { + javacArea = buildJavacArea(); + eclipseArea = buildEclipseArea(); + uninstallArea = buildUninstallArea(); + uninstallArea.setVisible(false); + howIWorkArea = buildHowIWorkArea(); + howIWorkArea.setVisible(false); + buildChrome(appWindow.getContentPane()); + appWindow.pack(); + } catch ( Throwable t ) { + handleException(t); + } + } + + private void handleException(final Throwable t) { + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { + JOptionPane.showMessageDialog(appWindow, "There was a problem during the installation process:\n" + t, "Uh Oh!", JOptionPane.ERROR_MESSAGE); + t.printStackTrace(); + System.exit(1); + } + }); + } + + private Component buildHowIWorkArea() { + JPanel container = new JPanel(); + + container.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + + container.add(new JLabel(HOW_I_WORK_TITLE), constraints); + + constraints.gridy = 1; + constraints.insets = new Insets(8, 0, 0, 16); + container.add(new JLabel(HOW_I_WORK_EXPLANATION), constraints); + + Box buttonBar = Box.createHorizontalBox(); + JButton backButton = new JButton("Okay - Good to know!"); + buttonBar.add(Box.createHorizontalGlue()); + buttonBar.add(backButton); + + backButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + howIWorkArea.setVisible(false); + javacArea.setVisible(true); + eclipseArea.setVisible(true); + appWindow.pack(); + } + }); + + constraints.gridy = 2; + container.add(buttonBar, constraints); + + return container; + } + + private Component buildUninstallArea() { + JPanel container = new JPanel(); + + container.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + + container.add(new JLabel(UNINSTALL_TITLE), constraints); + + constraints.gridy = 1; + constraints.insets = new Insets(8, 0, 0, 16); + container.add(new JLabel(UNINSTALL_EXPLANATION), constraints); + + uninstallBox = Box.createVerticalBox(); + constraints.gridy = 2; + constraints.fill = GridBagConstraints.HORIZONTAL; + container.add(uninstallBox, constraints); + + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.gridy = 3; + container.add(new JLabel("Are you sure?"), constraints); + + Box buttonBar = Box.createHorizontalBox(); + JButton noButton = new JButton("No - Don't uninstall"); + buttonBar.add(noButton); + buttonBar.add(Box.createHorizontalGlue()); + JButton yesButton = new JButton("Yes - uninstall Lombok"); + buttonBar.add(yesButton); + + noButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + uninstallArea.setVisible(false); + javacArea.setVisible(true); + eclipseArea.setVisible(true); + appWindow.pack(); + } + }); + + yesButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + doUninstall(); + } + }); + + constraints.gridy = 4; + container.add(buttonBar, constraints); + + return container; + } + + private Component buildJavacArea() { + JPanel container = new JPanel(); + + container.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + + container.add(new JLabel(JAVAC_TITLE), constraints); + + constraints.gridy = 1; + constraints.insets = new Insets(8, 0, 0, 16); + container.add(new JLabel(JAVAC_EXPLANATION), constraints); + + JLabel example = new JLabel(JAVAC_EXAMPLE); + + constraints.gridy = 2; + constraints.insets = new Insets(8, 0, 0, 16); + container.add(example, constraints); + return container; + } + + private Component buildEclipseArea() throws IOException { + // "Or:" [I'll tell you where eclipse is] [Tell me how to install lombok manually] + + //Mode 2 (manual): + // Replace the entirety of the content (javac+eclipse) with an explanation about what to do: + // - copy lombok.jar to your eclipse directory. + // - jar xvf lombok.jar lombok.eclipse.agent.jar + // - edit eclipse.ini with: + // -javaagent:../../../lombok.eclipse.agent.jar + // -Xbootclasspath/a:../../../lombok.eclipse.agent.jar:../../../lombok.jar + + //Mode 3 (let me choose): + // pop up a file chooser. Make sure we don't care what you pick - eclipse.ini, eclipse.exe, eclipse.app, or dir. + // empty the list, remove the spinner and the [let me find eclipse on my own] button, and put the chosen + // eclipse in the list. + + JPanel container = new JPanel(); + + container.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.WEST; + + container.add(new JLabel(ECLIPSE_TITLE), constraints); + + constraints.gridy = 1; + constraints.insets = new Insets(8, 0, 0, 16); + container.add(new JLabel(ECLIPSE_EXPLANATION), constraints); + + constraints.gridy = 2; + loadingExpl = Box.createHorizontalBox(); + loadingExpl.add(new JLabel(new ImageIcon(Installer.class.getResource("/lombok/installer/loading.gif")))); + loadingExpl.add(new JLabel(ECLIPSE_LOADING_EXPLANATION)); + container.add(loadingExpl, constraints); + + constraints.weightx = 1.0; + constraints.gridy = 3; + constraints.fill = GridBagConstraints.HORIZONTAL; + eclipsesList = new EclipsesList(); + + JScrollPane eclipsesListScroll = new JScrollPane(eclipsesList); + eclipsesListScroll.setBackground(Color.WHITE); + container.add(eclipsesListScroll, constraints); + + Thread findEclipsesThread = new Thread() { + @Override public void run() { + try { + final List eclipses = EclipseFinder.findEclipses(); + final List locations = new ArrayList(); + final List problems = new ArrayList(); + + if ( eclipses != null ) { + for ( String eclipse : eclipses ) try { + locations.add(new EclipseLocation(eclipse)); + } catch ( NotAnEclipseException e ) { + problems.add(e); + } + } + + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { + for ( EclipseLocation location : locations ) { + try { + eclipsesList.addEclipse(location); + } catch ( Throwable t ) { + handleException(t); + } + } + + for ( NotAnEclipseException problem : problems ) { + problem.showDialog(appWindow); + } + + loadingExpl.setVisible(false); + + if ( eclipses == null ) { + JOptionPane.showMessageDialog(appWindow, + "I don't know how to automatically find eclipse installations on this platform.\n" + + "Please use the 'Specify Eclipse Location...' button to manually point out the\n" + + "location of your eclipse installation to me. Thanks!", "Can't find eclipse", JOptionPane.INFORMATION_MESSAGE); + } + } + }); + } catch ( Throwable t ) { + handleException(t); + } + } + }; + + findEclipsesThread.start(); + + Box buttonBar = Box.createHorizontalBox(); + JButton specifyEclipseLocationButton = new JButton("Specify eclipse location..."); + buttonBar.add(specifyEclipseLocationButton); + specifyEclipseLocationButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent event) { + final String name = EclipseFinder.getEclipseExecutableName(); + String file = null; + + if ( EclipseFinder.getOS() == OS.MAC_OS_X ) { + FileDialog chooser = new FileDialog(appWindow); + chooser.setMode(FileDialog.LOAD); + chooser.setFilenameFilter(new FilenameFilter() { + @Override public boolean accept(File dir, String name) { + if ( name.equalsIgnoreCase(name) ) return true; + if ( new File(dir, name).isDirectory() ) return true; + + return false; + } + }); + + chooser.setVisible(true); + file = new File(chooser.getDirectory(), chooser.getFile()).getAbsolutePath(); + } else { + JFileChooser chooser = new JFileChooser(); + + chooser.setAcceptAllFileFilterUsed(false); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setFileFilter(new FileFilter() { + @Override public boolean accept(File f) { + if ( f.getName().equalsIgnoreCase(name) ) return true; + if ( f.isDirectory() ) return true; + + return false; + } + + @Override public String getDescription() { + return "Eclipse Installation"; + } + }); + + switch ( chooser.showDialog(appWindow, "Select") ) { + case JFileChooser.APPROVE_OPTION: + file = chooser.getSelectedFile().getAbsolutePath(); + } + } + + if ( file != null ) { + try { + eclipsesList.addEclipse(new EclipseLocation(file)); + } catch ( NotAnEclipseException e ) { + e.showDialog(appWindow); + } catch ( Throwable t ) { + handleException(t); + } + } + } + }); + + buttonBar.add(Box.createHorizontalGlue()); + installButton = new JButton("Install / Update"); + buttonBar.add(installButton); + + installButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + List locationsToInstall = new ArrayList(eclipsesList.getSelectedEclipses()); + if ( locationsToInstall.isEmpty() ) { + JOptionPane.showMessageDialog(appWindow, "You haven't selected any eclipse installations!.", "No Selection", JOptionPane.WARNING_MESSAGE); + return; + } + + install(locationsToInstall); + } + }); + + constraints.gridy = 4; + constraints.weightx = 0; + container.add(buttonBar, constraints); + + constraints.gridy = 5; + constraints.fill = GridBagConstraints.NONE; + uninstallButton = new JHyperLink("Uninstall lombok from selected eclipse installations."); + uninstallButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + List locationsToUninstall = new ArrayList(); + for ( EclipseLocation location : eclipsesList.getSelectedEclipses() ) { + if ( location.hasLombok() ) locationsToUninstall.add(location); + } + + if ( locationsToUninstall.isEmpty() ) { + JOptionPane.showMessageDialog(appWindow, "You haven't selected any eclipse installations that have been lombok-enabled.", "No Selection", JOptionPane.WARNING_MESSAGE); + return; + } + + + uninstall(locationsToUninstall); + } + }); + container.add(uninstallButton, constraints); + + constraints.gridy = 6; + JHyperLink showMe = new JHyperLink("Show me what this installer will do to my eclipse installation."); + container.add(showMe, constraints); + showMe.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + showWhatIDo(); + } + }); + + return container; + } + + private void showWhatIDo() { + javacArea.setVisible(false); + eclipseArea.setVisible(false); + howIWorkArea.setVisible(true); + appWindow.pack(); + } + + private void uninstall(List locations) { + javacArea.setVisible(false); + eclipseArea.setVisible(false); + + uninstallBox.removeAll(); + uninstallBox.add(Box.createRigidArea(new Dimension(1, 16))); + for ( EclipseLocation location : locations ) { + JLabel label = new JLabel(location.getPath()); + label.setFont(label.getFont().deriveFont(Font.BOLD)); + uninstallBox.add(label); + } + uninstallBox.add(Box.createRigidArea(new Dimension(1, 16))); + + toUninstall = locations; + uninstallArea.setVisible(true); + appWindow.pack(); + } + + private void install(final List toInstall) { + JPanel spinner = new JPanel(); + spinner.setOpaque(true); + spinner.setLayout(new FlowLayout()); + spinner.add(new JLabel(new ImageIcon(Installer.class.getResource("/lombok/installer/loading.gif")))); + appWindow.setContentPane(spinner); + + final AtomicReference success = new AtomicReference(true); + + new Thread() { + @Override public void run() { + for ( EclipseLocation loc : toInstall ) { + try { + loc.install(); + } catch ( final InstallException e ) { + success.set(false); + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override public void run() { + JOptionPane.showMessageDialog(appWindow, + e.getMessage(), "Install Problem", JOptionPane.ERROR_MESSAGE); + } + }); + } catch ( Exception e2 ) { + //Shouldn't happen. + throw new RuntimeException(e2); + } + } + } + + if ( success.get() ) SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { + JOptionPane.showMessageDialog(appWindow, "Lombok has been installed on the selected eclipse installations.", "Install successful", JOptionPane.INFORMATION_MESSAGE); + appWindow.setVisible(false); + System.exit(0); + } + }); + } + }.start(); + } + + private void doUninstall() { + JPanel spinner = new JPanel(); + spinner.setOpaque(true); + spinner.setLayout(new FlowLayout()); + spinner.add(new JLabel(new ImageIcon(Installer.class.getResource("/lombok/installer/loading.gif")))); + + appWindow.setContentPane(spinner); + + final AtomicReference success = new AtomicReference(true); + new Thread() { + @Override public void run() { + for ( EclipseLocation loc : toUninstall ) { + try { + loc.uninstall(); + } catch ( final UninstallException e ) { + success.set(false); + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override public void run() { + JOptionPane.showMessageDialog(appWindow, + e.getMessage(), "Uninstall Problem", JOptionPane.ERROR_MESSAGE); + } + }); + } catch ( Exception e2 ) { + //Shouldn't happen. + throw new RuntimeException(e2); + } + } + } + + if ( success.get() ) SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { + JOptionPane.showMessageDialog(appWindow, "Lombok has been removed from the selected eclipse installations.", "Uninstall successful", JOptionPane.INFORMATION_MESSAGE); + appWindow.setVisible(false); + System.exit(0); + } + }); + } + }.start(); + } + + private EclipsesList eclipsesList = new EclipsesList(); + + private static class JHyperLink extends JButton { + private static final long serialVersionUID = 1L; + + public JHyperLink(String text) { + super(); + setFont(getFont().deriveFont(Collections.singletonMap(TextAttribute.UNDERLINE, 1))); + setText(text); + setBorder(null); + setContentAreaFilled(false); + setForeground(Color.BLUE); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + setMargin(new Insets(0, 0, 0, 0)); + } + } + + void selectedLomboksChanged(List selectedEclipses) { + boolean uninstallAvailable = false; + boolean installAvailable = false; + for ( EclipseLocation loc : selectedEclipses ) { + if ( loc.hasLombok() ) uninstallAvailable = true; + installAvailable = true; + } + + uninstallButton.setVisible(uninstallAvailable); + installButton.setEnabled(installAvailable); + } + + private class EclipsesList extends JPanel implements Scrollable { + private static final long serialVersionUID = 1L; + + List locations = new ArrayList(); + + EclipsesList() { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setBackground(Color.WHITE); + } + + List getSelectedEclipses() { + List list = new ArrayList(); + for ( EclipseLocation loc : locations ) if ( loc.selected ) list.add(loc); + return list; + } + + void fireSelectionChange() { + selectedLomboksChanged(getSelectedEclipses()); + } + + void addEclipse(final EclipseLocation location) { + if ( locations.contains(location) ) return; + Box box = Box.createHorizontalBox(); + box.setBackground(Color.WHITE); + final JCheckBox checkbox = new JCheckBox(location.getPath()); + box.add(checkbox); + checkbox.setSelected(true); + checkbox.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + location.selected = checkbox.isSelected(); + fireSelectionChange(); + } + }); + + if ( location.hasLombok() ) { + box.add(new JLabel(new ImageIcon(Installer.class.getResource("/lombok/installer/lombokIcon.png")))); + } + box.add(Box.createHorizontalGlue()); + locations.add(location); + add(box); + getParent().doLayout(); + fireSelectionChange(); + } + + @Override public Dimension getPreferredScrollableViewportSize() { + return new Dimension(1, 100); + } + + @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return 12; + } + + @Override public boolean getScrollableTracksViewportHeight() { + return false; + } + + @Override public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return 1; + } + }; + + private void buildChrome(Container appWindowContainer) { + JLabel leftGraphic = new JLabel(new ImageIcon(Installer.class.getResource("/lombok/installer/lombok.png"))); + JLabel topGraphic = new JLabel(new ImageIcon(Installer.class.getResource("/lombok/installer/lombokText.png"))); + + GridBagConstraints constraints = new GridBagConstraints(); + + appWindowContainer.setLayout(new GridBagLayout()); + + constraints.gridheight = 3; + constraints.gridwidth = 1; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.insets = new Insets(8, 8, 8, 8); + appWindowContainer.add(leftGraphic,constraints); + + constraints.gridx++; + constraints.gridheight = 1; + constraints.fill = GridBagConstraints.NONE; + constraints.ipadx = 40; + constraints.ipady = 64; + appWindowContainer.add(topGraphic, constraints); + + constraints.gridy++; + constraints.ipadx = 16; + constraints.ipady = 16; + appWindowContainer.add(javacArea, constraints); + + constraints.gridy++; + constraints.weightx = 1; + constraints.weighty = 1; + constraints.fill = GridBagConstraints.BOTH; + appWindowContainer.add(eclipseArea, constraints); + + appWindowContainer.add(uninstallArea, constraints); + + appWindowContainer.add(howIWorkArea, constraints); + + constraints.gridy++; + constraints.gridwidth = 2; + constraints.gridx = 0; + constraints.weightx = 0; + constraints.weighty = 0; + constraints.ipadx = 0; + constraints.ipady = 0; + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.anchor = GridBagConstraints.SOUTHEAST; + constraints.insets = new Insets(0, 16, 8, 8); + Box buttonBar = Box.createHorizontalBox(); + JButton quitButton = new JButton("Quit Installer"); + quitButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + appWindow.setVisible(false); + System.exit(0); + } + }); + final JHyperLink hyperlink = new JHyperLink(ABOUT_LOMBOK_URL.toString()); + hyperlink.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent event) { + hyperlink.setForeground(new Color(85, 145, 90)); + try { + //java.awt.Desktop doesn't exist in 1.5. + Object desktop = Class.forName("java.awt.Desktop").getMethod("getDesktop").invoke(null); + Class.forName("java.awt.Desktop").getMethod("browse", URI.class).invoke(desktop, ABOUT_LOMBOK_URL); + } catch ( Exception e ) { + Runtime rt = Runtime.getRuntime(); + try { + switch ( EclipseFinder.getOS() ) { + case WINDOWS: + String[] cmd = new String[4]; + cmd[0] = "cmd.exe"; + cmd[1] = "/C"; + cmd[2] = "start"; + cmd[3] = ABOUT_LOMBOK_URL.toString(); + rt.exec(cmd); + break; + case MAC_OS_X: + rt.exec( "open " + ABOUT_LOMBOK_URL.toString()); + break; + default: + case UNIX: + rt.exec("firefox " + ABOUT_LOMBOK_URL.toString()); + break; + } + } catch ( Exception e2 ) { + JOptionPane.showMessageDialog(appWindow, + "Well, this is embarrassing. I don't know how to open a webbrowser.\n" + + "I guess you'll have to open it. Browse to:\n" + + "http://projectlombok.org for more information about Lombok.", + "I'm embarrassed", JOptionPane.INFORMATION_MESSAGE); + } + } + } + }); + buttonBar.add(hyperlink); + buttonBar.add(Box.createRigidArea(new Dimension(16, 1))); + buttonBar.add(new JLabel("v" + Version.getVersion() + "")); + + buttonBar.add(Box.createHorizontalGlue()); + buttonBar.add(quitButton); + appWindow.add(buttonBar, constraints); + } + + /** + * Makes the installer window visible. + */ + public void show() { + appWindow.setVisible(true); + if ( EclipseFinder.getOS() == OS.MAC_OS_X ) { + try { + AppleNativeLook.go(); + } catch ( Throwable ignore ) { + //We're just prettying up the app. If it fails, meh. + } + } + } + + private static final String ECLIPSE_TITLE = + "Eclipse"; + + private static final String ECLIPSE_EXPLANATION = + "Lombok can update your eclipse to fully support all Lombok features.
" + + "Select eclipse installations below and hit 'Install/Update'."; + + private static final String ECLIPSE_LOADING_EXPLANATION = + "Scanning your drives for eclipse installations..."; + + private static final String JAVAC_TITLE = + "Javac       (and tools that invoke javac such as ant and maven)"; + + private static final String JAVAC_EXPLANATION = + "Lombok works 'out of the box' with javac.
Just make sure the lombok.jar is in your classpath when you compile."; + + private static final String JAVAC_EXAMPLE = + "Example: javac -cp lombok.jar MyCode.java"; + + private static final String UNINSTALL_TITLE = + "Uninstall"; + + private static final String UNINSTALL_EXPLANATION = + "Uninstall Lombok from the following Eclipse Installations?"; + + private static final String HOW_I_WORK_TITLE = + "What this installer does"; + + private static final String HOW_I_WORK_EXPLANATION = + "
    " + + "
  1. First, I copy myself (lombok.jar) to your eclipse install directory.
  2. " + + "
  3. Then, I unpack lombok.eclipse.agent.jar like so:
    " + + "
    jar xvf lombok.jar lombok.eclipse.agent.jar
  4. " + + "
  5. Then, I edit the eclipse.ini file to add the following two entries:
    " + + "
    -Xbootclasspath/a:lombok.jar:lombok.eclipse.agent.jar
    " + + "-javaagent:lombok.jar
" + + "
" + + "That's all there is to it. Note that on Mac OS X, eclipse.ini is hidden in
" + + "Eclipse.app/Contents/MacOS so that's where I place the jar files."; +} diff --git a/src/lombok/installer/InstallerWindow.java b/src/lombok/installer/InstallerWindow.java deleted file mode 100644 index a0911c26..00000000 --- a/src/lombok/installer/InstallerWindow.java +++ /dev/null @@ -1,740 +0,0 @@ -package lombok.installer; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.HeadlessException; -import java.awt.Insets; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.font.TextAttribute; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.Scrollable; -import javax.swing.SwingUtilities; -import javax.swing.filechooser.FileFilter; - -import lombok.core.Version; -import lombok.installer.EclipseLocation.InstallException; -import lombok.installer.EclipseLocation.NotAnEclipseException; -import lombok.installer.EclipseLocation.UninstallException; - -public class InstallerWindow { - private static final URI ABOUT_LOMBOK_URL = URI.create("http://projectlombok.org"); - - private JFrame appWindow; - - private JComponent loadingExpl; - - private Component javacArea; - private Component eclipseArea; - private Component uninstallArea; - private Component howIWorkArea; - - private Box uninstallBox; - - private List toUninstall; - - public static void main(String[] args) { - try { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - try { - new InstallerWindow().show(); - } catch ( HeadlessException e ) { - printHeadlessInfo(); - } - } - }); - } catch ( HeadlessException e ) { - printHeadlessInfo(); - } - } - - private static void printHeadlessInfo() { - System.out.printf("About lombok v%s\n" + - "Lombok makes java better by providing very spicy additions to the Java programming language," + - "such as using @Getter to automatically generate a getter method for any field.\n\n" + - "Browse to %s for more information. To install lombok on eclipse, re-run this jar file on a " + - "graphical computer system - this message is being shown because your terminal is not graphics capable." + - "If you are just using 'javac' or a tool that calls on javac, no installation is neccessary; just " + - "make sure lombok.jar is in the classpath when you compile. Example:\n\n" + - " java -cp lombok.jar MyCode.java", Version.getVersion(), ABOUT_LOMBOK_URL); - } - - public InstallerWindow() { - appWindow = new JFrame(String.format("Project Lombok v%s - Installer", Version.getVersion())); - - appWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - appWindow.setResizable(false); - - try { - javacArea = buildJavacArea(); - eclipseArea = buildEclipseArea(); - uninstallArea = buildUninstallArea(); - uninstallArea.setVisible(false); - howIWorkArea = buildHowIWorkArea(); - howIWorkArea.setVisible(false); - buildChrome(appWindow.getContentPane()); - appWindow.pack(); - } catch ( Throwable t ) { - handleException(t); - } - } - - private void handleException(final Throwable t) { - SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { - JOptionPane.showMessageDialog(appWindow, "There was a problem during the installation process:\n" + t, "Uh Oh!", JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - }); - } - - private Component buildHowIWorkArea() { - JPanel container = new JPanel(); - - container.setLayout(new GridBagLayout()); - GridBagConstraints constraints = new GridBagConstraints(); - constraints.anchor = GridBagConstraints.WEST; - - container.add(new JLabel(HOW_I_WORK_TITLE), constraints); - - constraints.gridy = 1; - constraints.insets = new Insets(8, 0, 0, 16); - container.add(new JLabel(HOW_I_WORK_EXPLANATION), constraints); - - Box buttonBar = Box.createHorizontalBox(); - JButton backButton = new JButton("Okay - Good to know!"); - buttonBar.add(Box.createHorizontalGlue()); - buttonBar.add(backButton); - - backButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - howIWorkArea.setVisible(false); - javacArea.setVisible(true); - eclipseArea.setVisible(true); - appWindow.pack(); - } - }); - - constraints.gridy = 2; - container.add(buttonBar, constraints); - - return container; - } - - private Component buildUninstallArea() { - JPanel container = new JPanel(); - - container.setLayout(new GridBagLayout()); - GridBagConstraints constraints = new GridBagConstraints(); - constraints.anchor = GridBagConstraints.WEST; - - container.add(new JLabel(UNINSTALL_TITLE), constraints); - - constraints.gridy = 1; - constraints.insets = new Insets(8, 0, 0, 16); - container.add(new JLabel(UNINSTALL_EXPLANATION), constraints); - - uninstallBox = Box.createVerticalBox(); - constraints.gridy = 2; - constraints.fill = GridBagConstraints.HORIZONTAL; - container.add(uninstallBox, constraints); - - constraints.fill = GridBagConstraints.HORIZONTAL; - constraints.gridy = 3; - container.add(new JLabel("Are you sure?"), constraints); - - Box buttonBar = Box.createHorizontalBox(); - JButton noButton = new JButton("No - Don't uninstall"); - buttonBar.add(noButton); - buttonBar.add(Box.createHorizontalGlue()); - JButton yesButton = new JButton("Yes - uninstall Lombok"); - buttonBar.add(yesButton); - - noButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - uninstallArea.setVisible(false); - javacArea.setVisible(true); - eclipseArea.setVisible(true); - appWindow.pack(); - } - }); - - yesButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - doUninstall(); - } - }); - - constraints.gridy = 4; - container.add(buttonBar, constraints); - - return container; - } - - private Component buildJavacArea() { - JPanel container = new JPanel(); - - container.setLayout(new GridBagLayout()); - GridBagConstraints constraints = new GridBagConstraints(); - constraints.anchor = GridBagConstraints.WEST; - - container.add(new JLabel(JAVAC_TITLE), constraints); - - constraints.gridy = 1; - constraints.insets = new Insets(8, 0, 0, 16); - container.add(new JLabel(JAVAC_EXPLANATION), constraints); - - JLabel example = new JLabel(JAVAC_EXAMPLE); - - constraints.gridy = 2; - constraints.insets = new Insets(8, 0, 0, 16); - container.add(example, constraints); - return container; - } - - private Component buildEclipseArea() throws IOException { - // "Or:" [I'll tell you where eclipse is] [Tell me how to install lombok manually] - - //Mode 2 (manual): - // Replace the entirety of the content (javac+eclipse) with an explanation about what to do: - // - copy lombok.jar to your eclipse directory. - // - jar xvf lombok.jar lombok.eclipse.agent.jar - // - edit eclipse.ini with: - // -javaagent:../../../lombok.eclipse.agent.jar - // -Xbootclasspath/a:../../../lombok.eclipse.agent.jar:../../../lombok.jar - - //Mode 3 (let me choose): - // pop up a file chooser. Make sure we don't care what you pick - eclipse.ini, eclipse.exe, eclipse.app, or dir. - // empty the list, remove the spinner and the [let me find eclipse on my own] button, and put the chosen - // eclipse in the list. - - JPanel container = new JPanel(); - - container.setLayout(new GridBagLayout()); - GridBagConstraints constraints = new GridBagConstraints(); - constraints.anchor = GridBagConstraints.WEST; - - container.add(new JLabel(ECLIPSE_TITLE), constraints); - - constraints.gridy = 1; - constraints.insets = new Insets(8, 0, 0, 16); - container.add(new JLabel(ECLIPSE_EXPLANATION), constraints); - - constraints.gridy = 2; - loadingExpl = Box.createHorizontalBox(); - loadingExpl.add(new JLabel(new ImageIcon(InstallerWindow.class.getResource("/lombok/installer/loading.gif")))); - loadingExpl.add(new JLabel(ECLIPSE_LOADING_EXPLANATION)); - container.add(loadingExpl, constraints); - - constraints.weightx = 1.0; - constraints.gridy = 3; - constraints.fill = GridBagConstraints.HORIZONTAL; - eclipsesList = new EclipsesList(); - - JScrollPane eclipsesListScroll = new JScrollPane(eclipsesList); - eclipsesListScroll.setBackground(Color.WHITE); - container.add(eclipsesListScroll, constraints); - - Thread findEclipsesThread = new Thread() { - @Override public void run() { - try { - final List eclipses = EclipseFinder.findEclipses(); - final List locations = new ArrayList(); - final List problems = new ArrayList(); - - for ( String eclipse : eclipses ) try { - locations.add(new EclipseLocation(eclipse)); - } catch ( NotAnEclipseException e ) { - problems.add(e); - } - - SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { - for ( EclipseLocation location : locations ) { - try { - eclipsesList.addEclipse(location); - } catch ( Throwable t ) { - handleException(t); - } - } - - for ( NotAnEclipseException problem : problems ) { - problem.showDialog(appWindow); - } - - loadingExpl.setVisible(false); - } - }); - } catch ( Throwable t ) { - handleException(t); - } - } - }; - - findEclipsesThread.start(); - - Box buttonBar = Box.createHorizontalBox(); - JButton specifyEclipseLocationButton = new JButton("Specify eclipse location..."); - buttonBar.add(specifyEclipseLocationButton); - specifyEclipseLocationButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent event) { - JFileChooser chooser = new JFileChooser(); - - chooser.setAcceptAllFileFilterUsed(false); - chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - chooser.setFileFilter(new FileFilter() { - private final String name = EclipseFinder.getEclipseExecutableName(); - @Override public boolean accept(File f) { - if ( f.getName().equalsIgnoreCase(name) ) return true; - if ( f.isDirectory() ) return true; - - return false; - } - - @Override public String getDescription() { - return "Eclipse Installation"; - } - }); - - switch ( chooser.showDialog(appWindow, "Select") ) { - case JFileChooser.APPROVE_OPTION: - try { - try { - eclipsesList.addEclipse(new EclipseLocation(chooser.getSelectedFile().getAbsolutePath())); - } catch ( NotAnEclipseException e ) { - e.showDialog(appWindow); - } - } catch ( Throwable t ) { - handleException(t); - } - } - } - }); - buttonBar.add(Box.createHorizontalGlue()); - installButton = new JButton("Install / Update"); - buttonBar.add(installButton); - - installButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - List locationsToInstall = new ArrayList(eclipsesList.getSelectedEclipses()); - if ( locationsToInstall.isEmpty() ) { - JOptionPane.showMessageDialog(appWindow, "You haven't selected any eclipse installations!.", "No Selection", JOptionPane.WARNING_MESSAGE); - return; - } - - install(locationsToInstall); - } - }); - - constraints.gridy = 4; - constraints.weightx = 0; - container.add(buttonBar, constraints); - - constraints.gridy = 5; - constraints.fill = GridBagConstraints.NONE; - uninstallButton = new JHyperLink("Uninstall lombok from selected eclipse installations."); - uninstallButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - List locationsToUninstall = new ArrayList(); - for ( EclipseLocation location : eclipsesList.getSelectedEclipses() ) { - if ( location.hasLombok() ) locationsToUninstall.add(location); - } - - if ( locationsToUninstall.isEmpty() ) { - JOptionPane.showMessageDialog(appWindow, "You haven't selected any eclipse installations that have been lombok-enabled.", "No Selection", JOptionPane.WARNING_MESSAGE); - return; - } - - - uninstall(locationsToUninstall); - } - }); - container.add(uninstallButton, constraints); - - constraints.gridy = 6; - JHyperLink showMe = new JHyperLink("Show me what this installer will do to my eclipse installation."); - container.add(showMe, constraints); - showMe.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - showWhatIDo(); - } - }); - - return container; - } - - private void showWhatIDo() { - javacArea.setVisible(false); - eclipseArea.setVisible(false); - howIWorkArea.setVisible(true); - appWindow.pack(); - } - - private void uninstall(List locations) { - javacArea.setVisible(false); - eclipseArea.setVisible(false); - - uninstallBox.removeAll(); - uninstallBox.add(Box.createRigidArea(new Dimension(1, 16))); - for ( EclipseLocation location : locations ) { - JLabel label = new JLabel(location.getPath()); - label.setFont(label.getFont().deriveFont(Font.BOLD)); - uninstallBox.add(label); - } - uninstallBox.add(Box.createRigidArea(new Dimension(1, 16))); - - toUninstall = locations; - uninstallArea.setVisible(true); - appWindow.pack(); - } - - private void install(final List toInstall) { - JPanel spinner = new JPanel(); - spinner.setOpaque(true); - spinner.setLayout(new FlowLayout()); - spinner.add(new JLabel(new ImageIcon(InstallerWindow.class.getResource("/lombok/installer/loading.gif")))); - appWindow.setContentPane(spinner); - - final AtomicReference success = new AtomicReference(true); - - new Thread() { - @Override public void run() { - for ( EclipseLocation loc : toInstall ) { - try { - loc.install(); - } catch ( final InstallException e ) { - success.set(false); - try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override public void run() { - JOptionPane.showMessageDialog(appWindow, - e.getMessage(), "Install Problem", JOptionPane.ERROR_MESSAGE); - } - }); - } catch ( Exception e2 ) { - //Shouldn't happen. - throw new RuntimeException(e2); - } - } - } - - if ( success.get() ) SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { - JOptionPane.showMessageDialog(appWindow, "Lombok has been installed on the selected eclipse installations.", "Install successful", JOptionPane.INFORMATION_MESSAGE); - appWindow.setVisible(false); - System.exit(0); - } - }); - } - }.start(); - } - - private void doUninstall() { - JPanel spinner = new JPanel(); - spinner.setOpaque(true); - spinner.setLayout(new FlowLayout()); - spinner.add(new JLabel(new ImageIcon(InstallerWindow.class.getResource("/lombok/installer/loading.gif")))); - - appWindow.setContentPane(spinner); - - final AtomicReference success = new AtomicReference(true); - new Thread() { - @Override public void run() { - for ( EclipseLocation loc : toUninstall ) { - try { - loc.uninstall(); - } catch ( final UninstallException e ) { - success.set(false); - try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override public void run() { - JOptionPane.showMessageDialog(appWindow, - e.getMessage(), "Uninstall Problem", JOptionPane.ERROR_MESSAGE); - } - }); - } catch ( Exception e2 ) { - //Shouldn't happen. - throw new RuntimeException(e2); - } - } - } - - if ( success.get() ) SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { - JOptionPane.showMessageDialog(appWindow, "Lombok has been removed from the selected eclipse installations.", "Uninstall successful", JOptionPane.INFORMATION_MESSAGE); - appWindow.setVisible(false); - System.exit(0); - } - }); - } - }.start(); - } - - private EclipsesList eclipsesList = new EclipsesList(); - - private static class JHyperLink extends JButton { - private static final long serialVersionUID = 1L; - - public JHyperLink(String text) { - super(); - setFont(getFont().deriveFont(Collections.singletonMap(TextAttribute.UNDERLINE, 1))); - setText(text); - setBorder(null); - setContentAreaFilled(false); - setForeground(Color.BLUE); - setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - setMargin(new Insets(0, 0, 0, 0)); - } - } - - void selectedLomboksChanged(List selectedEclipses) { - boolean uninstallAvailable = false; - boolean installAvailable = false; - for ( EclipseLocation loc : selectedEclipses ) { - if ( loc.hasLombok() ) uninstallAvailable = true; - installAvailable = true; - } - - uninstallButton.setVisible(uninstallAvailable); - installButton.setEnabled(installAvailable); - } - - private class EclipsesList extends JPanel implements Scrollable { - private static final long serialVersionUID = 1L; - - List locations = new ArrayList(); - - EclipsesList() { - setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - setBackground(Color.WHITE); - } - - List getSelectedEclipses() { - List list = new ArrayList(); - for ( EclipseLocation loc : locations ) if ( loc.selected ) list.add(loc); - return list; - } - - void fireSelectionChange() { - selectedLomboksChanged(getSelectedEclipses()); - } - - void addEclipse(final EclipseLocation location) { - if ( locations.contains(location) ) return; - Box box = Box.createHorizontalBox(); - box.setBackground(Color.WHITE); - final JCheckBox checkbox = new JCheckBox(location.getPath()); - box.add(checkbox); - checkbox.setSelected(true); - checkbox.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - location.selected = checkbox.isSelected(); - fireSelectionChange(); - } - }); - - if ( location.hasLombok() ) { - box.add(new JLabel(new ImageIcon(InstallerWindow.class.getResource("/lombok/installer/lombokIcon.png")))); - } - box.add(Box.createHorizontalGlue()); - locations.add(location); - add(box); - getParent().doLayout(); - fireSelectionChange(); - } - - @Override public Dimension getPreferredScrollableViewportSize() { - return new Dimension(1, 100); - } - - @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return 12; - } - - @Override public boolean getScrollableTracksViewportHeight() { - return false; - } - - @Override public boolean getScrollableTracksViewportWidth() { - return true; - } - - @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return 1; - } - }; - - private void buildChrome(Container appWindowContainer) { - JLabel leftGraphic = new JLabel(new ImageIcon(InstallerWindow.class.getResource("/lombok/installer/lombok.png"))); - JLabel topGraphic = new JLabel(new ImageIcon(InstallerWindow.class.getResource("/lombok/installer/lombokText.png"))); - - GridBagConstraints constraints = new GridBagConstraints(); - - appWindowContainer.setLayout(new GridBagLayout()); - - constraints.gridheight = 3; - constraints.gridwidth = 1; - constraints.gridx = 0; - constraints.gridy = 0; - constraints.insets = new Insets(8, 8, 8, 8); - appWindowContainer.add(leftGraphic,constraints); - - constraints.gridx++; - constraints.gridheight = 1; - constraints.fill = GridBagConstraints.NONE; - constraints.ipadx = 40; - constraints.ipady = 64; - appWindowContainer.add(topGraphic, constraints); - - constraints.gridy++; - constraints.ipadx = 16; - constraints.ipady = 16; - appWindowContainer.add(javacArea, constraints); - - constraints.gridy++; - constraints.weightx = 1; - constraints.weighty = 1; - constraints.fill = GridBagConstraints.BOTH; - appWindowContainer.add(eclipseArea, constraints); - - appWindowContainer.add(uninstallArea, constraints); - - appWindowContainer.add(howIWorkArea, constraints); - - constraints.gridy++; - constraints.gridwidth = 2; - constraints.gridx = 0; - constraints.weightx = 0; - constraints.weighty = 0; - constraints.ipadx = 0; - constraints.ipady = 0; - constraints.fill = GridBagConstraints.HORIZONTAL; - constraints.anchor = GridBagConstraints.SOUTHEAST; - constraints.insets = new Insets(0, 16, 8, 8); - Box buttonBar = Box.createHorizontalBox(); - JButton quitButton = new JButton("Quit Installer"); - quitButton.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { - appWindow.setVisible(false); - System.exit(0); - } - }); - final JHyperLink hyperlink = new JHyperLink(ABOUT_LOMBOK_URL.toString()); - hyperlink.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent event) { - hyperlink.setForeground(new Color(85, 145, 90)); - try { - //java.awt.Desktop doesn't exist in 1.5. - Object desktop = Class.forName("java.awt.Desktop").getMethod("getDesktop").invoke(null); - Class.forName("java.awt.Desktop").getMethod("browse", URI.class).invoke(desktop, ABOUT_LOMBOK_URL); - } catch ( Exception e ) { - String os = System.getProperty("os.name").toLowerCase(); - Runtime rt = Runtime.getRuntime(); - try { - if ( os.indexOf( "win" ) > -1 ) { - String[] cmd = new String[4]; - cmd[0] = "cmd.exe"; - cmd[1] = "/C"; - cmd[2] = "start"; - cmd[3] = ABOUT_LOMBOK_URL.toString(); - rt.exec(cmd); - } else if ( os.indexOf( "mac" ) >= 0 ) { - rt.exec( "open " + ABOUT_LOMBOK_URL.toString()); - } else { - rt.exec("firefox " + ABOUT_LOMBOK_URL.toString()); - } - } catch ( Exception e2 ) { - JOptionPane.showMessageDialog(appWindow, - "Well, this is embarrassing. I don't know how to open a webbrowser.\n" + - "I guess you'll have to open it. Browse to:\n" + - "http://projectlombok.org for more information about Lombok.", - "I'm embarrassed", JOptionPane.INFORMATION_MESSAGE); - } - } - } - }); - buttonBar.add(hyperlink); - buttonBar.add(Box.createRigidArea(new Dimension(16, 1))); - buttonBar.add(new JLabel("v" + Version.getVersion() + "")); - - buttonBar.add(Box.createHorizontalGlue()); - buttonBar.add(quitButton); - appWindow.add(buttonBar, constraints); - } - - public void show() { - appWindow.setVisible(true); - } - - private static final String ECLIPSE_TITLE = - "Eclipse"; - - private static final String ECLIPSE_EXPLANATION = - "Lombok can update your eclipse to fully support all Lombok features.
" + - "Select eclipse installations below and hit 'Install/Update'."; - - private static final String ECLIPSE_LOADING_EXPLANATION = - "Scanning your drives for eclipse installations..."; - - private static final String JAVAC_TITLE = - "Javac       (and tools that invoke javac such as ant and maven)"; - - private static final String JAVAC_EXPLANATION = - "Lombok works 'out of the box' with javac.
Just make sure the lombok.jar is in your classpath when you compile."; - - private static final String JAVAC_EXAMPLE = - "Example: javac -cp lombok.jar MyCode.java"; - - private static final String UNINSTALL_TITLE = - "Uninstall"; - - private static final String UNINSTALL_EXPLANATION = - "Uninstall Lombok from the following Eclipse Installations?"; - - private static final String HOW_I_WORK_TITLE = - "What this installer does"; - - private static final String HOW_I_WORK_EXPLANATION = - "
    " + - "
  1. First, I copy myself (lombok.jar) to your eclipse install directory.
  2. " + - "
  3. Then, I unpack lombok.eclipse.agent.jar like so:
    " + - "
    jar xvf lombok.jar lombok.eclipse.agent.jar
  4. " + - "
  5. Then, I edit the eclipse.ini file to add the following two entries:
    " + - "
    -Xbootclasspath/a:lombok.jar:lombok.eclipse.agent.jar
    " + - "-javaagent:lombok.jar
" + - "
" + - "That's all there is to it. Note that on Mac OS X, eclipse.ini is hidden in
" + - "Eclipse.app/Contents/MacOS so that's where I place the jar files."; - - private JHyperLink uninstallButton; - - private JButton installButton; -} diff --git a/src/lombok/installer/lombok.svg b/src/lombok/installer/lombok.svg index b28e1cbe..0d561aea 100644 --- a/src/lombok/installer/lombok.svg +++ b/src/lombok/installer/lombok.svg @@ -1,4 +1,9 @@ + +