/* * Copyright 2009-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.groovy.eclipse.launchers; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.codehaus.groovy.eclipse.GroovyPlugin; import org.codehaus.groovy.eclipse.core.GroovyCore; import org.codehaus.groovy.eclipse.editor.GroovyEditor; import org.codehaus.jdt.groovy.model.GroovyNature; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.Path; import org.eclipse.debug.ui.console.FileLink; import org.eclipse.debug.ui.console.IConsole; import org.eclipse.debug.ui.console.IConsoleLineTracker; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.groovy.core.util.ReflectionUtils; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IHyperlink; import org.eclipse.ui.dialogs.ListDialog; import org.eclipse.ui.model.WorkbenchLabelProvider; /** * @author Scott Hickey */ public class GroovyConsoleLineTracker implements IConsoleLineTracker { /** * @author Andrew Eisenberg * @created Sep 21, 2009 */ public class AmbiguousFileLink extends FileLink implements IHyperlink { IFile[] files; boolean fileChosen = false; public AmbiguousFileLink(IFile[] files, String editorId, int fileOffset, int fileLength, int fileLineNumber) { super(null, editorId, fileOffset, fileLength, fileLineNumber); this.files = files; } @Override public void linkActivated() { if (!fileChosen) { IFile file = chooseFile(files); if (file != null) { fileChosen = true; ReflectionUtils.setPrivateField(FileLink.class, "fFile", this, file); } } if (fileChosen) { super.linkActivated(); } } } /** * @author Andrew Eisenberg * @created Sep 21, 2009 */ public class FileContentProvider implements IStructuredContentProvider { public Object[] getElements(Object inputElement) { IFile[] files = (IFile[]) inputElement; Object[] out = new Object[files.length]; for (int i = 0; i < out.length; i++) { out[i] = files[i]; } return out; } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } private IConsole console; private final static Pattern linePattern = Pattern.compile(".*\\((.*)\\.groovy(:(.*))?\\)"); public void init(IConsole console) { this.console = console; } /** * Hyperlink error lines to the editor. */ // not implemented yet public void lineAppended(IRegion line) { if (console == null) return; int lineOffset = line.getOffset(); int lineLength = line.getLength(); try { String consoleLine = console.getDocument().get(lineOffset, lineLength); GroovyPlugin.trace(consoleLine); Matcher m = linePattern.matcher(consoleLine); String groovyFileName = null; int lineNumber = -1; int openParenIndexAt = -1; int closeParenIndexAt = -1; // match if (m.matches()) { GroovyCore.trace("match: " + m); consoleLine = m.group(0); openParenIndexAt = consoleLine.indexOf("("); if (openParenIndexAt >= 0) { int end = consoleLine.indexOf(".groovy"); if(end == -1 || (openParenIndexAt + 1) >= end) { return; } String groovyClassName = consoleLine.substring(openParenIndexAt + 1, end); int classIndex = consoleLine.indexOf(groovyClassName); int start = 3; if(classIndex < start || classIndex >= consoleLine.length()) { return; } String groovyFilePath = consoleLine.substring(start, classIndex).trim().replace('.','/'); groovyFileName = groovyFilePath + groovyClassName + ".groovy"; int colonIndex = consoleLine.indexOf(":"); // get the line number in groovy class closeParenIndexAt = consoleLine.lastIndexOf(")"); if (colonIndex > 0) { lineNumber = Integer.parseInt(consoleLine.substring(colonIndex + 1, closeParenIndexAt)); } GroovyPlugin.trace("groovyFile=" + groovyFileName + " lineNumber:" + lineNumber); } // hyperlink if we found something if (groovyFileName != null) { IFile[] file = searchForFileInLaunchConfig(groovyFileName); if (file.length == 1) { IHyperlink link = new FileLink(file[0], GroovyEditor.EDITOR_ID, -1, -1, lineNumber); console.addLink(link, lineOffset + openParenIndexAt + 1, closeParenIndexAt - openParenIndexAt -1); } else if (file.length > 1) { IHyperlink link = new AmbiguousFileLink(file, GroovyEditor.EDITOR_ID, -1, -1, lineNumber); console.addLink(link, lineOffset + openParenIndexAt + 1, closeParenIndexAt - openParenIndexAt -1); } } } } catch (Exception e) { GroovyPlugin.trace("unexpected error:" + e.getMessage()); } } /** * @param groovyFileName * @return * @throws JavaModelException */ private IFile[] searchForFileInLaunchConfig(String groovyFileName) throws JavaModelException { List<IFile> files = new LinkedList<IFile>(); IJavaProject[] projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects(); for (IJavaProject javaProject : projects) { if (GroovyNature.hasGroovyNature(javaProject.getProject())) { for (IPackageFragmentRoot root : javaProject.getAllPackageFragmentRoots()) { if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { IResource resource = root.getResource(); if (resource.isAccessible() && resource.getType() != IResource.FILE) { IFile file = ((IContainer) resource).getFile(new Path(groovyFileName)); if (file.isAccessible()) { files.add(file); } } } } } } return files.toArray(new IFile[files.size()]); } /** * @param files * @return */ IFile chooseFile(final IFile[] files) { final IFile[] result = new IFile[] { null }; ConsolePlugin.getStandardDisplay().syncExec(new Runnable() { public void run() { final ListDialog dialog = new ListDialog(ConsolePlugin.getStandardDisplay().getActiveShell()); dialog.setLabelProvider(new WorkbenchLabelProvider() { @Override protected String decorateText(String input, Object element) { return ((IFile) element).getFullPath().toPortableString(); } }); dialog.setTitle("Choose file to open"); dialog.setInput(files); dialog.setContentProvider(new FileContentProvider()); dialog.setAddCancelButton(true); dialog.setMessage("Select a file:"); dialog.setBlockOnOpen(true); int dialogResult = dialog.open(); if (dialogResult == Window.OK && dialog.getResult().length > 0) { result[0] = (IFile) dialog.getResult()[0]; } } }); return result[0]; } /** * @see org.eclipse.debug.ui.console.IConsoleLineTracker#dispose() */ public void dispose() { console = null; } }