/*******************************************************************************
* Copyright (c) 2015 Bruno Medeiros and other Contributors.
* 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:
* Bruno Medeiros - initial API and implementation
*******************************************************************************/
package melnorme.lang.ide.ui.editor;
import static melnorme.lang.ide.core.utils.ResourceUtils.getWorkspaceRoot;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import java.util.Iterator;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.ui.texteditor.MarkerUtilities;
import melnorme.lang.ide.core.utils.ResourceUtils;
import melnorme.utilbox.core.CommonException;
import melnorme.utilbox.misc.Location;
import melnorme.utilbox.ownership.LifecycleObject;
public class ExternalBreakpointWatcher extends LifecycleObject {
protected static final String CBREAKPOINT_MARKER_TYPE = "org.eclipse.cdt.debug.core.cLineBreakpointMarker";
// ICLineBreakpoint2.REQUESTED_SOURCE_HANDLE
protected static final String ATTRIBUTE_SOURCE_HANDLE = "requestedSourceHandle";
protected final Location location;
protected final IDocument document;
protected final IAnnotationModel annotationModel;
protected final IAnnotationModelExtension annotationModelExt;
public ExternalBreakpointWatcher(IEditorInput input, IDocument document, IAnnotationModel annotationModel) {
this.location = EditorUtils.getLocationOrNull(input);
this.document = assertNotNull(document);
this.annotationModel = assertNotNull(annotationModel);
if(annotationModel instanceof IAnnotationModelExtension) {
this.annotationModelExt = (IAnnotationModelExtension) annotationModel;
} else {
this.annotationModelExt = null;
}
if(location != null) {
ResourceUtils.connectResourceListener(resourceListener, this::initializeFromResources,
getWorkspaceRoot(), owned);
}
}
protected void initializeFromResources() throws CoreException {
IMarker[] findMarkers = getWorkspaceRoot().findMarkers(CBREAKPOINT_MARKER_TYPE, true, 0);
for (IMarker marker : findMarkers) {
addMarkerAnnotation(marker);
}
}
protected final IResourceChangeListener resourceListener = new IResourceChangeListener() {
@Override
public void resourceChanged(IResourceChangeEvent event) {
IResourceDelta delta = event.getDelta();
if(delta == null) {
return;
}
IResource res = delta.getResource();
if(res instanceof IWorkspaceRoot && (delta.getFlags() & IResourceDelta.MARKERS) != 0) {
IMarkerDelta[] markerDeltas = delta.getMarkerDeltas();
for (IMarkerDelta markerDelta : markerDeltas) {
int kind = markerDelta.getKind();
if(markerDelta.isSubtypeOf(CBREAKPOINT_MARKER_TYPE)) {
if (kind == IResourceDelta.ADDED) {
addMarkerAnnotation(markerDelta.getMarker());
}
if (kind == IResourceDelta.REMOVED) {
removeMarkerAnnotation(markerDelta.getMarker());
}
if (kind == IResourceDelta.CHANGED) {
updateMarkerAnnotation(markerDelta.getMarker());
}
}
}
}
}
};
protected void addMarkerAnnotation(IMarker marker) {
try {
String sourceHandle = marker.getAttribute(ATTRIBUTE_SOURCE_HANDLE, null);
if(sourceHandle == null) {
return;
}
Location markerLocation = Location.create(sourceHandle);
if(!markerLocation.equals(location)) {
return;
}
} catch(CommonException e) {
return;
}
int markerOffset;
try {
markerOffset = getMarkerPosition(marker);
} catch(BadLocationException e) {
return;
}
Position position = new Position(markerOffset, 0);
Annotation annotation = new MarkerAnnotation(marker);
annotationModel.addAnnotation(annotation, position);
}
protected int getMarkerPosition(IMarker marker) throws BadLocationException {
int line = MarkerUtilities.getLineNumber(marker);
if (line > 0) {
return document.getLineOffset(line - 1);
}
throw new BadLocationException();
}
protected void removeMarkerAnnotation(IMarker marker) {
Iterator<Annotation> iter = annotationModel.getAnnotationIterator();
for (Annotation ann : (Iterable<Annotation>) () -> iter) {
if(ann instanceof MarkerAnnotation) {
MarkerAnnotation markerAnnotation = (MarkerAnnotation) ann;
if(markerAnnotation.getMarker().equals(marker)) {
annotationModel.removeAnnotation(markerAnnotation);
return;
}
}
}
}
protected void updateMarkerAnnotation(IMarker marker) {
Iterator<Annotation> iter = annotationModel.getAnnotationIterator();
for (Annotation ann : (Iterable<Annotation>) () -> iter) {
if(ann instanceof MarkerAnnotation) {
MarkerAnnotation markerAnnotation = (MarkerAnnotation) ann;
if(markerAnnotation.getMarker().equals(marker)) {
Position position = annotationModel.getPosition(markerAnnotation);
// Trigger a model update.
annotationModelExt.modifyAnnotationPosition(markerAnnotation, position);
return;
}
}
}
}
}