/*
* Copyright 2003-2009 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.debug.ui;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.eclipse.core.GroovyCore;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaLineBreakpoint;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.ui.texteditor.IMarkerUpdater;
import org.eclipse.ui.texteditor.MarkerUtilities;
/**
* Largely borrowed from {@link BreakpointMarkerUpdater}
*
* @author Andrew Eisenberg
* @created Oct 6, 2009
*
*/
public class BreakpointUpdater implements IMarkerUpdater {
public String[] getAttribute() {
return new String[] {IMarker.LINE_NUMBER};
}
public String getMarkerType() {
return "org.eclipse.debug.core.breakpointMarker"; //$NON-NLS-1$
}
public boolean updateMarker(IMarker marker, IDocument document,
Position position) {
GroovyCompilationUnit unit = getCompilationUnit(marker);
if (unit == null) {
// ignore non-GroovyCompilationUnits
return true;
}
if(position.isDeleted()) {
return false;
}
IBreakpointManager manager = DebugPlugin.getDefault().getBreakpointManager();
IBreakpoint breakpoint = manager.getBreakpoint(marker);
if(breakpoint == null) {
return false;
}
try {
Object attribute = marker.getAttribute(IMarker.LINE_NUMBER);
if (attribute != null) {
ValidBreakpointLocationFinder finder = new ValidBreakpointLocationFinder(
((Integer) attribute).intValue());
ASTNode validNode = finder.findValidBreakpointLocation(unit.getModuleNode());
if (validNode == null) {
return false;
}
int line = validNode.getLineNumber();
MarkerUtilities.setLineNumber(marker, line);
if(isLineBreakpoint(marker)) {
ensureRanges(document, marker, line);
return lineBreakpointExists(marker.getResource(), ((IJavaLineBreakpoint)breakpoint).getTypeName(), line, marker) == null;
}
}
return true;
} catch (CoreException e) {
GroovyCore.logException("Error updating breakpoint", e);
return false;
} catch (BadLocationException e) {
GroovyCore.logException("Error updating breakpoint", e);
return false;
}
}
/**
* Finds the groovy compilation unit associated with this marker's resource.
* Tries to get a working copy first, or otherwise creates a new compilation unit
* @param marker
* @return {@link GroovyCompilationUnit} associated with this marker's resource or
* null if none exists.
*/
private GroovyCompilationUnit getCompilationUnit(IMarker marker) {
IResource resource = marker.getResource();
if (!Util.isJavaLikeFileName(resource.getName())) {
return null;
}
ICompilationUnit unit = JavaPlugin.getDefault().getCompilationUnitDocumentProvider().getWorkingCopy(resource);
if (unit == null && resource.getType() == IResource.FILE) {
// nope...must create from new
unit = JavaCore.createCompilationUnitFrom((IFile) resource);
}
if (unit != null && unit instanceof GroovyCompilationUnit) {
return (GroovyCompilationUnit) unit;
} else {
return null;
}
}
/**
* Updates the charstart and charend ranges if necessary for the given line.
* Returns immediately if the line is not valid (< 0 or greater than the total line number count)
* @param document
* @param marker
* @param line
* @throws BadLocationException
*/
private void ensureRanges(IDocument document, IMarker marker, int line) throws BadLocationException {
if(line < 0 || line > document.getNumberOfLines()) {
return;
}
IRegion region = document.getLineInformation(line - 1);
int charstart = region.getOffset();
int charend = charstart + region.getLength();
MarkerUtilities.setCharStart(marker, charstart);
MarkerUtilities.setCharEnd(marker, charend);
}
/**
* Returns if the specified marker is for an <code>IJavaLineBreakpoint</code>
* @param marker
* @return true if the marker is for an <code>IJavalineBreakpoint</code>, false otherwise
*
* @since 3.4
*/
private boolean isLineBreakpoint(IMarker marker) {
return MarkerUtilities.isMarkerType(marker, "org.eclipse.jdt.debug.javaLineBreakpointMarker"); //$NON-NLS-1$
}
/**
* Searches for an existing line breakpoint on the specified line in the current type that does not match the id of the specified marker
* @param resource the resource to care about
* @param typeName the name of the type the breakpoint is in
* @param lineNumber the number of the line the breakpoint is on
* @param currentmarker the current marker we are comparing to see if it will be moved onto an existing one
* @return an existing line breakpoint on the current line of the given resource and type if there is one
* @throws CoreException
*
* @since 3.4
*/
private IJavaLineBreakpoint lineBreakpointExists(IResource resource, String typeName, int lineNumber, IMarker currentmarker) throws CoreException {
String modelId = JDIDebugPlugin.getUniqueIdentifier();
String markerType= JavaLineBreakpoint.getMarkerType();
IBreakpointManager manager= DebugPlugin.getDefault().getBreakpointManager();
IBreakpoint[] breakpoints= manager.getBreakpoints(modelId);
for (int i = 0; i < breakpoints.length; i++) {
if (!(breakpoints[i] instanceof IJavaLineBreakpoint)) {
continue;
}
IJavaLineBreakpoint breakpoint = (IJavaLineBreakpoint) breakpoints[i];
IMarker marker = breakpoint.getMarker();
if (marker != null && marker.exists() && marker.getType().equals(markerType) && currentmarker.getId() != marker.getId()) {
String breakpointTypeName = breakpoint.getTypeName();
if ((breakpointTypeName.equals(typeName) || breakpointTypeName.startsWith(typeName + '$')) &&
breakpoint.getLineNumber() == lineNumber &&
resource.equals(marker.getResource())) {
return breakpoint;
}
}
}
return null;
}
}