/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on 11/09/2005
*/
package org.python.pydev.builder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.python.pydev.core.ArrayUtils;
import org.python.pydev.core.log.Log;
import com.aptana.shared_core.callbacks.ICallback;
import com.aptana.shared_core.string.FastStringBuffer;
/**
* Helper class to deal with markers.
*
* It's main use is to replace the markers in a given resource for another set of markers.
*
* @author Fabio
*/
public class PydevMarkerUtils {
/**
* This class represents the information to create a marker.
*
* @author Fabio
*/
public static class MarkerInfo {
public IDocument doc;
public String message;
public String markerType;
public int severity;
public boolean userEditable;
public boolean isTransient;
public int lineStart;
public int colStart;
public int lineEnd;
public int absoluteStart = -1;
public int absoluteEnd = -1;
public int colEnd;
public Map<String, Object> additionalInfo;
/**
* Constructor passing lines and relative positions
*/
public MarkerInfo(IDocument doc, String message, String markerType, int severity, boolean userEditable,
boolean isTransient, int lineStart, int colStart, int lineEnd, int colEnd,
Map<String, Object> additionalInfo) {
super();
this.doc = doc;
this.message = message;
this.markerType = markerType;
this.severity = severity;
this.userEditable = userEditable;
this.isTransient = isTransient;
this.lineStart = lineStart;
this.colStart = colStart;
this.lineEnd = lineEnd;
this.colEnd = colEnd;
this.additionalInfo = additionalInfo;
}
/**
* Constructor passing absolute position
*/
public MarkerInfo(IDocument doc, String message, String markerType, int severity, boolean userEditable,
boolean isTransient, int line, int absoluteStart, int absoluteEnd, Map<String, Object> additionalInfo) {
super();
this.doc = doc;
this.message = message;
this.markerType = markerType;
this.severity = severity;
this.userEditable = userEditable;
this.isTransient = isTransient;
this.lineStart = line;
this.lineEnd = line;
this.absoluteStart = absoluteStart;
this.absoluteEnd = absoluteEnd;
this.additionalInfo = additionalInfo;
}
/**
* @return a map with the properties to be set in the marker or null if some error happened while doing it.
* @throws BadLocationException
*/
private HashMap<String, Object> getAsMap() throws BadLocationException {
if (lineStart < 0) {
lineStart = 0;
}
if (absoluteStart == -1 || absoluteEnd == -1) {
//if the absolute wasn't specified, let's calculate it
IRegion start;
try {
start = doc.getLineInformation(lineStart);
} catch (BadLocationException e) {
//Don't log it. Just return null -- this happens because there's a delay from calculating things
//to actually using them and the document might have changed and the given line is no longer available
//(a new request should fix this)
return null;
} catch (Exception e) {
Log.log(IStatus.ERROR, "Could not get line: " + lineStart + " to add message: " + message, e);
return null;
}
try {
absoluteStart = start.getOffset() + colStart;
if (lineEnd >= 0 && colEnd >= 0) {
IRegion end = doc.getLineInformation(lineEnd);
absoluteEnd = end.getOffset() + colEnd;
} else {
//ok, we have to calculate it based on the line contents...
String line = doc.get(start.getOffset(), start.getLength());
int i;
FastStringBuffer buffer;
if ((i = line.indexOf('#')) != -1) {
buffer = new FastStringBuffer(line.substring(0, i), 0);
} else {
buffer = new FastStringBuffer(line, 0);
}
while (buffer.length() > 0 && Character.isWhitespace(buffer.lastChar())) {
buffer.deleteLast();
}
absoluteEnd = start.getOffset() + buffer.length();
}
} catch (BadLocationException e) {
//Don't log it. Just return null -- this happens because there's a delay from calculating things
//to actually using them and the document might have changed and the given line is no longer available
//(a new request should fix this and create the markers correctly because of the change in the document)
return null;
} catch (Exception e) {
Log.log(IStatus.INFO, "Problem creating map for:" + this.toString(), e);
return null;
}
}
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(IMarker.MESSAGE, message);
map.put(IMarker.LINE_NUMBER, lineStart);
map.put(IMarker.CHAR_START, absoluteStart);
map.put(IMarker.CHAR_END, absoluteEnd);
map.put(IMarker.SEVERITY, severity);
map.put(IMarker.USER_EDITABLE, userEditable);
map.put(IMarker.TRANSIENT, isTransient);
if (additionalInfo != null) {
map.putAll(additionalInfo);
}
return map;
}
/**
* Constructs a <code>String</code> with all attributes
* in name = value format.
*
* @return a <code>String</code> representation
* of this object.
*/
public String toString() {
final String NL = "\n";
StringBuffer retValue = new StringBuffer();
retValue.append("MarkerInfo (\n").append("doc = ").append(this.doc).append(NL).append("message = ")
.append(this.message).append(NL).append("markerType = ").append(this.markerType).append(NL)
.append("severity = ").append(this.severity).append(NL).append("userEditable = ")
.append(this.userEditable).append(NL).append("isTransient = ").append(this.isTransient).append(NL)
.append("lineStart = ").append(this.lineStart).append(NL).append("colStart = ")
.append(this.colStart).append(NL).append("lineEnd = ").append(this.lineEnd).append(NL)
.append("absoluteStart = ").append(this.absoluteStart).append(NL).append("absoluteEnd = ")
.append(this.absoluteEnd).append(NL).append("colEnd = ").append(this.colEnd).append(NL)
.append("additionalInfo = ").append(this.additionalInfo).append(NL).append(")");
return retValue.toString();
}
}
/**
* This method allows clients to replace the existing markers of some type in a given resource for other markers.
*
* @param lst the new markers to be set in the resource
* @param resource the resource were the markers should be replaced
* @param markerType the type of the marker that'll be replaced
* @param removeUserEditable if true, will remove the user-editable markers too (otherwise, will leave the user-editable markers)
* @param monitor used to check whether this process should be canceled.
*/
public static void replaceMarkers(final List<MarkerInfo> lst, final IResource resource, final String markerType,
final boolean removeUserEditable, IProgressMonitor monitor) {
IWorkspaceRunnable r = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
if (!resource.exists()) {
return;
}
try {
if (removeUserEditable) {
resource.deleteMarkers(markerType, true, IResource.DEPTH_ZERO);
} else {
IMarker[] existingMarkers;
existingMarkers = resource.findMarkers(markerType, false, IResource.DEPTH_ZERO);
//we don't want to remove the user-editable markers, so, let's filter them out!
existingMarkers = ArrayUtils.filter(existingMarkers, new ICallback<Boolean, IMarker>() {
public Boolean call(IMarker marker) {
//if it's user-editable, it should not be included in the list
return !marker.getAttribute(IMarker.USER_EDITABLE, true); //default for user-editable is true.
}
}).toArray(new IMarker[0]);
ResourcesPlugin.getWorkspace().deleteMarkers(existingMarkers);
}
} catch (Exception e1) {
Log.log(e1);
}
try {
for (MarkerInfo markerInfo : lst) {
HashMap<String, Object> asMap = markerInfo.getAsMap();
IMarker marker = resource.createMarker(markerType);
marker.setAttributes(asMap);
}
} catch (Exception e) {
Log.log(e);
}
}
};
try {
resource.getWorkspace().run(r, ResourcesPlugin.getWorkspace().getRuleFactory().markerRule(resource),
IWorkspace.AVOID_UPDATE, monitor);
} catch (Exception e) {
Log.log(e);
}
}
/**
* @param original
* @param pydevCoverageMarker
*/
public static void removeMarkers(IResource resource, String markerType) {
if (resource == null) {
return;
}
try {
resource.deleteMarkers(markerType, false, IResource.DEPTH_ZERO);
} catch (Exception e) {
Log.log(e);
}
}
}