/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package org.absmodels.abs.plugin.util; import static org.absmodels.abs.plugin.util.Constants.*; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import org.absmodels.abs.plugin.Activator; import org.absmodels.abs.plugin.builder.AbsNature; import org.absmodels.abs.plugin.console.ConsoleManager; import org.absmodels.abs.plugin.console.MsgConsole; import org.absmodels.abs.plugin.console.ConsoleManager.MessageType; import org.absmodels.abs.plugin.editor.ABSEditor; import org.absmodels.abs.plugin.editor.outline.PackageAbsFile; import org.absmodels.abs.plugin.editor.outline.PackageAbsFileEditorInput; import org.absmodels.abs.plugin.editor.outline.PackageContainer; import org.absmodels.abs.plugin.editor.outline.PackageEntry; import org.absmodels.abs.plugin.editor.outline.ABSContentOutlineConstants.AnnotationType; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPersistentPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.TextSelection; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; import abs.frontend.ast.*; import abs.frontend.parser.ABSPackageFile; import abs.frontend.parser.SourcePosition; /** * Collection of project-wide utility functions * @author cseise, tfischer */ public class UtilityFunctions { public static class EditorPosition{ private int linestart; private int colstart; private int lineend; private int colend; private IPath path; public int getLinestart() { return linestart; } public int getColstart() { return colstart; } public int getLineend() { return lineend; } public int getColend() { return colend; } public IFile getFile() { return ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(path); } public IPath getPath() { return path; } public EditorPosition(IPath path, int linestart, int colstart, int lineend, int colend) { this.path = path; this.linestart = linestart; this.colstart = colstart; this.lineend = lineend; this.colend = colend; } } /** * Checks, whether a ModuleDecl has declarations * @param m * @return TRUE if a module declaration has * <ul> * <li>Exports</li> * <li>Imports</li> * <li>MainBlock</li> * </ul> * declarations,<br/> * FALSE if m is null or the the module declarations */ public static boolean hasDecls(ModuleDecl m){ if (m!=null){ return (m.getNumDecl() > 0) || (m.getNumExport() > 0) || (m.getNumImport() > 1) || (m.hasBlock()); }else{ return false; } } /** * Checks whether a class declaration has a <code>[...]</code> annotation * with a specific content * * @param cd * The classDecl to be checked * @param type * the type of the annotation * @return TRUE if cd has the respective annotation, FALSE else */ public static boolean hasClassAnnotation(ClassDecl cd, AnnotationType type) { for (Annotation ant : cd.getAnnotationList()) { if (ant.getNumChild() > 0) { if (ant.getChild(0) instanceof DataConstructorExp) { DataConstructorExp exp = (DataConstructorExp) ant.getChild(0); if (exp.getConstructor().equals(type.getAnnotationString())) { return true; } } } } return false; } public static IPreferenceStore getDefaultPreferenceStore(){ return Activator.getDefault().getPreferenceStore(); } /** * Returns an {@link UtilityFunctions.EditorPosition} for the specified {@link ASTNode} * @param node The target ASTNode * @return The EditorPosition of the ASTNode, or null if node is null */ public static EditorPosition getPosition(ASTNode<?> node){ if (node != null){ //Get the start position int startLine = node.getStartLine() - 1; int startCol = node.getStartColumn() - 1; //Get the end position int endLine = node.getEndLine() - 1; int endCol = node.getEndColumn(); return new EditorPosition(null,startLine,startCol,endLine,endCol); } return null; } /** * deletes a file or directory. In case of a directory, it is deleted including all contents. */ public static boolean deleteRecursive(File dir){ if (dir.isDirectory()) { String[] children = dir.list(); for (int i=0; i<children.length; i++) { boolean success = deleteRecursive(new File(dir, children[i])); if (!success) { return false; } } } // The directory is now empty so delete it return dir.delete(); } public static boolean isIdentifierChar(char c){ return Character.isLetterOrDigit(c) || c=='_'; } /** Get String value of a persistent property. * @param name QualifiedName of the property which should be retrieved * @param project project where the property is stored * @return String value of the stored property * @throws CoreException */ public static String getPersistentProperty(QualifiedName name, IProject project) throws CoreException { return project.getPersistentProperty(name); } /** Get boolean value of a persistent property. * @param name QualifiedName of the property which should be retrieved * @param project project where the property is stored * @return boolean value of the stored property * @throws CoreException */ public static boolean getBooleanProperty(QualifiedName name, IProject project) throws CoreException { return Boolean.parseBoolean(getPersistentProperty(name, project)); } /** * Set String value of a persistent property * @param name * @param project * @param value * @throws CoreException */ public static void setProperty(QualifiedName name, IProject project, String value) throws CoreException { project.setPersistentProperty(name, value); } /** * Set boolean value of a boolean persistent property. * @param name * @param project * @param value * @throws CoreException */ public static void setProperty(QualifiedName name, IProject project, boolean value) throws CoreException { setProperty(name, project, String.valueOf(value)); } /** * Copies a file to a different location * @param source * @param target * @throws IOException Is thrown, if the source file does not exist */ public static void copyFile(File source, File target) throws IOException { InputStream inputStream = new FileInputStream(source); OutputStream outputStream = new FileOutputStream(target); try { copyFile(inputStream, outputStream); } finally{ if(inputStream != null){ inputStream.close(); } if(outputStream != null){ outputStream.close(); } } } /** * Copies an input stream into an output stream * @param inputStream * @param outputStream * @throws IOException */ public static void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException{ int length; byte[] buffer = new byte[4096]; while((length = inputStream.read(buffer))!= -1 ){ outputStream.write(buffer,0 , length); } } /** * Writes output of a process in a StringBuffer. Returns true if the process terminated without errors (output was copied * from output stream), false if the process encountered an error (output was copied from error stream) * @param p * @param output * @return false if the process encountered an error, true otherwise * @throws IOException * @throws InterruptedException */ public static boolean getProcessOutput(Process p, StringBuffer output) throws IOException, InterruptedException { return getProcessOutput(p, output, output); } public static boolean getProcessOutput(Process p, Appendable output, Appendable error) throws IOException, InterruptedException { String s; boolean success = true; BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); BufferedReader er = new BufferedReader(new InputStreamReader(p.getErrorStream())); //Wait until some output is available while(p.getInputStream().available() <= 0 && p.getErrorStream().available() <= 0){ Thread.sleep(10); } //If output is available append it to the output string while(p.getInputStream().available() > 0){ while((s = in.readLine()) != null) { output.append(s); output.append('\n'); } } //If Maude encountered an error, append this to the output string while(p.getErrorStream().available() > 0){ while((s = er.readLine()) != null) { error.append(s); error.append('\n'); success = false; } } return success; } /** * Wrapper method for {@link #getAbsNature(IProject)} * * @param file * @see #getAbsNature(IProject) * @return The corresponding AbsNature of the file is returned, or null if * file is null */ public static AbsNature getAbsNature(IResource file){ if(file==null) return null; return getAbsNature(file.getProject()); } /** * Returns the AbsNature of the corresponding project * * @param project * @return the AbsNature of the project or null, not accessible, if project * is null or a CoreException occurs */ public static AbsNature getAbsNature(IProject project){ try { if (project != null && project.isAccessible()){ return (AbsNature)project.getNature(Constants.NATURE_ID); } } catch (CoreException e) { standardExceptionHandling(e); return null; } return null; } /** * Convenience method showing an error dialog with the given error message. */ public static void showErrorMessage(final String errorMessage){ MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", errorMessage); } /** * shows an error dialog using asyncExec */ public static void showErrorMessageAsync(final String errorMessage){ Display.getDefault().asyncExec(new Runnable() { @Override public void run() { MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", errorMessage); } }); } public static void syncPreferenceStore(IPersistentPreferenceStore prefstore) { try { prefstore.save(); } catch (IOException e) { standardExceptionHandling(e); } } public static ASTNode<?> getASTNodeOfOffset(IDocument doc, CompilationUnit compunit, int offset) throws BadLocationException { int selline = doc.getLineOfOffset(offset) + 1; int lineoff = doc.getLineOffset(selline - 1); int selcol = offset - lineoff + 1; SourcePosition sourcepos = SourcePosition.findPosition(compunit, selline, selcol); if(sourcepos==null) return compunit; ASTNode<?> node = sourcepos.getContextNode(); return node; } /** * Creates the Maude command to make a partial run with a given step number and quit afterwards * @param steps Number of steps to be done. If negative, no steps will be taken at all * @return The assembled Maude command */ public static String getMaudeCommand (int steps){ return MAUDE_COMMAND1 + '[' + (steps < 0 ? 0 : steps) + ']' + MAUDE_COMMAND2 + MAUDE_COMMAND_QUIT; } public static ABSEditor openABSEditorForFile(PackageAbsFile file){ try { return (ABSEditor) IDE.openEditor(getActiveWorkbenchPage(), new PackageAbsFileEditorInput(file), Constants.EDITOR_ID); } catch (PartInitException e) { assert false : e; } return null; } /** * A convenient method to reconstruct a {@link PackageAbsFile} from the absolute * path to the ABS package and the name to the specific entry in the package. * @param proj the project which the package belongs to * @param pak * @param entry * @return */ public static PackageAbsFile getPackageAbsFile(IProject proj, String pak, String entry) { File file = new File(pak); try { if (new ABSPackageFile(file).isABSPackage()) { PackageEntry pentry = null; if (proj != null) { AbsNature nature = getAbsNature(proj); for (PackageEntry e : nature.getPackages().getPackages()) { if (e.getPath().equals(file.getAbsolutePath())) { pentry = e; break; } } } if (pentry == null) { PackageContainer container = new PackageContainer(); container.setProject(proj); pentry = new PackageEntry( container, file.getName(), file.getAbsolutePath(), true); } return new PackageAbsFile(pentry, entry); } } catch (IOException e) { } return null; } public static IProject getAbsProjectFromWorkspace(String name) { IProject p = ResourcesPlugin.getWorkspace().getRoot().getProject(name); if (getAbsNature(p) != null) { return p; } return null; } /** * Get the current active {@link IWorkbenchPage}. * @return the current active {@link IWorkbenchPage}. */ private static IWorkbenchPage getActiveWorkbenchPage() { return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); } public static ABSEditor openABSEditorForFile(IPath path, IProject project){ IFileStore fileStore = EFS.getLocalFileSystem().getStore(path); if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) { try { IEditorPart part = IDE.openEditorOnFileStore(getActiveWorkbenchPage(), fileStore); // Could be an ErrorEditorPart: if (part instanceof ABSEditor) { return (ABSEditor) part; } else { throw new PartInitException("Couldn't open editor, sorry."); } } catch (PartInitException e) { standardExceptionHandling(e); } } else if (path.toPortableString().startsWith("jar:file:") // works for windows systems || path.segment(0).equals("jar:file:")) { // works for linux systems // TODO make conditions above cross platform. // a jar file try { String parts = new URI(path.toString()).getRawSchemeSpecificPart(); String pak = new URI(parts.split("!/")[0]).getSchemeSpecificPart(); String entry = parts.split("!/")[1]; return openABSEditorForFile(getPackageAbsFile(project, pak, entry)); } catch (URISyntaxException e) { standardExceptionHandling(e); } } return null; } /** * Returns the IPath of a ModuleDecl's compilation unit * @param mDecl * @return IPath of a ModuleDecl's compilation unit */ public static IPath getPathOfModuleDecl(InternalASTNode<ModuleDecl> mDecl) { if (mDecl != null){ IPath path = new Path(mDecl.getASTNode().getCompilationUnit().getFileName()); return path; } return null; } public static boolean saveEditors(IProject project, boolean withConfirmation){ IEditorPart[] dirtyEditors = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getDirtyEditors(); if(dirtyEditors.length>0 && withConfirmation){ boolean doit = MessageDialog.openQuestion(Display.getDefault().getActiveShell(), "Save modified files", "Should the modified files be saved?"); if(!doit) return false; } boolean allsaved = true; for(IEditorPart iep : dirtyEditors){ IResource editorFile = (IResource)iep.getEditorInput().getAdapter(IResource.class); if(editorFile!=null && editorFile.getProject().equals(project)){ allsaved = allsaved && PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().saveEditor(iep, false); } } try { project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); } catch (CoreException e) { standardExceptionHandling(e); } return allsaved; } public static boolean saveEditor(IFile file, boolean withConfirmation){ IEditorPart[] dirtyEditors = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getDirtyEditors(); for(IEditorPart iep : dirtyEditors){ IResource editorFile = (IResource)iep.getEditorInput().getAdapter(IResource.class); if(editorFile!=null && editorFile.equals(file)){ return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().saveEditor(iep, withConfirmation); } } return true; //file was not dirty } public static IFile getFileOfModuleDecl(ModuleDecl m) { if (m != null) { Path path = new Path(m.getFileName()); path.makeRelative(); IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(path); return file; } return null; } public static boolean hasABSFileExtension(IResource file){ return file != null && ABS_FILE_EXTENSION.equals(file.getFileExtension()); } public static boolean isABSSourceFile(IResource file){ if (file != null && file instanceof IFile) // TODO: IResource? return hasABSFileExtension(file); return false; } public static boolean isABSFile(IResource file){ if (file != null && file instanceof IFile){ // TODO: IResource? return hasABSFileExtension(file) || isABSPackage((IFile)file); } return false; } public static boolean isABSPackage(File file) throws IOException { return file.exists() && new ABSPackageFile(file).isABSPackage(); } // assumes file != null public static boolean isABSPackage(IFile file) { if (! "jar".equals(file.getFileExtension())) return false; try { return isABSPackage(file.getLocation().toFile()); } catch (IOException e) { standardExceptionHandling(e); return false; } } /** * Always log exceptions in the Error Log. * @param e */ public static void standardExceptionHandling(Exception e){ Activator.logException(e); } /** * checks if a string is formatted as a number */ public static boolean isNumber(String s) { try { Integer.parseInt(s); return true; } catch (NumberFormatException e) { return false; } } /** * prints a message to the default console */ public static void printToConsole(String msg) { MsgConsole c = ConsoleManager.getDefault(); c.println(msg, MessageType.MESSAGE_INFO); } public static boolean jumpToPosition(IProject project, EditorPosition pos) { ABSEditor targeteditor = UtilityFunctions.openABSEditorForFile(pos.getPath(),project ); if(targeteditor==null){ return false; } IDocument doc = targeteditor.getDocumentProvider().getDocument(targeteditor.getEditorInput()); try { int startoff = doc.getLineOffset(pos.getLinestart()-1) + pos.getColstart()-1; targeteditor.getSelectionProvider().setSelection(new TextSelection(startoff,0)); return true; } catch(BadLocationException ex){ Activator.logException(ex); return false; } } }