/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - �ompleted initial version (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.ruby.internal.debug.ui.console; import java.io.File; import java.io.IOException; import org.eclipse.core.resources.IFile; 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.Path; import org.eclipse.dltk.core.IDLTKLanguageToolkit; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.debug.core.DLTKDebugPlugin; import org.eclipse.dltk.debug.ui.DLTKDebugUIPlugin; import org.eclipse.dltk.internal.ui.editor.EditorUtility; import org.eclipse.dltk.ruby.core.RubyLanguageToolkit; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IRegion; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.console.IHyperlink; import org.eclipse.ui.console.TextConsole; import org.eclipse.ui.ide.IDE; /** * A hyper link from a stack trace line of the form "*(*.rb:*)" */ public class RubyFileHyperlink implements IHyperlink { private static final String ERROR_UNKNOWN_HYPERLINK = "Unknown hyperlink"; //$NON-NLS-1$ private static final String ERROR_NO_COLON_IN_LINK = "No ':' in link"; //$NON-NLS-1$ static final boolean DEBUG = false; private final TextConsole fConsole; public RubyFileHyperlink(TextConsole console) { fConsole = console; } @Override public void linkEntered() { } @Override public void linkExited() { } @Override public void linkActivated() { final String fileName; int lineNumber; try { final String linkText = getLinkText(); fileName = extractFileName(linkText); lineNumber = extractLineNumber(linkText); } catch (IllegalArgumentException e) { DLTKDebugPlugin.log(e); return; } try { final Object element = findSourceModule(fileName); if (element == null) { // did not find source MessageDialog .openInformation( DLTKDebugUIPlugin.getActiveWorkbenchShell(), ConsoleMessages.RubyFileHyperlink_Information_1, NLS .bind( ConsoleMessages.RubyFileHyperlink_Source_not_found_for__0__2, new Object[] { fileName })); return; } openInEditor(element, lineNumber); } catch (CoreException e) { DLTKDebugUIPlugin .errorDialog( ConsoleMessages.RubyFileHyperlink_An_exception_occurred_while_following_link__3, e); } } /** * Opens the editor on the specified element and line number * * @param element * @param lineNumber * line number starting at 1 * @throws CoreException */ public static void openInEditor(final Object element, int lineNumber) throws CoreException { final IEditorInput input = EditorUtility.getEditorInput(element); if (input == null) { return; } final IEditorDescriptor descriptor = IDE.getEditorDescriptor(input .getName()); final IWorkbenchPage page = DLTKDebugUIPlugin.getActivePage(); final IEditorPart editor = page.openEditor(input, descriptor.getId()); // documents start at 0 if (lineNumber > 0) { lineNumber--; } EditorUtility.revealInEditor(editor, lineNumber); } /** * Finds {@link IFile} or {@link ISourceModule} matching the specified file * name * * @param fileName * @return */ public static Object findSourceModule(String fileName) { final IPath path = Path.fromOSString(fileName); final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IFile[] files = root.findFilesForLocation(path); if (files.length != 0) { return files[0]; } if (DEBUG) { System.out.println("File for " + path + " is not found"); //$NON-NLS-1$ //$NON-NLS-2$ } final IDLTKLanguageToolkit toolkit = RubyLanguageToolkit.getDefault(); final RubyConsoleSourceModuleLookup lookup = new RubyConsoleSourceModuleLookup( toolkit); return lookup.findSourceModuleByLocalPath(path); } /** * Returns the fully qualified name of the type to open * * @return fully qualified type name * @exception IllegalArgumentException * if unable to parse the type name */ static String extractFileName(String linkText) throws IllegalArgumentException { int pos = linkText.lastIndexOf(':'); if (pos > 0 && pos < linkText.length() - 1) { return normalizePath(linkText.substring(0, pos)); } throw new IllegalArgumentException(ERROR_NO_COLON_IN_LINK); } /** * Calls {@link File#getCanonicalPath()} for the specified file name * * @param filePath * @return */ private static String normalizePath(String filePath) { try { final File file = new File(filePath); return file.getCanonicalPath(); } catch (IOException e) { return filePath; } } /** * Returns the line number associated with the stack trace or throws * {@link IllegalArgumentException}. * * @exception IllegalArgumentException * if unable to parse the number */ static int extractLineNumber(String linkText) throws IllegalArgumentException { int pos = linkText.lastIndexOf(':'); if (pos > 0 && pos < linkText.length() - 1) { return Integer.parseInt(linkText.substring(pos + 1)); } throw new IllegalArgumentException(ERROR_NO_COLON_IN_LINK); } /** * Returns this link's text * * @exception IllegalArgumentException * if unable to retrieve the text */ protected String getLinkText() throws IllegalArgumentException { IRegion region = fConsole.getRegion(this); if (region == null) { throw new IllegalArgumentException(ERROR_UNKNOWN_HYPERLINK); } return getText(region.getOffset(), region.getLength()); } protected String getText(int offset, int length) throws IllegalArgumentException { try { return fConsole.getDocument().get(offset, length); } catch (BadLocationException e) { throw new IllegalArgumentException(e.getMessage()); } } }