package org.lejos.ev3.ldt.util; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.lejos.ev3.ldt.LeJOSEV3Nature; import org.lejos.ev3.ldt.LeJOSEV3Plugin; import org.lejos.ev3.ldt.preferences.PreferenceConstants; /** * utility methods for the plugin * * @author Matthias Paul Scholz and Lawrie Griffiths * */ public class LeJOSEV3Util { public static final String LIBDIR = "lib"; public static final String LIBSUBDIR_PC = ""; public static final String LIBSUBDIR_EV3 = ""; public static boolean isWindows() { return System.getProperty("os.name", "").toLowerCase().startsWith("windows "); } public static boolean isOSX() { return System.getProperty("os.name", "").toLowerCase().startsWith("mac os x"); } public static boolean getJavaProjectFromSelection(ISelection selection, Collection<IJavaProject> dst) { boolean foundInvalid = false; if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection)selection; for (Iterator<?> it = ss.iterator(); it.hasNext();) { Object element = it.next(); if (element instanceof IJavaProject) { // we see IJavaProjects when selecting in package explorer dst.add((IJavaProject) element); } else if (element instanceof IProject) { // we see IProjects when selecting in project explorer, or navigator. IProject p = (IProject) element; try { if (p.isOpen() && p.isNatureEnabled(JavaCore.NATURE_ID)) dst.add(JavaCore.create(p)); } catch (CoreException e) { foundInvalid = true; } } else { foundInvalid = true; } } } return foundInvalid; } public static boolean getFilesFromSelection(ISelection selection, Collection<File> dst) { boolean foundInvalid = false; if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) selection; for (Iterator<?> it = ss.iterator(); it.hasNext(); ) { Object element = it.next(); if (element instanceof IFile) { IFile f = (IFile)element; dst.add(f.getLocation().toFile()); } else { foundInvalid = true; } } } return foundInvalid; } public static IJavaElement getFirstJavaElementFromSelection(ISelection selection) { if (selection instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) selection; for (Iterator<?> it = ss.iterator(); it.hasNext(); ) { Object element = it.next(); if (element instanceof IJavaElement) { // we see IJavaProjects when selecting in package explorer IJavaElement e = (IJavaElement) element; return e; } else if (element instanceof IFile) { // we see IFile when selecting in project explorer, or navigator. IJavaElement e = JavaCore.create((IFile)element); if (e != null) return e; } } } return null; } public static IType getJavaTypeFromElement(IJavaElement element) { switch (element.getElementType()) { case IJavaElement.COMPILATION_UNIT: return ((ICompilationUnit)element).findPrimaryType(); case IJavaElement.TYPE: return (IType)element; } return null; } public static boolean isLeJOSProject(IProject project) throws CoreException { return project.isNatureEnabled(LeJOSEV3Nature.ID); } public static boolean isLeJOSProject(IJavaProject project) throws CoreException { return isLeJOSProject(project.getProject()); } public static void message(String message) { message(message, null); } public static void message(String msg, Throwable throwable) { // log to leJOS EV3 console PrintWriter pw = LeJOSEV3Plugin.getDefault().getConsoleWriter(); if (msg != null) pw.println(msg); if (throwable != null) { pw.println(throwable); Throwable t = throwable.getCause(); while (t != null) { pw.println("Caused by "+t); t = t.getCause(); } pw.println("See Eclipse error log for detailed stack trace."); log(throwable); } } public static void error(String message) { error(message, null); } public static void error(String msg, Throwable throwable) { LeJOSEV3Plugin.getDefault().getConsole().activate(); message(msg, throwable); } public static void log(Throwable throwable) { // log to error log Status status = new Status(IStatus.ERROR, LeJOSEV3Plugin.ID, throwable.getMessage(), throwable); LeJOSEV3Plugin.getDefault().getLog().log(status); } private static void walkTreeAndReturnJARS(File dir, Collection<File> dst) { for (File e : dir.listFiles()) { if (e.isDirectory()) walkTreeAndReturnJARS(e, dst); else { if (e.getName().toLowerCase().endsWith(".jar")) { dst.add(e); } } } } public static void buildClasspath(File ev3Home, String subdir, Collection<File> dst) throws LeJOSEV3Exception { File f1 = new File(ev3Home, LIBDIR); File f2 = new File(f1, subdir); if (!f2.isDirectory()) throw new LeJOSEV3Exception(f2+" is not a directory"); walkTreeAndReturnJARS(f2, dst); } public static void buildEV3Classpath(File ev3Home, Collection<File> dst) throws LeJOSEV3Exception { buildClasspath(ev3Home, LIBSUBDIR_EV3, dst); } public static void buildPCClasspath(File ev3Home, Collection<File> dst) throws LeJOSEV3Exception { buildClasspath(ev3Home, LIBSUBDIR_PC, dst); } public static File getEV3Home() throws LeJOSEV3Exception { // get EV3_HOME PrefsResolver p = new PrefsResolver(LeJOSEV3Plugin.ID, null); String ev3Home = p.getString(PreferenceConstants.KEY_EV3_HOME, null); if (ev3Home == null || ev3Home.length() <= 0) throw new LeJOSEV3Exception("EV3_HOME is not set. Please specify it in the plug-in's preferences"); File f = new File(ev3Home); if (!f.isDirectory()) throw new LeJOSEV3Exception("EV3_HOME="+f+" is not a directory"); //message("EV3 Home is " + f.getPath()); return f; } public static void getUploadOpts(List<String> dst) throws LeJOSEV3Exception { PrefsResolver p = new PrefsResolver(LeJOSEV3Plugin.ID, null); boolean byName = p.getBoolean(PreferenceConstants.KEY_TARGET_CONNECT_BY_NAME, false); } public static void getUploadOpts(List<String> dst, String type, String addr, String name) throws LeJOSEV3Exception { if (addr != null) { dst.add("-d"); dst.add(addr); } if (name != null) { dst.add("-n"); dst.add(name); } } public static File resolvePath(IPath path) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource res = root.findMember(path); if (res != null) path = res.getLocation(); return path.toFile(); } public static IPath toPath(File f) { return Path.fromOSString(f.getAbsolutePath()); } /** * * build the classpath for the link and upload utility * * @param project * @return String classpath * @throws JavaModelException * @throws LeJOSEV3Exception */ public static void getProjectClassPath(IJavaProject project, boolean onlyExported, List<File> dst) throws JavaModelException, LeJOSEV3Exception { dst.add(resolvePath(project.getOutputLocation())); // project's classpath IClasspathEntry[] entries = project.getResolvedClasspath(true); // build string for (IClasspathEntry classpathEntry : entries) { if (!onlyExported || classpathEntry.isExported()) { switch (classpathEntry.getEntryKind()) { case IClasspathEntry.CPE_SOURCE: // source => ignore IPath p = classpathEntry.getOutputLocation(); if (p != null) dst.add(resolvePath(p)); break; case IClasspathEntry.CPE_PROJECT: // another project => // append classpath of other project IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IProject referencedProject = workspaceRoot.getProject(classpathEntry.getPath().toString()); IJavaProject referencedJavaProject = JavaCore.create(referencedProject); getProjectClassPath(referencedJavaProject, true, dst); break; case IClasspathEntry.CPE_LIBRARY: dst.add(resolvePath(classpathEntry.getPath())); break; case IClasspathEntry.CPE_VARIABLE: case IClasspathEntry.CPE_CONTAINER: // variable and container should never occur, since we use resolved classpath default: throw new LeJOSEV3Exception("unsupported classpath entry "+classpathEntry); } } } } public static String getClasspathString(List<File> c) { StringBuilder sb = new StringBuilder(); Iterator<File> i = c.iterator(); if (i.hasNext()) { sb.append(i.next().getAbsolutePath()); while (i.hasNext()) { sb.append(File.pathSeparatorChar); sb.append(i.next().getAbsolutePath()); } } return sb.toString(); } private static ToolStarter currentStarter; private static ToolStarter currentStarterExt; private static boolean currentStarterType; private static synchronized void updateStarters() throws LeJOSEV3Exception { PrefsResolver p = new PrefsResolver(LeJOSEV3Plugin.ID, null); boolean separateJVM = p.getBoolean(PreferenceConstants.KEY_SEPARATE_JVM, false); File ev3Home = getEV3Home(); if (currentStarterExt == null || !ev3Home.equals(currentStarterExt.getEV3Home()) || !currentStarterExt.isUp2Date()) { currentStarterExt = new ExternalJVMToolStarter(ev3Home); } if (currentStarter == null || currentStarterType != separateJVM || !ev3Home.equals(currentStarter.getEV3Home()) || !currentStarter.isUp2Date()) { if (separateJVM) currentStarter = currentStarterExt; else currentStarter = new ClassLoaderToolStarter(ev3Home); currentStarterType = separateJVM; } } public static synchronized ToolStarter getCachedExternalStarter() throws LeJOSEV3Exception { updateStarters(); return currentStarterExt; } public static synchronized ToolStarter getCachedToolStarter() throws LeJOSEV3Exception { updateStarters(); return currentStarter; } public static final String TOOL_EV3CONTROL = "lejos.ev3.tools.EV3Control"; public static final String TOOL_EV3MAPCOMMAND = "lejos.ev3.tools.EV3MapCommand"; public static final String TOOL_EV3SCPUPLOAD = "lejos.ev3.tools.EV3ScpUpload"; public static final String TOOL_EV3SDCARD = "lejos.ev3.tools.EV3SDCard"; public static ProcessBuilder createProcessBuilder(List<String> args2) throws IOException { int len = args2.size(); ArrayList<String> args3; if (len <= 1 || !isWindows()) { args3 = new ArrayList<String>(len); args3.addAll(args2); } else { args3 = new ArrayList<String>(2); Iterator<String> it = args2.iterator(); args3.add(it.next()); // Both java.lang.Runtime.exec(String[]) as well as in java.lang.ProcessBuilder // don't escape the arguments that are passed to the program. Also, they fail to // handle the empty string correctly. Hence, we manually escape all arguments. // If the second element of the command list starts and ends with a quote, // then ProcessBuilder won't add more quotes and the string will be passed // to the invoked program without any further processing (not documented, but // the implementation shows that this is the case). StringBuilder sb = new StringBuilder(); while (it.hasNext()) { sb.append(' '); // the escaping must add quotes around the argument in order to // satisfy the requirements described above. escapeWindowsArg(it.next(), sb); } args3.add(sb.substring(1)); } return new ProcessBuilder(args3); } private static void escapeWindowsArg(String t, StringBuilder sb) { /* Escaping of quotes and backslashes, compatible * with that Microsoft's default decoding. See * http://msdn.microsoft.com/en-us/library/a1y7w461.aspx * * How microsoft's default decoding works: * 2n backslashes + quote => n backslashes + closing quote * 2n+1 backslashes + quote => n backslashes + inner quote * n backslashes not followed by a quote => n backslashes */ int len = t.length(); sb.append('"'); for (int i=0; i<len; ) { char c = t.charAt(i); switch (c) { case '\\': int j=i++; sb.append('\\'); for (; i<len && t.charAt(i)=='\\'; i++) sb.append('\\'); // double the number of backslashes if they are at the // end of the argument or precede a quote if (i==len || t.charAt(i)=='"') for (; j<i; j++) sb.append('\\'); break; case '"': sb.append('\\'); default: sb.append(c); i++; } } sb.append('"'); } }