/*
* Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License, Version 1.0,
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package net.rim.ejde.internal.ui.consoles;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.rim.ejde.internal.core.ContextManager;
import net.rim.ejde.internal.util.ProjectUtils;
import org.apache.log4j.Logger;
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.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.console.IHyperlink;
import org.eclipse.ui.console.IPatternMatchListener;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.console.PatternMatchEvent;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* Console for RAPC output that uses special highlighting to link resources.
*/
public class PackagingConsole extends MessageConsole {
private Logger log = Logger.getLogger( PackagingConsole.class );
private static PackagingConsole rapcConsole;
// The difference between a "LinePattern" and a simple "Pattern" is that a
// LinePattern matches a whole line that contains that pattern. A Pattern
// will return the exact occurence of that thing. This is confusing, I
// agree, but saves time having to compile regex'es dynamically on the fly.
static public final Pattern errorLinePattern = Pattern.compile( "\\S.*\\.[Jj][Aa][Vv][Aa]:\\d+:\\s.*" );
static public final Pattern errorPattern = Pattern.compile( "\\S.*\\.[Jj][Aa][Vv][Aa]:\\d+:" );
static public final Pattern projectLinePattern = Pattern.compile( "(Cleaning|Building|Project).*" );
private static Pattern projectPattern = null;
private String cachedProjectName;
private PackagingConsole() {
this( "BlackBerry Packaging Console" );
// Cache the initial project pattern and depend on
// RimResourceChangeAdapter.java to update it when necessary.
updateProjectPattern();
this.addPatternMatchListener( new RapcPatternMatchListener() );
// this.setConsoleWidth(80);
}
public static PackagingConsole getInstance() {
if( rapcConsole == null ) {
synchronized( PackagingConsole.class ) {
rapcConsole = new PackagingConsole();
}
}
return rapcConsole;
}
private PackagingConsole( String name ) {
super( name, null );
}
public MessageConsoleStream newMessageStream() {
return new BBLogConsoleStream(this);
}
/**
* Pattern matcher that recognizes project names.
*/
class RapcPatternMatchListener implements IPatternMatchListener {
private TextConsole console;
public RapcPatternMatchListener() {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IPatternMatchListenerDelegate#connect(org. eclipse.ui.console.TextConsole)
*/
public void connect( TextConsole console ) {
this.console = console;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IPatternMatchListenerDelegate#disconnect()
*/
public void disconnect() {
console = null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IPatternMatchListenerDelegate#matchFound(org .eclipse.ui.console.PatternMatchEvent)
*/
public void matchFound( PatternMatchEvent event ) {
try {
int offset = event.getOffset();
int length = event.getLength();
String match = console.getDocument().get( event.getOffset(), event.getLength() );
if( projectLinePattern.matcher( match ).matches() ) {
processProject( match, offset, length );
} else if( errorLinePattern.matcher( match ).matches() ) {
processError( match, offset, length );
}
} catch( BadLocationException e ) {
log.error( e.getMessage(), e );
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IPatternMatchListener#getCompilerFlags()
*/
public int getCompilerFlags() {
// See flags here:
// http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.
// html#compile(java.lang.String,%20int)
return 0;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IPatternMatchListener#getLineQualifier()
*/
public String getLineQualifier() {
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IPatternMatchListener#getPattern()
*/
public String getPattern() {
return ".*\\w.*"; //$NON-NLS-1$
}
private void processProject( String match, int offset, int length ) throws BadLocationException {
if( projectPattern == null ) {
return;
}
Matcher matcher = projectPattern.matcher( match );
if( matcher.find() ) {
String projectName = matcher.group();
// This lets the subsequent error lines (if any, have something
// to reduce the lookup scope by...)
cachedProjectName = projectName;
ProjectHyperlink link = new ProjectHyperlink( projectName );
console.addHyperlink( link, offset + matcher.start(), matcher.end() - matcher.start() );
}
}
private void processError( String match, int offset, int length ) throws BadLocationException {
Matcher matcher = errorPattern.matcher( match );
if( matcher.find() ) {
SourceCodeHyperlink link = new SourceCodeHyperlink( matcher.group(), cachedProjectName );
console.addHyperlink( link, offset + matcher.start(), matcher.end() - matcher.start() - 1 );
}
}
}
/**
* Hyperlink class that opens up the package explorer declaration when clicked.
*/
class ProjectHyperlink implements IHyperlink {
private String projectName;
public ProjectHyperlink( String projectName ) {
this.projectName = projectName;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IHyperlink#linkActivated()
*/
@SuppressWarnings("restriction")
public void linkActivated() {
PackageExplorerPart view = PackageExplorerPart.openInActivePerspective();
IJavaProject javaProject = JavaCore.create( ResourcesPlugin.getWorkspace().getRoot().getProject( projectName ) );
view.tryToReveal( javaProject );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IHyperlink#linkEntered()
*/
public void linkEntered() {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IHyperlink#linkExited()
*/
public void linkExited() {
}
}
/**
* A hyperlink that opens up a line of source code in an editor. It searchs all project source folders.
*/
class SourceCodeHyperlink implements IHyperlink {
private String code;
private String projectName;
public SourceCodeHyperlink( String code, String projectName ) {
this.code = code;
this.projectName = projectName;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IHyperlink#linkActivated()
*/
public void linkActivated() {
int lastColon = code.lastIndexOf( ':' );
int secondLastColon = code.lastIndexOf( ':', lastColon - 1 );
String file = code.substring( 0, secondLastColon );
int lineNumber = Integer.parseInt( code.substring( secondLastColon + 1, lastColon ) );
IPath filePath = new Path( file );
log.debug( "Jumping to " + filePath.toOSString() + " on line " + lineNumber ); //$NON-NLS-1$ //$NON-NLS-2$
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject( projectName );
IResource resource = ProjectUtils.getResource( project, filePath.toFile() );
try {
if( resource instanceof IFile ) {
IFile input = (IFile) resource;
IWorkbenchWindow workbenchWindow = ContextManager.getActiveWorkbenchWindow();
IWorkbenchPage workbenchPage = workbenchWindow.getActivePage();
// open the source file in an editor
IEditorPart editorPart = IDE.openEditor( workbenchPage, input );
if( editorPart instanceof ITextEditor ) {
ITextEditor textEditor = (ITextEditor) editorPart;
IDocument document = textEditor.getDocumentProvider().getDocument( textEditor.getEditorInput() );
// get IRegion instance of the line
IRegion region = document.getLineInformation( lineNumber - 1 );
// highlight line
textEditor.setHighlightRange( region.getOffset(), region.getLength(), true );
}
}
} catch( CoreException e ) {
log.error( e.getMessage(), e );
} catch( BadLocationException e ) {
log.error( e.getMessage(), e );
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IHyperlink#linkEntered()
*/
public void linkEntered() {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.console.IHyperlink#linkExited()
*/
public void linkExited() {
}
}
public static void updateProjectPattern() {
String pattern = ""; //$NON-NLS-1$
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject[] projects = workspaceRoot.getProjects();
String addenum = null;
IProject project = null;
Pattern metaCharSearch = Pattern.compile( "[\\Q([{\\^-$|]})?*+.\\E]" ); //$NON-NLS-1$
StringBuffer buf = new StringBuffer();
for( int i = 0; i < projects.length; i++ ) {
project = projects[ i ];
if( metaCharSearch.matcher( project.getName() ).find() ) {
// Project name contains Pattern meta characters
addenum = Pattern.quote( project.getName() ) + "|"; //$NON-NLS-1$
} else {
addenum = project.getName() + "|"; //$NON-NLS-1$
}
buf.append( addenum );
}
pattern = buf.toString();
if( pattern.length() == 0 ) {
// Only the beginning of the group was captured
projectPattern = null;
} else {
pattern = "\\b(" + pattern.substring( 0, pattern.length() - 1 ) + ")\\b"; //$NON-NLS-1$ //$NON-NLS-2$
projectPattern = Pattern.compile( pattern );
}
}
}