blob: 0391c9b0d85ca629ad35400a5cde481afce79f12 [file] [log] [blame]
/*
* Copyright (c) 2015, 2016 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.util;
import org.eclipse.oomph.internal.util.UtilPlugin;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.util.NLS;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @author Eike Stepper
*/
public abstract class OS
{
public static final OS INSTANCE = create();
public static final List<OS> INSTANCES = createAll();
private final String osgiOS;
private final String osgiWS;
private final String osgiArch;
protected OS(String osgiOS, String osgiWS, String osgiArch)
{
this.osgiOS = osgiOS;
this.osgiWS = osgiWS;
this.osgiArch = osgiArch;
}
public String getOsgiOS()
{
return osgiOS;
}
public String getOsgiWS()
{
return osgiWS;
}
public String getOsgiArch()
{
return osgiArch;
}
public boolean isWin()
{
return false;
}
public boolean isMac()
{
return false;
}
public boolean isLinux()
{
return false;
}
public boolean isCurrent()
{
return Platform.getOS().equals(osgiOS) && Platform.getWS().equals(osgiWS) && Platform.getOSArch().equals(osgiArch);
}
public boolean isCurrentOS()
{
return Platform.getOS().equals(osgiOS);
}
public boolean is32BitAvailable()
{
return true;
}
public int getBitness()
{
String osgiArch = getOsgiArch();
if (Platform.ARCH_X86.equals(osgiArch))
{
return 32;
}
return 64;
}
public boolean isLineEndingConversionNeeded()
{
return false;
}
protected String getEncoding()
{
return "ISO-8859-1"; //$NON-NLS-1$
}
protected abstract String[] getOpenCommands();
public boolean openSystemBrowser(String url)
{
try
{
for (String command : getOpenCommands())
{
if (openSystemBrowser(command, url))
{
return true;
}
}
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
try
{
// java.awt.Desktop was introduced with Java 1.6!
Class<?> desktopClass = CommonPlugin.loadClass(UtilPlugin.INSTANCE.getSymbolicName(), "java.awt.Desktop"); //$NON-NLS-1$
Method getDesktopMethod = ReflectUtil.getMethod(desktopClass, "getDesktop"); //$NON-NLS-1$
Method browseMethod = ReflectUtil.getMethod(desktopClass, "browse", URI.class); //$NON-NLS-1$
Object desktop = getDesktopMethod.invoke(null);
browseMethod.invoke(desktop, new URI(url));
return true;
}
catch (Throwable ex)
{
UtilPlugin.INSTANCE.log(ex, IStatus.WARNING);
}
return false;
}
private boolean openSystemBrowser(String command, String url)
{
if (IOUtil.getFromPath(command) != null)
{
String[] cmdarray = { command, url };
try
{
Process process = Runtime.getRuntime().exec(cmdarray);
if (process != null)
{
// Don't check whether the process is still running; some commands just delegate to others and terminate.
return true;
}
}
catch (IOException ex)
{
//$FALL-THROUGH$
}
}
return false;
}
public Process execute(List<String> command, boolean terminal) throws Exception
{
ProcessBuilder processBuilder = new ProcessBuilder(command);
try
{
Map<String, String> environment = processBuilder.environment();
environment.remove("SWT_GTK3"); //$NON-NLS-1$
}
catch (UnsupportedOperationException ex)
{
// Ignore.
}
Process process = processBuilder.start();
process.getInputStream().close();
process.getOutputStream().close();
process.getErrorStream().close();
return process;
}
protected String getCommandLine(List<String> command)
{
StringBuilder builder = new StringBuilder();
for (String token : command)
{
if (builder.length() != 0)
{
builder.append(' ');
}
builder.append('"');
builder.append(token);
builder.append('"');
}
return builder.toString();
}
public String getRelativeProductFolder(String folderName)
{
if (StringUtil.isEmpty(folderName))
{
return "eclipse"; //$NON-NLS-1$
}
return folderName;
}
public abstract String getRelativeExecutableFolder();
public String getExecutableName(String launcherName)
{
return launcherName;
}
public static String getCurrentLauncher(boolean console)
{
try
{
String launcher = PropertiesUtil.getProperty("eclipse.launcher"); //$NON-NLS-1$
if (launcher != null)
{
File launcherFile = new File(launcher);
if (launcherFile.isFile())
{
File result = IOUtil.getCanonicalFile(launcherFile);
if (INSTANCE.isWin())
{
// If we don't need a console, but actually ended up here with eclipsec.exe, we don't try to find the product-specifically named executable.
if (console)
{
File parentFolder = result.getParentFile();
if (parentFolder != null)
{
File consoleLauncher = new File(parentFolder, "eclipsec.exe"); //$NON-NLS-1$
if (consoleLauncher.isFile())
{
return consoleLauncher.getPath();
}
}
}
}
return result.getPath();
}
}
}
catch (Throwable ex)
{
//$FALL-THROUGH$
}
return null;
}
public abstract String getGitPrefix();
public abstract String getJREsRoot();
public abstract OS getForBitness(int bitness);
public static void close(Closeable closeable)
{
if (closeable != null)
{
try
{
closeable.close();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
}
private static OS create()
{
String os = Platform.getOS();
String ws = Platform.getWS();
String arch = Platform.getOSArch();
if (Platform.OS_WIN32.equals(os))
{
if (Platform.ARCH_X86_64.equals(arch))
{
return new Win64(ws);
}
return new Win32(ws, arch);
}
if (Platform.OS_MACOSX.equals(os))
{
return new Mac(ws, arch);
}
if (Platform.OS_LINUX.equals(os))
{
return new Linux(ws, arch);
}
throw new IllegalStateException(NLS.bind(Messages.OS_OS_NotSupported_exception, os));
}
private static List<OS> createAll()
{
List<OS> result = new ArrayList<OS>();
result.add(Win64.INSTANCE);
result.add(Win32.INSTANCE);
result.add(new Mac(Platform.WS_COCOA, Platform.ARCH_X86_64));
result.add(new Linux(Platform.WS_GTK, Platform.ARCH_X86_64));
result.add(new Linux(Platform.WS_GTK, Platform.ARCH_X86));
return Collections.unmodifiableList(result);
}
@Override
public String toString()
{
return getOsgiOS() + " " + getOsgiWS() + " " + getOsgiArch(); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* @author Eike Stepper
*/
private static class Win32 extends OS
{
private static final Win32 INSTANCE = new Win32(Platform.WS_WIN32, Platform.ARCH_X86);
// Don't use "explorer" as it forks another process and returns a confusing exit value.
private static final String[] OPEN_COMMANDS = {};
public Win32(String osgiWS, String osgiArch)
{
super(Platform.OS_WIN32, osgiWS, osgiArch);
}
@Override
public boolean isWin()
{
return true;
}
@Override
public boolean isLineEndingConversionNeeded()
{
return true;
}
@Override
public String getRelativeExecutableFolder()
{
return ""; //$NON-NLS-1$
}
@Override
public String getExecutableName(String launcherName)
{
return super.getExecutableName(launcherName) + ".exe"; //$NON-NLS-1$
}
@Override
public String getGitPrefix()
{
return "C:\\Program Files (x86)\\Git"; //$NON-NLS-1$
}
@Override
public String getJREsRoot()
{
return "C:\\Program Files (x86)\\Java"; //$NON-NLS-1$
}
@Override
public OS getForBitness(int bitness)
{
if (bitness == 64)
{
return Win64.INSTANCE;
}
return this;
}
@Override
public Process execute(List<String> command, boolean terminal) throws Exception
{
if (terminal)
{
List<String> terminalCommand = new ArrayList<String>();
terminalCommand.add("cmd"); //$NON-NLS-1$
terminalCommand.add("/C"); //$NON-NLS-1$
terminalCommand.add("start"); //$NON-NLS-1$
terminalCommand.addAll(command);
command = terminalCommand;
}
return super.execute(command, terminal);
}
@Override
protected String[] getOpenCommands()
{
return OPEN_COMMANDS;
}
}
/**
* @author Eike Stepper
*/
private static class Win64 extends Win32
{
private static final Win64 INSTANCE = new Win64(Platform.WS_WIN32);
public Win64(String osgiWS)
{
super(osgiWS, Platform.ARCH_X86_64);
}
@Override
public String getJREsRoot()
{
return "C:\\Program Files\\Java"; //$NON-NLS-1$
}
@Override
public OS getForBitness(int bitness)
{
if (bitness == 32)
{
return Win32.INSTANCE;
}
return this;
}
}
/**
* @author Eike Stepper
*/
private static final class Mac extends OS
{
private static final String[] OPEN_COMMANDS = { "open" }; //$NON-NLS-1$
public Mac(String osgiWS, String osgiArch)
{
super(Platform.OS_MACOSX, osgiWS, osgiArch);
}
@Override
public boolean isMac()
{
return true;
}
@Override
public String getRelativeProductFolder(String folderName)
{
if (StringUtil.isEmpty(folderName))
{
folderName = "Eclipse.app"; //$NON-NLS-1$
}
else if (!folderName.endsWith(".app")) //$NON-NLS-1$
{
folderName += ".app"; //$NON-NLS-1$
}
return folderName + "/Contents/Eclipse"; //$NON-NLS-1$
}
@Override
public String getRelativeExecutableFolder()
{
return "../MacOS"; //$NON-NLS-1$
}
@Override
public String getGitPrefix()
{
return "/"; //$NON-NLS-1$
}
@Override
public String getJREsRoot()
{
return "/"; //$NON-NLS-1$
}
@Override
public boolean is32BitAvailable()
{
return false;
}
@Override
public OS getForBitness(int bitness)
{
return this;
}
@Override
public Process execute(List<String> command, boolean terminal) throws Exception
{
if (terminal)
{
File commandFile = File.createTempFile("cmd-", ""); //$NON-NLS-1$ //$NON-NLS-2$
commandFile.deleteOnExit();
String commandPath = commandFile.toString();
String commandLine = getCommandLine(command);
IOUtil.writeUTF8(commandFile, commandLine);
List<String> chmodCommand = new ArrayList<String>();
chmodCommand.add("chmod"); //$NON-NLS-1$
chmodCommand.add("a+x"); //$NON-NLS-1$
chmodCommand.add(commandPath);
ProcessBuilder chmodProcessBuilder = new ProcessBuilder(chmodCommand);
Process chmodProcess = chmodProcessBuilder.start();
chmodProcess.waitFor();
List<String> terminalCommand = new ArrayList<String>();
terminalCommand.add("open"); //$NON-NLS-1$
terminalCommand.add("-b"); //$NON-NLS-1$
terminalCommand.add("com.apple.terminal"); //$NON-NLS-1$
terminalCommand.add(commandPath);
command = terminalCommand;
}
return super.execute(command, terminal);
}
@Override
protected String[] getOpenCommands()
{
return OPEN_COMMANDS;
}
}
/**
* @author Eike Stepper
*/
private static final class Linux extends OS
{
private static final String[] OPEN_COMMANDS = { "kde-open", "gnome-open", "xdg-open", "sensible-browser" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
private static final String[] TERMINAL_COMMANDS = { "gnome-terminal", "xterm" }; //$NON-NLS-1$ //$NON-NLS-2$
public Linux(String osgiWS, String osgiArch)
{
super(Platform.OS_LINUX, osgiWS, osgiArch);
}
@Override
public boolean isLinux()
{
return true;
}
@Override
public String getRelativeExecutableFolder()
{
return ""; //$NON-NLS-1$
}
@Override
public String getGitPrefix()
{
return ""; //$NON-NLS-1$
}
@Override
public String getJREsRoot()
{
return ""; //$NON-NLS-1$
}
@Override
public OS getForBitness(int bitness)
{
String osgiArch = getOsgiArch();
if (bitness == 32)
{
if (Platform.ARCH_X86_64.equals(osgiArch))
{
return createLinux(Platform.ARCH_X86);
}
}
else
{
if (Platform.ARCH_X86.equals(osgiArch))
{
return createLinux(Platform.ARCH_X86_64);
}
}
return this;
}
@Override
public Process execute(List<String> command, boolean terminal) throws Exception
{
if (terminal)
{
String commandLine = getCommandLine(command);
for (String xterm : TERMINAL_COMMANDS)
{
try
{
List<String> terminalCommand = new ArrayList<String>();
terminalCommand.add(xterm);
terminalCommand.add("-e"); //$NON-NLS-1$
terminalCommand.add(commandLine);
return super.execute(terminalCommand, true);
}
catch (Exception ex)
{
//$FALL-THROUGH$
}
}
throw new IOException(NLS.bind(Messages.OS_CannotStartTerminal_exception, new Object[] { TERMINAL_COMMANDS }));
}
return super.execute(command, false);
}
@Override
protected String[] getOpenCommands()
{
return OPEN_COMMANDS;
}
private Linux createLinux(String arch)
{
return new Linux(getOsgiWS(), arch);
}
}
}