/* * NetBeans.java * * Created on December 15, 2005, 8:48 PM * */ package launch; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Provides simple access to a locally installed NetBeans IDE. * It's intended to enable one to open a local file, launching * NetBeans if needed. Typical use of this class is: *
 * NetBeans nb = new NetBeans();
 * nb.initialize();
 * if (!nb.isInstalled()) {
 *     // point the user at the download page
 * }
 * else {
 *     File file = new File("C:\MyProject\src\pkg\MyFile.java");
 *     try {
 *         nb.openFile(file);
 *     }
 *     catch (NetBeans.Failure e) {
 *         // report the failure
 *     }
 * }
 * 
* * @author Hans Muller (Hans.Muller@Sun.COM) */ public class NetBeans { private final PropertyChangeSupport pcs; private boolean isInitialized = false; private boolean installed = false; private String displayName = null; private File installLocation = null; private ProcessBuilder openFileCommand = null; public NetBeans() { pcs = new PropertyChangeSupport(this); } public void addPropertyChangeListener(PropertyChangeListener listener) { if (listener != null) { pcs.addPropertyChangeListener(listener); } } public void removePropertyChangeListener(PropertyChangeListener listener) { if (listener != null) { pcs.removePropertyChangeListener(listener); } } public PropertyChangeListener[] getPropertyChangeListeners() { return pcs.getPropertyChangeListeners(); } private void firePCS(String propertyName, Object oldValue, Object newValue) { if (!(oldValue != null && newValue != null && oldValue.equals(newValue))) { pcs.firePropertyChange(propertyName, oldValue, newValue); } } /** * @return true if an installed copy of NetBeans exists. * @see initialize */ public boolean isInstalled() { return installed; } private void setInstalled(boolean installed) { boolean oldValue = this.installed; this.installed = installed; firePCS("installed", oldValue, this.installed); } /** * @return a String that identifies the (installed) version of NetBeans * @see initialize */ public String getDisplayName() { return displayName; } private void setDisplayName(String displayName) { String oldValue = this.displayName; this.displayName = displayName; firePCS("displayName", oldValue, this.displayName); } /** * @return the Directory where NetBeans is installed * @see initialize */ public File getInstallLocation() { return installLocation; } private void setInstallLocation(File installLocation) { File oldValue = this.installLocation; this.installLocation = installLocation; firePCS("installLocation", oldValue, this.installLocation); } /** * Returns a ProcessBuilder that launches NetBeans with the * "-open" option. The NetBeans process will pull the name * of the file to be loaded from an environment variable * called "NBFILE". For example the Unix version of the * command for launching NetBeans is: *
     * sh -c './netbeans -open $NBFILE'
     * 
* * @return a ProcessBuilder that will launch NetBeans * @see initialize * @see openFile */ public ProcessBuilder getOpenFileCommand() { return openFileCommand; } private void setOpenFileCommand(ProcessBuilder openFileCommand) { ProcessBuilder oldValue = this.openFileCommand; this.openFileCommand = openFileCommand; firePCS("openFileCommand", oldValue, this.openFileCommand); } /** * Launch a NetBeans subprocess that causes the IDE to load * the specified file. * * @see initialize * @see getOpenFileCommand */ public void openFile(File file) throws Failure { ProcessBuilder pb = getOpenFileCommand(); pb.environment().put("NBFILE", file.toString()); pb.redirectErrorStream(true); try { Process p = pb.start(); BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream())); String line; while ((line = stdout.readLine()) != null) { // [TBD send output to the GUI] System.out.println(line); } } catch (IOException e) { throw new Failure(e); } } public static class Failure extends Exception { public Failure(Exception e) { super(e); } } /** * Given a (possibly valid) install directory, initialize * the installed, displayName, installLocation, and * openFileCommand properties. */ public boolean initializeFrom(String dirName) { String osName = System.getProperty("os.name", ""); File binDir = new File(dirName, "bin"); if (osName.contains("Windows")) { if (new File(binDir, "netbeans.exe").exists()) { setInstalled(true); setDisplayName("NetBeans"); // could do better setInstallLocation(new File(dirName)); initializeWindowsOpenFileCommand(); return true; } } else { if (new File(binDir, "netbeans").exists()) { setInstalled(true); setDisplayName("NetBeans"); // could do better setInstallLocation(new File(dirName)); initializeUnixOpenFileCommand(); return true; } } return false; } /** * Initialize the installed, displayName, installLocation, and * openFileCommand properties. * * Case analysis based on the value of the "os.name" system property. * There's no definitive guide for possible values however some collections * exist, e.g.: http://lopica.sourceforge.net/os.html and * http://tolstoy.com/samizdat/sysprops-windows.html */ public boolean initialize() { String osName = System.getProperty("os.name", ""); if (osName.contains("Windows")) { return initializeWindows(); } else if (osName.contains("Mac")) { return initializeUnix(); } else { return initializeUnix(); } } private void initializeUnixOpenFileCommand() { File dir = getInstallLocation(); ProcessBuilder ofc = new ProcessBuilder("sh", "-c","'./netbeans -open $NBFILE'"); ofc.directory(new File(dir, "bin")); setOpenFileCommand(ofc); } /* Hack: assume user has "[install-location]/bin/netbeans in their * unix PATH and the Bourne shell (sh) and the which command * are available and do what's expected. */ private boolean initializeUnixTry() throws IOException { ProcessBuilder pb = new ProcessBuilder("which", "netbeans"); pb.redirectErrorStream(true); Process p = pb.start(); BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream())); String nbPathName = stdout.readLine(); p.destroy(); if ((nbPathName != null) && (nbPathName.endsWith("/netbeans"))) { File nbPath = new File(nbPathName); String nbLocationName = nbPath.getParent(); if (nbLocationName.contains("netbeans")) { File nbLocation = new File(nbLocationName); setInstalled(true); setDisplayName(nbLocation.getName()); setInstallLocation(nbLocation); initializeUnixOpenFileCommand(); return true; } } return false; } private boolean initializeUnix() { try { return initializeWindowsTry(); } catch (IOException e) {} return false; } /* Create the ProcessBuilder for a Windows NetBeans command. * * This is a bit tricky to do. If the full pathname is used * instead of just "netbeans.exe", the command shell gets * hopelessly confused about spaces in pathnames. Quoting the * entire "C:\[...]\bin\netbeans.exe" command doesn't help. * Inserting the quote after the drive indicator, like * C:\"[...]\bin\netbeans.exe" does work but that's a * pain. Setting the working directory to "C:\[...]\bin" avoids * the problem and hopefully there's no reason to care what the * working directory actually is on Windows. */ private void initializeWindowsOpenFileCommand() { File dir = getInstallLocation(); ProcessBuilder ofc = new ProcessBuilder("cmd", "/c", "netbeans.exe", "-open", "\"%NBFILE%\""); ofc.directory(new File(dir, "bin")); setOpenFileCommand(ofc); } /* getRegValue("DisplayName REG_SZ NetBeans IDE 5.0 Beta 2") => * "NetBeans IDE 5.0 Beta 2". */ private String windowsRegValue(String line) { int i = line.indexOf("REG_SZ"); if (i != -1) { return line.substring(i + "REG_SZ".length()).trim(); } else { return null; } } /* * Find the NetBeans directories on Windows by looking for * the "Uninstall" registry entries left behind by the standard installer. * We use this query to dump all of the uninstall entries, * and then extract the ones whose DisplayName contains "NetBeans". * Windows command is: * reg query hklm\Software\Microsoft\Windows\CurrentVersion\Uninstall /s * * The NetBeans uninstall records look like this (there's a blank * line between records): * * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\274c5407c4fa26908310cb5c1c[...] * DisplayName REG_SZ NetBeans IDE 5.0 Beta 2 * DisplayIcon REG_SZ C:\Program Files\netbeans-5.0beta2\bin\netbeans.exe * InstallLocation REG_SZ C:\Program Files\netbeans-5.0beta2 * * The 6 characters that follow the 26 character ID, noted "[...]" * above, are the NetBeans version number. For example: * 410000 - version 4.1 * 5000b2 - version 5.0 beta2 * */ private boolean initializeWindowsTry() throws IOException { /* Start a subprocess to execute the "reg query" command */ String regPath = "hklm\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; ProcessBuilder pb = new ProcessBuilder("reg", "query", regPath, "/s"); pb.redirectErrorStream(true); Process p = pb.start(); BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream())); /* Create a nbRegEntry for each NetBeans entry */ final String nbid = "274c5407c4fa26908310cb5c1c"; String line; NBRegEntry nbRegEntry = null; List nbRegEntries = new ArrayList(); while ((line = stdout.readLine()) != null) { line = line.trim(); if (line.startsWith("HKEY_LOCAL_MACHINE")) { int i = line.indexOf(nbid); if ((i != -1) && (line.length() >= (i + nbid.length() + 6))) { int n0 = i + nbid.length(); String versionNumber = line.substring(n0, n0 + 4); String versionType = line.substring(n0 + 5, n0 + 6); nbRegEntry = new NBRegEntry(versionNumber, versionType); } } else if (line.length() == 0) { if ((nbRegEntry != null) && (nbRegEntry.installLocation != null)) { nbRegEntries.add(nbRegEntry); nbRegEntry = null; } } if (nbRegEntry != null) { if (line.startsWith("DisplayName")) { nbRegEntry.displayName = windowsRegValue(line); } else if (line.startsWith("InstallLocation")) { nbRegEntry.installLocation = windowsRegValue(line); } } } /* All of installed copies of NetBeans we've found are in * nbRegEntries. We'll pick the newest with a "bin/netbeans.exe" * app and initialize this with that. */ Collections.sort(nbRegEntries); for(NBRegEntry entry : nbRegEntries) { File nbExe = new File(entry.installLocation, "bin\\netbeans.exe"); if (nbExe.exists()) { setInstalled(true); setDisplayName(entry.displayName); setInstallLocation(new File(entry.installLocation)); initializeWindowsOpenFileCommand(); return true; } } return false; } private boolean initializeWindows() { try { return initializeWindowsTry(); } catch (IOException e) {} return false; } private static class NBRegEntry implements Comparable { public String displayName = ""; public String installLocation; public final String versionNumber; public final String versionType; public NBRegEntry(String versionNumber, String versionType) { this.versionNumber = versionNumber; this.versionType = versionType; } public int compareTo(NBRegEntry o) { int rv = o.versionNumber.compareTo(versionNumber); return (rv == 0) ? o.versionType.compareTo(versionType) : rv; } public String toString() { return displayName + " [" + versionNumber + " " + versionType + "]"; } } }