/* * Copyright 2016 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.robotframework.ide.eclipse.main.plugin.launch; import static com.google.common.collect.Lists.newArrayList; import java.io.File; 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.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.IHyperlink; import org.eclipse.ui.console.IPatternMatchListener; import org.eclipse.ui.console.PatternMatchEvent; import org.eclipse.ui.console.TextConsole; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.FileEditorInput; import org.robotframework.ide.eclipse.main.plugin.RedPlugin; import org.robotframework.ide.eclipse.main.plugin.RedWorkspace; import org.robotframework.ide.eclipse.main.plugin.model.LibspecsFolder; import org.robotframework.ide.eclipse.main.plugin.model.RobotProject; /** * @author Michal Anglart * */ public class RobotConsolePatternsListener implements IPatternMatchListener { private TextConsole console; private final RobotProject robotProject; public RobotConsolePatternsListener(final RobotProject robotProject) { this.robotProject = robotProject; } @Override public void connect(final TextConsole console) { this.console = console; } @Override public void disconnect() { this.console = null; } @Override public void matchFound(final PatternMatchEvent event) { try { final String matchedLine = console.getDocument().get(event.getOffset(), event.getLength()); final PathWithOffset pathWithOffset = getPath(matchedLine); final File file = new File(pathWithOffset.path); if (file.exists() && file.isFile()) { final int offset = event.getOffset() + pathWithOffset.offsetInLine; final int length = pathWithOffset.path.length(); console.addHyperlink(new ExecutionArtifactsHyperlink(robotProject.getProject(), file), offset, length); } } catch (final BadLocationException e) { // fine, no hyperlinks then } } private PathWithOffset getPath(final String matchedLine) { if (matchedLine.startsWith("Output:")) { final String path = matchedLine.substring("Output:".length()).trim(); final int offset = matchedLine.length() - path.length(); return new PathWithOffset(path, offset); } else if (matchedLine.startsWith("Log:")) { final String path = matchedLine.substring("Log:".length()).trim(); final int offset = matchedLine.length() - path.length(); return new PathWithOffset(path, offset); } else if (matchedLine.startsWith("Report:")) { final String path = matchedLine.substring("Report:".length()).trim(); final int offset = matchedLine.length() - path.length(); return new PathWithOffset(path, offset); } else if (matchedLine.startsWith("Command:")) { final String listenerArg = "--argumentfile "; final int start = matchedLine.indexOf(listenerArg) + listenerArg.length(); final int end = matchedLine.indexOf(' ', start); return new PathWithOffset(matchedLine.substring(start, end), start); } return null; } @Override public String getPattern() { return "(Output|Log|Report|Command):\\s*(.*)"; } @Override public int getCompilerFlags() { return 0; } @Override public String getLineQualifier() { return "(Output|Log|Report|Command): "; } private static final class ExecutionArtifactsHyperlink implements IHyperlink { private final IProject project; private final File file; private ExecutionArtifactsHyperlink(final IProject project, final File file) { this.project = project; this.file = file; } @Override public void linkExited() { // nothing to do } @Override public void linkEntered() { // nothing to do } @Override public void linkActivated() { final IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (!file.exists()) { // it could have been deleted in the meantime final IStatus status = new Status(IStatus.ERROR, RedPlugin.PLUGIN_ID, "The file " + file.getAbsolutePath() + " does not exist in the file system anymore."); ErrorDialog.openError(workbenchWindow.getShell(), "Missing file", "File does not exist", status); return; } final IWorkspaceRoot root = project.getWorkspace().getRoot(); final RedWorkspace workspace = new RedWorkspace(root); IFile wsFile = (IFile) workspace.forUri(file.toURI()); if (wsFile == null) { wsFile = LibspecsFolder.get(project).getFile(file.getName()); try { wsFile.createLink(file.toURI(), IResource.REPLACE | IResource.HIDDEN, null); } catch (final CoreException e) { throw new IllegalArgumentException("Unable to open file", e); } } else if (!wsFile.exists()) { try { refreshAllNeededResources(root, wsFile.getFullPath()); refreshFile(wsFile); } catch (final CoreException e) { final String message = "Unable to open editor for file: " + wsFile.getName(); ErrorDialog.openError(workbenchWindow.getShell(), "Error opening file", message, new Status(IStatus.ERROR, RedPlugin.PLUGIN_ID, message, e)); } } try { openInEditor(workbenchWindow, wsFile); } catch (final PartInitException e) { final String message = "Unable to open editor for file: " + wsFile.getName(); ErrorDialog.openError(workbenchWindow.getShell(), "Error opening file", message, new Status(IStatus.ERROR, RedPlugin.PLUGIN_ID, message, e)); } } private IFile refreshAllNeededResources(final IWorkspaceRoot root, final IPath wsRelative) throws CoreException { IPath path = wsRelative; final List<String> removed = newArrayList(); while (root.findMember(path) == null) { removed.add(0, path.lastSegment()); path = path.removeLastSegments(1); } for (final String segment : removed) { root.findMember(path).refreshLocal(IResource.DEPTH_ONE, null); path = path.append(segment); } return (IFile) root.findMember(wsRelative); } private void refreshFile(final IFile wsFile) throws CoreException { if (!wsFile.isSynchronized(IResource.DEPTH_ZERO)) { wsFile.refreshLocal(IResource.DEPTH_ZERO, null); } } private void openInEditor(final IWorkbenchWindow workbenchWindow, final IFile wsFile) throws PartInitException { final IEditorDescriptor desc = IDE.getEditorDescriptor(wsFile); workbenchWindow.getActivePage().openEditor(new FileEditorInput(wsFile), desc.getId()); } } private static class PathWithOffset { private final String path; private final int offsetInLine; public PathWithOffset(final String path, final int offsetInLine) { this.path = path; this.offsetInLine = offsetInLine; } } }