/******************************************************************************* * Copyright (c) 2012 VMWare, Inc. * 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: * VMWare, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.ui.internal.launch; import java.io.File; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.ui.console.IConsole; import org.eclipse.debug.ui.console.IConsoleLineTracker; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IRegion; import org.eclipse.ui.console.IHyperlink; import org.grails.ide.eclipse.core.GrailsCoreActivator; import org.grails.ide.eclipse.core.launch.GrailsLaunchArgumentUtils; import org.grails.ide.eclipse.ui.internal.utils.WebUiUtils; /** * @author Christian Dupuis * @author Andy Clement * @author Andrew Eisenberg * @author Kris De Volder * @since 2.2.0 */ public class GrailsConsoleLineTracker implements IConsoleLineTracker { private static final String OR_MARKER = " or "; private static final String RUNNING_MARKER = "Server running. Browse to "; private static final String TEST_PASSED_MARKER = "Tests PASSED - view reports in "; private static final String TEST_FAILED_MARKER_1 = "Tests FAILED - view reports in "; //Grails 1.3.X private static final String TEST_FAILED_MARKER_2 = "Tests FAILED - view reports in "; //Grails 2.0.X // Typical line: (grails 1.1.1) // "Cobertura Code Coverage Complete (view reports in: N:\workspaces\grails_play\gTunes\test\reports/cobertura)" // Typical line: (grails 1.2m3) // "Cobertura Code Coverage Complete (view reports in: target\test-reports/cobertura)" private static final String CODE_COVERAGE_MARKER = "Cobertura Code Coverage Complete (view reports in: "; private IConsole console; public void init(IConsole console) { this.console = console; } public void dispose() { } public void lineAppended(IRegion line) { IProcess process = console.getProcess(); ILaunch launch = process.getLaunch(); ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); if (launchConfiguration != null && isGrailsLaunch(launchConfiguration)) { try { int offset = line.getOffset(); int length = line.getLength(); // open in browser hyperlink // if run-app --https, then there are 2 hyperlinks, check for that. String lineStr = console.getDocument().get(offset, length); String projectBaseDir = ""; try { projectBaseDir = launchConfiguration.getAttribute(GrailsLaunchArgumentUtils.PROJECT_DIR_LAUNCH_ATTR, ""); } catch (CoreException e) { // ignore for now } createRunAppHyperlink(lineStr, line.getOffset()); createResultsHyperlink(TEST_PASSED_MARKER, lineStr, line.getOffset(), projectBaseDir); createResultsHyperlink(TEST_FAILED_MARKER_1, lineStr, line.getOffset(), projectBaseDir); createResultsHyperlink(TEST_FAILED_MARKER_2, lineStr, line.getOffset(), projectBaseDir); createResultsHyperlink(CODE_COVERAGE_MARKER, lineStr, line.getOffset(), projectBaseDir); } catch (BadLocationException ex) { // ignore } } } /** * @param lineNum * @param lineStr */ protected void createRunAppHyperlink(String lineStr, int lineOffset) { int runningMarkerIndex = lineStr.indexOf(RUNNING_MARKER); if (runningMarkerIndex >= 0) { int startFirst = runningMarkerIndex + RUNNING_MARKER.length(); int endFirst = lineStr.indexOf(OR_MARKER, startFirst); int startSecond; int endSecond; if (endFirst >= 0) { startSecond = endFirst + OR_MARKER.length(); endSecond = lineStr.length(); } else { startSecond = -1; endSecond = -1; endFirst = lineStr.length(); } if (startFirst >= 0) { // add the http url String url = lineStr.substring(startFirst, endFirst).trim(); GrailsHyperLink link = new GrailsHyperLink(url); console.addLink(link, lineOffset + startFirst, url.length()); if (startSecond >= 0) { // add the https url url = lineStr.substring(startSecond, endSecond).trim(); link = new GrailsHyperLink(url); console.addLink(link, lineOffset + startSecond, url.length()); } } } } protected void createResultsHyperlink(String marker, String text, int offset, String baseDir) { int index = text.indexOf(marker); if (index >= 0) { int parenIndex = text.indexOf(')', index); int end = parenIndex > 0 ? parenIndex : text.length(); int beg = marker.length()+index; String path = text.substring(beg, end); String subFolder = marker.equals(CODE_COVERAGE_MARKER) ? "" : File.separator + "html"; String url = "file:" + toAbsolute(path, baseDir) + subFolder + File.separator + "index.html"; GrailsHyperLink link = new GrailsHyperLink(url); console.addLink(link, offset + beg, path.length()); } } /** * Poor first stab at detecting whether a path is relative and making it absolute. * * @param path the path that may or may not be relative * @param prefix the prefix to prepend to the path if it proves to be relative * @return the absolute path */ private String toAbsolute(String path, String prefix) { if (path.length() > 1 && path.charAt(1) == ':') { // windows drive qualifier } else { if (path.length() > 0 && path.charAt(0) != File.separatorChar) { return prefix + File.separator + path; } } return path; } private boolean isGrailsLaunch(ILaunchConfiguration launchConfiguration) { try { ILaunchConfigurationType type = launchConfiguration.getType(); return GrailsCoreActivator.PLUGIN_ID.equals(type.getPluginIdentifier()); } catch (CoreException ex) { return false; } } public class GrailsHyperLink implements IHyperlink { private final String url; public GrailsHyperLink(String url) { this.url = url; } public void linkActivated() { WebUiUtils.openUrl(url); } public void linkEntered() { } public void linkExited() { } public String getUrl() { return url; } } }