From df84dffa62fc51231e9215d04b9751dba6c6cf4d Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 2 Dec 2009 23:15:51 +0100 Subject: Massive update to how the installer works, and added netbeans to the installer. --- .../installer/CorruptedIdeLocationException.java | 26 + src/installer/lombok/installer/EclipseFinder.java | 325 -------- .../lombok/installer/EclipseLocation.java | 474 ------------ src/installer/lombok/installer/IdeFinder.java | 144 ++++ src/installer/lombok/installer/IdeLocation.java | 74 ++ .../lombok/installer/IdeLocationProvider.java | 40 + .../lombok/installer/InstallException.java | 10 + src/installer/lombok/installer/Installer.java | 834 ++------------------- src/installer/lombok/installer/InstallerGUI.java | 796 ++++++++++++++++++++ .../lombok/installer/UninstallException.java | 10 + .../lombok/installer/eclipse/EclipseFinder.java | 198 +++++ .../lombok/installer/eclipse/EclipseLocation.java | 343 +++++++++ .../installer/eclipse/EclipseLocationProvider.java | 144 ++++ src/installer/lombok/installer/eclipse/eclipse.png | Bin 0 -> 1328 bytes .../lombok/installer/netbeans/NetbeansFinder.java | 201 +++++ .../installer/netbeans/NetbeansLocation.java | 282 +++++++ .../netbeans/NetbeansLocationProvider.java | 125 +++ .../lombok/installer/netbeans/netbeans.png | Bin 0 -> 1705 bytes 18 files changed, 2463 insertions(+), 1563 deletions(-) create mode 100644 src/installer/lombok/installer/CorruptedIdeLocationException.java delete mode 100644 src/installer/lombok/installer/EclipseFinder.java delete mode 100644 src/installer/lombok/installer/EclipseLocation.java create mode 100644 src/installer/lombok/installer/IdeFinder.java create mode 100644 src/installer/lombok/installer/IdeLocation.java create mode 100644 src/installer/lombok/installer/IdeLocationProvider.java create mode 100644 src/installer/lombok/installer/InstallException.java create mode 100644 src/installer/lombok/installer/InstallerGUI.java create mode 100644 src/installer/lombok/installer/UninstallException.java create mode 100644 src/installer/lombok/installer/eclipse/EclipseFinder.java create mode 100644 src/installer/lombok/installer/eclipse/EclipseLocation.java create mode 100644 src/installer/lombok/installer/eclipse/EclipseLocationProvider.java create mode 100644 src/installer/lombok/installer/eclipse/eclipse.png create mode 100644 src/installer/lombok/installer/netbeans/NetbeansFinder.java create mode 100644 src/installer/lombok/installer/netbeans/NetbeansLocation.java create mode 100644 src/installer/lombok/installer/netbeans/NetbeansLocationProvider.java create mode 100644 src/installer/lombok/installer/netbeans/netbeans.png (limited to 'src/installer') diff --git a/src/installer/lombok/installer/CorruptedIdeLocationException.java b/src/installer/lombok/installer/CorruptedIdeLocationException.java new file mode 100644 index 00000000..9b185d1e --- /dev/null +++ b/src/installer/lombok/installer/CorruptedIdeLocationException.java @@ -0,0 +1,26 @@ +package lombok.installer; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; + +/** + * Represents an installation location problem. + * You should throw it upon creation of a {@code IdeLocation} class + * if the provided location looks like your kind of IDE but there's something wrong with it. + */ +public class CorruptedIdeLocationException extends Exception { + private final String ideType; + + public CorruptedIdeLocationException(String message, String ideType, Throwable cause) { + super(message, cause); + this.ideType = ideType; + } + + public String getIdeType() { + return ideType; + } + + void showDialog(JFrame appWindow) { + JOptionPane.showMessageDialog(appWindow, getMessage(), "Cannot configure " + ideType + " installation", JOptionPane.WARNING_MESSAGE); + } +} diff --git a/src/installer/lombok/installer/EclipseFinder.java b/src/installer/lombok/installer/EclipseFinder.java deleted file mode 100644 index 8e45852c..00000000 --- a/src/installer/lombok/installer/EclipseFinder.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * 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; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import lombok.Lombok; -import lombok.core.Version; -import lombok.installer.EclipseLocation.NotAnEclipseException; - -/** 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$"); - Matcher m = p.matcher(uri.toString()); - if (!m.matches()) return new File("lombok.jar"); - String rawUri = m.group(1); - return new File(URLDecoder.decode(rawUri, Charset.defaultCharset().name())); - } catch (Exception e) { - throw Lombok.sneakyThrow(e); - } - } - - private static final AtomicBoolean windowsDriveInfoLibLoaded = new AtomicBoolean(false); - private static void loadWindowsDriveInfoLib() throws IOException { - if (!windowsDriveInfoLibLoaded.compareAndSet(false, true)) return; - - final String prefix = "lombok-" + Version.getVersion() + "-"; - - File temp = File.createTempFile("lombok", ".mark"); - File dll1 = new File(temp.getParentFile(), prefix + "WindowsDriveInfo-i386.dll"); - File dll2 = new File(temp.getParentFile(), prefix + "WindowsDriveInfo-x86_64.dll"); - temp.delete(); - dll1.deleteOnExit(); - dll2.deleteOnExit(); - try { - if (unpackDLL("WindowsDriveInfo-i386.dll", dll1)) { - System.load(dll1.getAbsolutePath()); - return; - } - } catch (Throwable ignore) {} - - try { - if (unpackDLL("WindowsDriveInfo-x86_64.dll", dll2)) { - System.load(dll2.getAbsolutePath()); - } - } catch (Throwable ignore) {} - } - - private static boolean unpackDLL(String dllName, File target) throws IOException { - InputStream in = EclipseFinder.class.getResourceAsStream(dllName); - try { - try { - FileOutputStream out = new FileOutputStream(target); - try { - byte[] b = new byte[32000]; - while (true) { - int r = in.read(b); - if (r == -1) break; - out.write(b, 0, r); - } - } finally { - out.close(); - } - } catch (IOException e) { - //Fall through - if there is a file named lombok-WindowsDriveInfo-arch.dll, we'll try it. - return target.exists() && target.canRead(); - } - } finally { - in.close(); - } - - return true; - } - - /** - * Returns all drive letters on windows, regardless of what kind of drive is represented. - * - * @return A List of drive letters, such as ["A", "C", "D", "X"]. - */ - static List getDrivesOnWindows() throws Throwable { - loadWindowsDriveInfoLib(); - - List drives = new ArrayList(); - - WindowsDriveInfo info = new WindowsDriveInfo(); - for (String drive : info.getLogicalDrives()) { - if (info.isFixedDisk(drive)) drives.add(drive); - } - - return drives; - } - - /** - * 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. - */ - private static void findEclipseOnWindows(List locations, List problems) { - List driveLetters = asList("C"); - try { - driveLetters = getDrivesOnWindows(); - } catch (Throwable ignore) { - ignore.printStackTrace(); - } - - //Various try/catch/ignore statements are in this for loop. Weird conditions on the disk can cause exceptions, - //such as an unformatted drive causing a NullPointerException on listFiles. Best action is almost invariably to just - //continue onwards. - for (String letter : driveLetters) { - try { - File f = new File(letter + ":\\"); - for (File dir : f.listFiles()) { - if (!dir.isDirectory()) continue; - try { - if (dir.getName().toLowerCase().contains("eclipse")) { - String eclipseLocation = findEclipseOnWindows1(dir); - if (eclipseLocation != null) { - try { - locations.add(EclipseLocation.create(eclipseLocation)); - } catch (NotAnEclipseException e) { - problems.add(e); - } - } - } - } catch (Exception ignore) {} - - try { - if (dir.getName().toLowerCase().contains("program files")) { - for (File dir2 : dir.listFiles()) { - if (!dir2.isDirectory()) continue; - if (dir.getName().toLowerCase().contains("eclipse")) { - String eclipseLocation = findEclipseOnWindows1(dir); - if (eclipseLocation != null) { - try { - locations.add(EclipseLocation.create(eclipseLocation)); - } catch (NotAnEclipseException e) { - problems.add(e); - } - } - } - } - } - } catch (Exception ignore) {} - } - } catch (Exception ignore) {} - } - } - - /** 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.getAbsolutePath(); - return null; - } - - /** - * Calls the OS-dependent 'find Eclipse' routine. If the local OS doesn't have a routine written for it, - * null is returned. - * - * @param locations - * List of valid eclipse locations - provide an empty list; this - * method will fill it. - * @param problems - * List of eclipse locations that seem to contain half-baked - * eclipses that can't be installed. Provide an empty list; this - * method will fill it. - */ - static void findEclipses(List locations, List problems) { - switch (getOS()) { - case WINDOWS: - findEclipseOnWindows(locations, problems); - break; - case MAC_OS_X: - findEclipseOnMac(locations, problems); - break; - default: - case UNIX: - findEclipseOnUnix(locations, problems); - break; - } - } - - static enum OS { - MAC_OS_X, WINDOWS, UNIX; - } - - static OS getOS() { - String prop = System.getProperty("os.name", "").toLowerCase(); - if (prop.matches("^.*\\bmac\\b.*$")) return OS.MAC_OS_X; - if (prop.matches("^.*\\bdarwin\\b.*$")) return OS.MAC_OS_X; - if (prop.matches("^.*\\bwin(dows)\\b.*$")) return OS.WINDOWS; - - return OS.UNIX; - } - - /** - * 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 a couple of likely locations on linux. */ - private static void findEclipseOnUnix(List locations, List problems) { - List guesses = new ArrayList(); - - File d; - - d = new File("/usr/bin/eclipse"); - if (d.exists()) guesses.add(d.getPath()); - d = new File("/usr/local/bin/eclipse"); - if (d.exists()) guesses.add(d.getPath()); - d = new File(System.getProperty("user.home", "."), "bin/eclipse"); - if (d.exists()) guesses.add(d.getPath()); - - findEclipseInSubDir("/usr/local/share", guesses); - findEclipseInSubDir("/usr/local", guesses); - findEclipseInSubDir("/usr/share", guesses); - findEclipseInSubDir(System.getProperty("user.home", "."), guesses); - - for (String guess : guesses) { - try { - locations.add(EclipseLocation.create(guess)); - } catch (NotAnEclipseException e) { - problems.add(e); - } - } - } - - private static void findEclipseInSubDir(String dir, List guesses) { - File d = new File(dir); - if (!d.isDirectory()) return; - for (File f : d.listFiles()) { - if (f.isDirectory() && f.getName().toLowerCase().contains("eclipse")) { - File possible = new File(f, "eclipse"); - if (possible.exists()) guesses.add(possible.getAbsolutePath()); - } - } - } - - /** - * Scans /Applications for any folder named 'Eclipse' - */ - private static void findEclipseOnMac(List locations, List problems) { - 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. - try { - locations.add(EclipseLocation.create("/Applications")); - } catch (NotAnEclipseException e) { - problems.add(e); - } - } - if (dir.getName().toLowerCase().contains("eclipse")) { - if (new File(dir, "Eclipse.app").exists()) { - try { - locations.add(EclipseLocation.create(dir.toString())); - } catch (NotAnEclipseException e) { - problems.add(e); - } - } - } - } - } -} diff --git a/src/installer/lombok/installer/EclipseLocation.java b/src/installer/lombok/installer/EclipseLocation.java deleted file mode 100644 index c43c5042..00000000 --- a/src/installer/lombok/installer/EclipseLocation.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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; -import java.io.File; -import java.io.FileInputStream; -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.Collections; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -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; - - static { - String os = System.getProperty("os.name", ""); - - if ("Mac OS".equals(os)) OS_NEWLINE = "\r"; - else if (os.toLowerCase().contains("windows")) OS_NEWLINE = "\r\n"; - else OS_NEWLINE = "\n"; - } - - private final String name; - private final File eclipseIniPath; - 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. - */ - static final class NotAnEclipseException extends Exception { - private static final long serialVersionUID = 1L; - - public NotAnEclipseException(String message, Throwable cause) { - super(message, cause); - } - - /** - * 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); - } - } - - private EclipseLocation(String nameOfLocation, File pathToEclipseIni) throws NotAnEclipseException { - this.name = nameOfLocation; - this.eclipseIniPath = pathToEclipseIni; - try { - this.hasLombok = checkForLombok(eclipseIniPath); - } catch (IOException e) { - throw new NotAnEclipseException( - "I can't read the configuration file of the Eclipse installed at " + name + "\n" + - "You may need to run this installer with root privileges if you want to modify that Eclipse.", e); - } - } - - private static final List eclipseExecutableNames = Collections.unmodifiableList(Arrays.asList( - "eclipse.app", "eclipse.exe", "eclipse")); - - /** - * Create a new EclipseLocation by pointing at either the directory contain the Eclipse executable, or the executable itself, - * or an eclipse.ini file. - * - * @throws NotAnEclipseException - * If this isn't an Eclipse executable or a directory with an - * Eclipse executable. - */ - public static EclipseLocation create(String path) throws NotAnEclipseException { - if (path == null) throw new NullPointerException("path"); - File p = new File(path); - - if (!p.exists()) throw new NotAnEclipseException("File does not exist: " + path, null); - if (p.isDirectory()) { - for (String possibleExeName : eclipseExecutableNames) { - File f = new File(p, possibleExeName); - if (f.exists()) return findEclipseIniFromExe(f, 0); - } - - File f = new File(p, "eclipse.ini"); - if (f.exists()) return new EclipseLocation(getFilePath(p), f); - } - - if (p.isFile()) { - if (p.getName().equalsIgnoreCase("eclipse.ini")) { - return new EclipseLocation(getFilePath(p.getParentFile()), p); - } - - if (eclipseExecutableNames.contains(p.getName().toLowerCase())) { - return findEclipseIniFromExe(p, 0); - } - } - - throw new NotAnEclipseException("This path does not appear to contain an Eclipse installation: " + p, null); - } - - private static EclipseLocation findEclipseIniFromExe(File exePath, int loopCounter) throws NotAnEclipseException { - /* Try looking for eclipse.ini as sibling to the executable */ { - File ini = new File(exePath.getParentFile(), "eclipse.ini"); - if (ini.isFile()) return new EclipseLocation(getFilePath(exePath), ini); - } - - /* Try looking for Eclipse/app/Contents/MacOS/eclipse.ini as sibling to executable; this works on Mac OS X. */ { - File ini = new File(exePath.getParentFile(), "Eclipse.app/Contents/MacOS/eclipse.ini"); - if (ini.isFile()) return new EclipseLocation(getFilePath(exePath), ini); - } - - /* If executable is a soft link, follow it and retry. */ { - if (loopCounter < 50) { - try { - String oPath = exePath.getAbsolutePath(); - String nPath = exePath.getCanonicalPath(); - if (!oPath.equals(nPath)) try { - return findEclipseIniFromExe(new File(nPath), loopCounter + 1); - } catch (NotAnEclipseException ignore) { - // Unlinking didn't help find an eclipse, so continue. - } - } catch (IOException ignore) { /* okay, that didn't work, assume it isn't a soft link then. */ } - } - } - - /* If executable is a linux LSB-style path, then look in the usual places that package managers like apt-get use.*/ { - String path = exePath.getAbsolutePath(); - try { - path = exePath.getCanonicalPath(); - } catch (IOException ignore) { /* We'll stick with getAbsolutePath()'s result then. */ } - - if (path.equals("/usr/bin/eclipse") || path.equals("/bin/eclipse") || path.equals("/usr/local/bin/eclipse")) { - File ini = new File("/usr/lib/eclipse/eclipse.ini"); - if (ini.isFile()) return new EclipseLocation(path, ini); - ini = new File("/usr/local/lib/eclipse/eclipse.ini"); - if (ini.isFile()) return new EclipseLocation(path, ini); - ini = new File("/usr/local/etc/eclipse/eclipse.ini"); - if (ini.isFile()) return new EclipseLocation(path, ini); - ini = new File("/etc/eclipse.ini"); - if (ini.isFile()) return new EclipseLocation(path, ini); - } - } - - /* If we get this far, we lose. */ - throw new NotAnEclipseException("This path does not appear to contain an eclipse installation: " + exePath, null); - } - - public static String getFilePath(File p) { - try { - return p.getCanonicalPath(); - } catch (IOException e) { - String x = p.getAbsolutePath(); - return x == null ? p.getPath() : x; - } - } - - @Override public int hashCode() { - return eclipseIniPath.hashCode(); - } - - @Override public boolean equals(Object o) { - if (!(o instanceof EclipseLocation)) return false; - return ((EclipseLocation)o).eclipseIniPath.equals(eclipseIniPath); - } - - /** - * Returns the name of this location; generally the path to the eclipse executable. - * - * Executables: "eclipse.exe" (Windows), "Eclipse.app" (Mac OS X), "eclipse" (Linux and other unixes). - */ - String getName() { - return name; - } - - /** - * @return true if the Eclipse installation has been instrumented with lombok. - */ - boolean hasLombok() { - return hasLombok; - } - - private final Pattern JAVA_AGENT_LINE_MATCHER = Pattern.compile( - "^\\-javaagent\\:.*lombok.*\\.jar$", Pattern.CASE_INSENSITIVE); - - private final Pattern BOOTCLASSPATH_LINE_MATCHER = Pattern.compile( - "^\\-Xbootclasspath\\/a\\:(.*lombok.*\\.jar.*)$", Pattern.CASE_INSENSITIVE); - - private boolean checkForLombok(File iniFile) throws IOException { - if (!iniFile.exists()) return false; - FileInputStream fis = new FileInputStream(iniFile); - try { - BufferedReader br = new BufferedReader(new InputStreamReader(fis)); - String line; - while ((line = br.readLine()) != null) { - if (JAVA_AGENT_LINE_MATCHER.matcher(line.trim()).matches()) return true; - } - - return false; - } finally { - fis.close(); - } - } - - /** Thrown when uninstalling lombok fails. */ - static class UninstallException extends Exception { - private static final long serialVersionUID = 1L; - - public UninstallException(String message, Throwable cause) { - super(message, cause); - } - } - - /** Returns directories that may contain lombok.jar files that need to be deleted. */ - private List getUninstallDirs() { - List result = new ArrayList(); - File x = new File(name); - if (!x.isDirectory()) x = x.getParentFile(); - if (x.isDirectory()) result.add(x); - result.add(eclipseIniPath.getParentFile()); - return result; - } - - /** - * 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 : getUninstallDirs()) { - File lombokJar = new File(dir, "lombok.jar"); - if (lombokJar.exists()) { - if (!lombokJar.delete()) throw new UninstallException( - "Can't delete " + lombokJar.getAbsolutePath() + generateWriteErrorMessage(), null); - } - - /* legacy code - lombok at one point used to have a separate jar for the eclipse agent. - * Leave this code in to delete it for those upgrading from an old version. */ { - File agentJar = new File(dir, "lombok.eclipse.agent.jar"); - if (agentJar.exists()) { - if (!agentJar.delete()) throw new UninstallException( - "Can't delete " + agentJar.getAbsolutePath() + generateWriteErrorMessage(), null); - } - } - } - - StringBuilder newContents = new StringBuilder(); - if (eclipseIniPath.exists()) { - try { - FileInputStream fis = new FileInputStream(eclipseIniPath); - try { - BufferedReader br = new BufferedReader(new InputStreamReader(fis)); - String line; - while ((line = br.readLine()) != null) { - if (JAVA_AGENT_LINE_MATCHER.matcher(line).matches()) continue; - Matcher m = BOOTCLASSPATH_LINE_MATCHER.matcher(line); - if (m.matches()) { - StringBuilder elemBuilder = new StringBuilder(); - elemBuilder.append("-Xbootclasspath/a:"); - boolean first = true; - for (String elem : m.group(1).split(Pattern.quote(File.pathSeparator))) { - if (elem.toLowerCase().endsWith("lombok.jar")) continue; - /* legacy code -see previous comment that starts with 'legacy' */ { - if (elem.toLowerCase().endsWith("lombok.eclipse.agent.jar")) continue; - } - if (first) first = false; - else elemBuilder.append(File.pathSeparator); - elemBuilder.append(elem); - } - if (!first) newContents.append(elemBuilder.toString()).append(OS_NEWLINE); - continue; - } - - newContents.append(line).append(OS_NEWLINE); - } - - } finally { - fis.close(); - } - - FileOutputStream fos = new FileOutputStream(eclipseIniPath); - try { - fos.write(newContents.toString().getBytes()); - } finally { - fos.close(); - } - } catch (IOException e) { - throw new UninstallException("Cannot uninstall lombok from " + name + generateWriteErrorMessage(), e); - } - } - } - - /** Thrown when installing lombok fails. */ - static class InstallException extends Exception { - private static final long serialVersionUID = 1L; - - public InstallException(String message, Throwable cause) { - super(message, cause); - } - } - - private static String generateWriteErrorMessage() { - String osSpecificError; - - switch (EclipseFinder.getOS()) { - default: - case MAC_OS_X: - case UNIX: - osSpecificError = ":\nStart terminal, go to the directory with lombok.jar, and run: sudo java -jar lombok.jar"; - break; - case WINDOWS: - osSpecificError = ":\nStart a new cmd (dos box) with admin privileges, go to the directory with lombok.jar, and run: java -jar lombok.jar"; - break; - } - - return ", probably because this installer does not have the access rights.\n" + - "Try re-running the installer with administrative privileges" + osSpecificError; - } - - /** - * 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 { - // For whatever reason, relative paths in your eclipse.ini file don't work on linux, but only for -javaagent. - // If someone knows how to fix this, please do so, as this current hack solution (putting the absolute path - // to the jar files in your eclipse.ini) means you can't move your eclipse around on linux without lombok - // breaking it. NB: rerunning lombok.jar installer and hitting 'update' will fix it if you do that. - boolean fullPathRequired = EclipseFinder.getOS() == EclipseFinder.OS.UNIX; - - boolean installSucceeded = false; - StringBuilder newContents = new StringBuilder(); - //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(eclipseIniPath.getParentFile(), "lombok.jar"); - - File ourJar = EclipseFinder.findOurJar(); - byte[] b = new byte[524288]; - boolean readSucceeded = true; - try { - FileOutputStream out = new FileOutputStream(lombokJar); - try { - readSucceeded = false; - InputStream in = new FileInputStream(ourJar); - try { - while (true) { - int r = in.read(b); - if (r == -1) break; - if (r > 0) readSucceeded = true; - out.write(b, 0, r); - } - } finally { - in.close(); - } - } finally { - out.close(); - } - } catch (IOException e) { - try { - lombokJar.delete(); - } catch (Throwable ignore) { /* Nothing we can do about that. */ } - if (!readSucceeded) throw new InstallException( - "I can't read my own jar file. I think you've found a bug in this installer!\nI suggest you restart it " + - "and use the 'what do I do' link, to manually install lombok. Also, tell us about this at:\n" + - "http://groups.google.com/group/project-lombok - Thanks!", e); - throw new InstallException("I can't write to your Eclipse directory at " + name + generateWriteErrorMessage(), e); - } - - /* legacy - delete lombok.eclipse.agent.jar if its there, which lombok no longer uses. */ { - new File(lombokJar.getParentFile(), "lombok.eclipse.agent.jar").delete(); - } - - try { - FileInputStream fis = new FileInputStream(eclipseIniPath); - try { - BufferedReader br = new BufferedReader(new InputStreamReader(fis)); - String line; - while ((line = br.readLine()) != null) { - if (JAVA_AGENT_LINE_MATCHER.matcher(line).matches()) continue; - Matcher m = BOOTCLASSPATH_LINE_MATCHER.matcher(line); - if (m.matches()) { - StringBuilder elemBuilder = new StringBuilder(); - elemBuilder.append("-Xbootclasspath/a:"); - boolean first = true; - for (String elem : m.group(1).split(Pattern.quote(File.pathSeparator))) { - if (elem.toLowerCase().endsWith("lombok.jar")) continue; - /* legacy code -see previous comment that starts with 'legacy' */ { - if (elem.toLowerCase().endsWith("lombok.eclipse.agent.jar")) continue; - } - if (first) first = false; - else elemBuilder.append(File.pathSeparator); - elemBuilder.append(elem); - } - if (!first) newContents.append(elemBuilder.toString()).append(OS_NEWLINE); - continue; - } - - newContents.append(line).append(OS_NEWLINE); - } - - } finally { - fis.close(); - } - - String fullPathToLombok = fullPathRequired ? (lombokJar.getParentFile().getCanonicalPath() + File.separator) : ""; - - newContents.append(String.format( - "-javaagent:%slombok.jar", fullPathToLombok)).append(OS_NEWLINE); - newContents.append(String.format( - "-Xbootclasspath/a:%slombok.jar", fullPathToLombok)).append(OS_NEWLINE); - - FileOutputStream fos = new FileOutputStream(eclipseIniPath); - try { - fos.write(newContents.toString().getBytes()); - } finally { - fos.close(); - } - installSucceeded = true; - } catch (IOException e) { - throw new InstallException("Cannot install lombok at " + name + generateWriteErrorMessage(), e); - } finally { - if (!installSucceeded) try { - lombokJar.delete(); - } catch (Throwable ignore) {} - } - - if (!installSucceeded) { - throw new InstallException("I can't find the eclipse.ini file. Is this a real Eclipse installation?", null); - } - } -} diff --git a/src/installer/lombok/installer/IdeFinder.java b/src/installer/lombok/installer/IdeFinder.java new file mode 100644 index 00000000..165d0768 --- /dev/null +++ b/src/installer/lombok/installer/IdeFinder.java @@ -0,0 +1,144 @@ +/* + * 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.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import lombok.core.Version; + +/** + * Implement and provide this class to add auto-finding a certain brand of IDEs to the lombok installer. + */ +public abstract class IdeFinder { + private static final AtomicBoolean windowsDriveInfoLibLoaded = new AtomicBoolean(false); + + private static void loadWindowsDriveInfoLib() throws IOException { + if (!windowsDriveInfoLibLoaded.compareAndSet(false, true)) return; + + final String prefix = "lombok-" + Version.getVersion() + "-"; + + File temp = File.createTempFile("lombok", ".mark"); + File dll1 = new File(temp.getParentFile(), prefix + "WindowsDriveInfo-i386.dll"); + File dll2 = new File(temp.getParentFile(), prefix + "WindowsDriveInfo-x86_64.dll"); + temp.delete(); + dll1.deleteOnExit(); + dll2.deleteOnExit(); + try { + if (unpackDLL("WindowsDriveInfo-i386.dll", dll1)) { + System.load(dll1.getAbsolutePath()); + return; + } + } catch (Throwable ignore) {} + + try { + if (unpackDLL("WindowsDriveInfo-x86_64.dll", dll2)) { + System.load(dll2.getAbsolutePath()); + } + } catch (Throwable ignore) {} + } + + private static boolean unpackDLL(String dllName, File target) throws IOException { + InputStream in = IdeFinder.class.getResourceAsStream(dllName); + try { + try { + FileOutputStream out = new FileOutputStream(target); + try { + byte[] b = new byte[32000]; + while (true) { + int r = in.read(b); + if (r == -1) break; + out.write(b, 0, r); + } + } finally { + out.close(); + } + } catch (IOException e) { + //Fall through - if there is a file named lombok-WindowsDriveInfo-arch.dll, we'll try it. + return target.exists() && target.canRead(); + } + } finally { + in.close(); + } + + return true; + } + + /** + * Returns all drive letters on windows that represent fixed disks. + * + * Floppy drives, optical drives, USB sticks, and network drives should all be excluded. + * + * @return A List of drive letters, such as ["C", "D", "X"]. + */ + public static List getDrivesOnWindows() throws Throwable { + loadWindowsDriveInfoLib(); + + List drives = new ArrayList(); + + WindowsDriveInfo info = new WindowsDriveInfo(); + for (String drive : info.getLogicalDrives()) { + if (info.isFixedDisk(drive)) drives.add(drive); + } + + return drives; + } + + public static enum OS { + MAC_OS_X("\n"), WINDOWS("\r\n"), UNIX("\n"); + + private final String lineEnding; + + OS(String lineEnding) { + this.lineEnding = lineEnding; + } + + public String getLineEnding() { + return lineEnding; + } + } + + public static OS getOS() { + String prop = System.getProperty("os.name", "").toLowerCase(); + if (prop.matches("^.*\\bmac\\b.*$")) return OS.MAC_OS_X; + if (prop.matches("^.*\\bdarwin\\b.*$")) return OS.MAC_OS_X; + if (prop.matches("^.*\\bwin(dows)\\b.*$")) return OS.WINDOWS; + + return OS.UNIX; + } + + /** + * Look for installations of your IDE in the usual places. + * + * @param locations Add to this list any valid locations that you found. + * @param problems + * Add to this list any locations that look like installations, + * but have problems that prevent you from installing/uninstalling from them. DONT add to this list + * any common locations that have no installation at all - only add near misses. + */ + public abstract void findIdes(List locations, List problems); +} diff --git a/src/installer/lombok/installer/IdeLocation.java b/src/installer/lombok/installer/IdeLocation.java new file mode 100644 index 00000000..e23a0033 --- /dev/null +++ b/src/installer/lombok/installer/IdeLocation.java @@ -0,0 +1,74 @@ +/* + * 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.File; +import java.io.IOException; +import java.net.URL; + +import lombok.patcher.inject.LiveInjector; + +/** + * Represents a location that contains an IDE. + */ +public abstract class IdeLocation { + /** Toggling the 'selected' checkbox in the GUI is tracked via this boolean */ + boolean selected = true; + + public abstract String install() throws InstallException; + public abstract void uninstall() throws UninstallException; + public abstract String getName(); + public abstract boolean hasLombok(); + public abstract URL getIdeIcon(); + + /** + * 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. + */ + public static File findOurJar() { + return new File(LiveInjector.findPathJar(IdeFinder.class)); + } + + /** + * Returns a full path to the provided file. + * Returns the canonical path, unless that is not available, in which cae it returns the absolute path. + */ + public static String canonical(File p) { + try { + return p.getCanonicalPath(); + } catch (IOException e) { + String x = p.getAbsolutePath(); + return x == null ? p.getPath() : x; + } + } + + private static final String LEGAL_PATH_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/"; + public static String escapePath(String path) { + StringBuilder out = new StringBuilder(); + + for (char c : path.toCharArray()) { + if (LEGAL_PATH_CHARS.indexOf(c) == -1) out.append('\\'); + out.append(c); + } + return out.toString(); + } +} diff --git a/src/installer/lombok/installer/IdeLocationProvider.java b/src/installer/lombok/installer/IdeLocationProvider.java new file mode 100644 index 00000000..e98e199d --- /dev/null +++ b/src/installer/lombok/installer/IdeLocationProvider.java @@ -0,0 +1,40 @@ +/* + * 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.util.regex.Pattern; + +import lombok.installer.IdeFinder.OS; + +public interface IdeLocationProvider { + /** + * @throws CorruptedIdeLocationException + * Only throw this exception if the location seems like a proper installation except there's something wrong with it. + * Do not throw it (just return {@code null}) if there's nothing there or it looks absolutely nothing like your IDE. + */ + public abstract IdeLocation create(String path) throws CorruptedIdeLocationException; + + /** + * Return the usual name of the IDE executable or other obvious marker of an IDE installation on the provided platform. + */ + public abstract Pattern getLocationSelectors(OS os); +} diff --git a/src/installer/lombok/installer/InstallException.java b/src/installer/lombok/installer/InstallException.java new file mode 100644 index 00000000..41879604 --- /dev/null +++ b/src/installer/lombok/installer/InstallException.java @@ -0,0 +1,10 @@ +package lombok.installer; + +/** + * Thrown when installation of lombok into an IDE fails. + */ +public class InstallException extends Exception { + public InstallException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/installer/lombok/installer/Installer.java b/src/installer/lombok/installer/Installer.java index d4b31251..9ed9e93a 100644 --- a/src/installer/lombok/installer/Installer.java +++ b/src/installer/lombok/installer/Installer.java @@ -21,55 +21,23 @@ */ 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.Toolkit; -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.Arrays; import java.util.Collections; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; -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.Lombok; import lombok.core.LombokApp; +import lombok.core.SpiLoadUtil; import lombok.core.Version; -import lombok.installer.EclipseFinder.OS; -import lombok.installer.EclipseLocation.InstallException; -import lombok.installer.EclipseLocation.NotAnEclipseException; -import lombok.installer.EclipseLocation.UninstallException; +import lombok.installer.IdeFinder.OS; import org.mangosdk.spi.ProviderFor; @@ -77,6 +45,7 @@ import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; import com.zwitserloot.cmdreader.InvalidCommandLineException; import com.zwitserloot.cmdreader.Parameterized; +import com.zwitserloot.cmdreader.Sequential; import com.zwitserloot.cmdreader.Shorthand; /** @@ -86,24 +55,49 @@ import com.zwitserloot.cmdreader.Shorthand; * and looks in some common places on Mac OS X, Linux and Windows. */ public class Installer { - private static final URI ABOUT_LOMBOK_URL = URI.create("http://projectlombok.org"); - private static final AtomicReference exitMarker = new AtomicReference(); + static final URI ABOUT_LOMBOK_URL = URI.create("http://projectlombok.org"); + static final List locationProviders; - private JFrame appWindow; + static { + List list = new ArrayList(); + try { + for (IdeLocationProvider provider : SpiLoadUtil.findServices(IdeLocationProvider.class)) { + list.add(provider); + } + } catch (IOException e) { + throw Lombok.sneakyThrow(e); + } + locationProviders = Collections.unmodifiableList(list); + } - private JComponent loadingExpl; - - private Component javacArea; - private Component eclipseArea; - private Component uninstallArea; - private Component howIWorkArea; - - private Box uninstallBox; - private List toUninstall; + static List getIdeExecutableNames() { + OS os = IdeFinder.getOS(); + List list = new ArrayList(); + for (IdeLocationProvider provider : locationProviders) { + Pattern p = provider.getLocationSelectors(os); + if (p != null) list.add(p); + } + return list; + } + + static IdeLocation tryAllProviders(String location) throws CorruptedIdeLocationException { + for (IdeLocationProvider provider : locationProviders) { + IdeLocation loc = provider.create(location); + if (loc != null) return loc; + } + + return null; + } - private JHyperLink uninstallButton; - private JLabel uninstallPlaceholder; - private JButton installButton; + static void autoDiscover(List locations, List problems) { + try { + for (IdeFinder finder : SpiLoadUtil.findServices(IdeFinder.class)) { + finder.findIdes(locations, problems); + } + } catch (IOException e) { + throw Lombok.sneakyThrow(e); + } + } @ProviderFor(LombokApp.class) public static class GraphicalInstallerApp implements LombokApp { @@ -163,7 +157,7 @@ public class Installer { } private static int guiInstaller() { - if (EclipseFinder.getOS() == OS.MAC_OS_X) { + if (IdeFinder.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"); } @@ -176,22 +170,22 @@ public class Installer { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ignore) {} - new Installer().show(); + new InstallerGUI().show(); } catch (HeadlessException e) { printHeadlessInfo(); } } }); - synchronized (exitMarker) { - while (!Thread.interrupted() && exitMarker.get() == null) { + synchronized (InstallerGUI.exitMarker) { + while (!Thread.interrupted() && InstallerGUI.exitMarker.get() == null) { try { - exitMarker.wait(); + InstallerGUI.exitMarker.wait(); } catch (InterruptedException e) { return 1; } } - Integer errCode = exitMarker.get(); + Integer errCode = InstallerGUI.exitMarker.get(); return errCode == null ? 1 : errCode; } } catch (HeadlessException e) { @@ -201,12 +195,12 @@ public class Installer { } private static class CmdArgs { - @Shorthand("e") - @Description("Specify a path to an eclipse location to install/uninstall. Use 'auto' to apply to all automatically discoverable eclipse installations.") + @Description("Specify paths to a location to install/uninstall. Use 'auto' to apply to all automatically discoverable installations.") @Parameterized - List eclipse = new ArrayList(); + @Sequential + List path = new ArrayList(); - @Shorthand({"?", "h"}) + @Shorthand({"h", "?"}) @Description("Shows this help text") boolean help; } @@ -228,30 +222,33 @@ public class Installer { return 0; } - if (args.eclipse.isEmpty()) { + if (args.path.isEmpty()) { System.err.println("ERROR: Nothing to do!"); System.err.println("--------------------------"); System.err.println(generateCliHelp(uninstall, reader)); return 1; } - final List locations = new ArrayList(); - final List problems = new ArrayList(); + final List locations = new ArrayList(); + final List problems = new ArrayList(); + + if (args.path.contains("auto")) autoDiscover(locations, problems); - for (String rawPath : args.eclipse) { - if (rawPath.equals("auto")) { - EclipseFinder.findEclipses(locations, problems); - } else { + for (String rawPath : args.path) { + if (!rawPath.equals("auto")) { try { - locations.add(EclipseLocation.create(rawPath)); - } catch (NotAnEclipseException e) { + IdeLocation loc = tryAllProviders(rawPath); + if (loc != null) locations.add(loc); + else problems.add(new CorruptedIdeLocationException("Can't find any IDE at: " + rawPath, null, null)); + locations.add(tryAllProviders(rawPath)); + } catch (CorruptedIdeLocationException e) { problems.add(e); } } } int validLocations = locations.size(); - for (EclipseLocation loc : locations) { + for (IdeLocation loc : locations) { try { if (uninstall) { loc.uninstall(); @@ -270,7 +267,7 @@ public class Installer { } } - for (NotAnEclipseException problem : problems) { + for (CorruptedIdeLocationException problem : problems) { System.err.println("WARNING: " + problem.getMessage()); } @@ -301,695 +298,4 @@ public class Installer { " java -cp lombok.jar MyCode.java\n", Version.getVersion(), ABOUT_LOMBOK_URL); } - - /** - * Creates a new installer that starts out invisible. - * Call the {@link #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); - appWindow.setIconImage(Toolkit.getDefaultToolkit().getImage(Installer.class.getResource("lombokIcon.png"))); - - 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(String.format(HOW_I_WORK_EXPLANATION, File.pathSeparator)), 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; - constraints.insets = new Insets(8, 0, 0, 16); - - container.add(new JLabel(JAVAC_TITLE), constraints); - - constraints.gridy = 1; - constraints.weightx = 1.0; - constraints.fill = GridBagConstraints.HORIZONTAL; - container.add(new JLabel(JAVAC_EXPLANATION), constraints); - - JLabel example = new JLabel(JAVAC_EXAMPLE); - - constraints.gridy = 2; - container.add(example, constraints); - return container; - } - - private Component buildEclipseArea() { - JPanel container = new JPanel(); - - container.setLayout(new GridBagLayout()); - GridBagConstraints constraints = new GridBagConstraints(); - constraints.anchor = GridBagConstraints.WEST; - - constraints.insets = new Insets(8, 0, 0, 16); - container.add(new JLabel(ECLIPSE_TITLE), constraints); - - constraints.gridy = 1; - 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); - eclipsesListScroll.getViewport().setBackground(Color.WHITE); - container.add(eclipsesListScroll, constraints); - - Thread findEclipsesThread = new Thread() { - @Override public void run() { - try { - final List locations = new ArrayList(); - final List problems = new ArrayList(); - EclipseFinder.findEclipses(locations, problems); - - 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 (locations.size() + problems.size() == 0) { - 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 exeName = 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 fileName) { - if (exeName.equalsIgnoreCase(fileName)) return true; - if (new File(dir, fileName).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(exeName)) return true; - if (f.getName().equalsIgnoreCase("eclipse.ini")) 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(EclipseLocation.create(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; - 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(); - } - }); - - constraints.gridy = 6; - uninstallButton = new JHyperLink("Uninstall lombok from selected Eclipse installations."); - uninstallPlaceholder = new JLabel(" "); - 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); - uninstallPlaceholder.setVisible(false); - container.add(uninstallPlaceholder, constraints); - - - 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.getName()); - 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 Proble