/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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 com.google.dart.tools.debug.ui.internal.presentation;
import com.google.dart.tools.debug.core.breakpoints.DartBreakpoint;
import com.google.dart.tools.debug.core.dartium.DartiumDebugValue;
import com.google.dart.tools.debug.core.server.ServerDebugValue;
import com.google.dart.tools.debug.core.source.DartNoSourceFoundElement;
import com.google.dart.tools.debug.core.util.DebuggerUtils;
import com.google.dart.tools.debug.core.util.IDartDebugVariable;
import com.google.dart.tools.debug.core.util.IDartStackFrame;
import com.google.dart.tools.debug.core.util.IExceptionStackFrame;
import com.google.dart.tools.debug.ui.internal.DartDebugUIPlugin;
import com.google.dart.tools.debug.ui.internal.DartUtil;
import com.google.dart.tools.debug.ui.internal.util.DebuggerEditorInput;
import com.google.dart.tools.ui.internal.viewsupport.DartElementImageProvider;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.ILineBreakpoint;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IInstructionPointerPresentation;
import org.eclipse.debug.ui.IValueDetailListener;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import java.net.URI;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* A debug model presentation is responsible for providing labels, images, and editors associated
* with debug elements in a specific debug model.
*/
public class DartDebugModelPresentation implements IDebugModelPresentation,
IInstructionPointerPresentation {
private static final String DART_EDITOR_ID = "com.google.dart.tools.ui.text.editor.CompilationUnitEditor";
private static final String BREAK_ON_EXCEPTION_ANNOTAION = "org.eclipse.debug.ui.currentIPEx";
private List<ILabelProviderListener> listeners = new ArrayList<ILabelProviderListener>();
public DartDebugModelPresentation() {
}
@Override
public void addListener(ILabelProviderListener listener) {
listeners.add(listener);
}
/**
* Computes a detailed description of the given value, reporting the result to the specified
* listener. This allows a presentation to provide extra details about a selected value in the
* variable detail portion of the variables view. Since this can be a long-running operation, the
* details are reported back to the specified listener asynchronously. If <code>null</code> is
* reported, the value's value string is displayed (<code>IValue.getValueString()</code>).
*
* @param value the value for which a detailed description is required
* @param listener the listener to report the details to asynchronously
*/
@Override
public void computeDetail(final IValue value, final IValueDetailListener listener) {
if (value instanceof DartiumDebugValue) {
DartiumDebugValue debugValue = (DartiumDebugValue) value;
debugValue.computeDetail(new DartiumDebugValue.ValueCallback() {
@Override
public void detailComputed(String stringValue) {
listener.detailComputed(value, stringValue);
}
});
} else if (value instanceof ServerDebugValue) {
ServerDebugValue debugValue = (ServerDebugValue) value;
debugValue.computeDetail(new DartiumDebugValue.ValueCallback() {
@Override
public void detailComputed(String stringValue) {
listener.detailComputed(value, stringValue);
}
});
} else {
listener.detailComputed(value, null);
}
}
@Override
public void dispose() {
}
@Override
public String getEditorId(IEditorInput input, Object element) {
IFile file = null;
if (element instanceof IFile) {
file = (IFile) element;
}
if (element instanceof ILineBreakpoint) {
IResource resource = ((ILineBreakpoint) element).getMarker().getResource();
if (resource instanceof IFile) {
file = (IFile) resource;
}
}
if (file != null) {
try {
return IDE.getEditorDescriptor(file).getId();
} catch (PartInitException e) {
}
}
if (element instanceof LocalFileStorage) {
return DART_EDITOR_ID;
}
if (input instanceof DartSourceNotFoundEditorInput) {
return DartSourceNotFoundEditor.EDITOR_ID;
}
return null;
}
@Override
public IEditorInput getEditorInput(Object element) {
if (element instanceof IFile) {
return new FileEditorInput((IFile) element);
}
if (element instanceof DartBreakpoint) {
IFile file = ((DartBreakpoint) element).getFile();
if (file != null) {
return new FileEditorInput(file);
}
return new FileStoreEditorInput(EFS.getLocalFileSystem().getStore(
new Path(((DartBreakpoint) element).getFilePath())));
}
if (element instanceof LocalFileStorage) {
try {
URI fileUri = ((LocalFileStorage) element).getFile().toURI();
return new DebuggerEditorInput(EFS.getStore(fileUri));
} catch (CoreException e) {
DartUtil.logError(e);
}
}
if (element instanceof DartNoSourceFoundElement) {
return new DartSourceNotFoundEditorInput((DartNoSourceFoundElement) element);
}
return null;
}
public String getFormattedValueText(IValue value) throws DebugException {
String valueString = null;
if (value instanceof DartiumDebugValue) {
DartiumDebugValue dartiumValue = (DartiumDebugValue) value;
valueString = getValueText(dartiumValue);
} else if (value instanceof ServerDebugValue) {
ServerDebugValue debugValue = (ServerDebugValue) value;
valueString = debugValue.getDetailValue();
} else if (value != null) {
valueString = value.getValueString();
if (valueString == null) {
valueString = "<unknown value>";
}
}
if (valueString == null) {
valueString = "<unknown value>";
}
return valueString;
}
/**
* This method allows us to customize images for Dart objects that are displayed in the debugger.
*/
@Override
public Image getImage(Object element) {
try {
if (element instanceof IDartDebugVariable) {
IDartDebugVariable variable = (IDartDebugVariable) element;
if (variable.isThrownException()) {
return DartDebugUIPlugin.getImage("obj16/object_exception.png");
} else if (variable.isThisObject()) {
return DartDebugUIPlugin.getImage("obj16/object_this.png");
} else if (variable.isLibraryObject()) {
return DartDebugUIPlugin.getImage("obj16/object_library.png");
} else if (variable.isStatic()) {
return DartDebugUIPlugin.getImage("obj16/object_static.png");
} else if (variable.isLocal()) {
return DartDebugUIPlugin.getImage("obj16/object_local.gif");
} else {
return DartDebugUIPlugin.getImage("obj16/object_obj.png");
}
} else if (element instanceof IDartStackFrame) {
IDartStackFrame frame = (IDartStackFrame) element;
Image image = DartDebugUIPlugin.getImage(DartElementImageProvider.getMethodImageDescriptor(
false,
frame.isPrivate()));
if (frame.isUsingSourceMaps()) {
DecorationOverlayIcon overlayDescriptor = new DecorationOverlayIcon(
image,
DartDebugUIPlugin.getImageDescriptor("ovr16/mapped.png"),
IDecoration.BOTTOM_RIGHT);
image = DartDebugUIPlugin.getImage(overlayDescriptor);
}
return image;
} else if (element instanceof DartBreakpoint) {
return getBreakpointImage((DartBreakpoint) element);
} else {
return null;
}
} catch (Throwable t) {
DartDebugUIPlugin.logError(t);
return null;
}
}
@Override
public Annotation getInstructionPointerAnnotation(IEditorPart editorPart, IStackFrame frame) {
return null;
}
@Override
public String getInstructionPointerAnnotationType(IEditorPart editorPart, IStackFrame frame) {
if (frame instanceof IExceptionStackFrame) {
IExceptionStackFrame f = (IExceptionStackFrame) frame;
if (f.hasException()) {
return BREAK_ON_EXCEPTION_ANNOTAION;
}
}
return null;
}
@Override
public Image getInstructionPointerImage(IEditorPart editorPart, IStackFrame frame) {
if (frame instanceof IExceptionStackFrame) {
IExceptionStackFrame f = (IExceptionStackFrame) frame;
if (f.hasException()) {
return DartDebugUIPlugin.getImage("obj16/inst_ptr_exception.png");
}
}
try {
IStackFrame topOfStack = frame.getThread().getTopStackFrame();
if (frame.equals(topOfStack)) {
return DartDebugUIPlugin.getImage("obj16/inst_ptr_current.png");
}
} catch (DebugException de) {
}
return DartDebugUIPlugin.getImage("obj16/inst_ptr_normal.png");
}
@Override
public String getInstructionPointerText(IEditorPart editorPart, IStackFrame frame) {
if (frame instanceof IExceptionStackFrame) {
IExceptionStackFrame f = (IExceptionStackFrame) frame;
if (f.hasException()) {
try {
return f.getExceptionDisplayText();
} catch (DebugException e) {
DartUtil.logError(e);
}
}
}
return null;
}
@Override
public String getText(Object element) {
if (element instanceof DartBreakpoint) {
DartBreakpoint bp = (DartBreakpoint) element;
return getBreakpointText(bp);
}
return null;
}
public String getVariableText(IVariable var) {
try {
StringBuffer buff = new StringBuffer();
buff.append(var.getName());
IValue value = var.getValue();
String valueString = getFormattedValueText(value);
if (valueString.length() != 0) {
buff.append(" = ");
buff.append(valueString);
}
return buff.toString();
} catch (DebugException e) {
return null;
}
}
@Override
public boolean isLabelProperty(Object element, String property) {
return false;
}
@Override
public void removeListener(ILabelProviderListener listener) {
listeners.remove(listener);
}
@Override
public void setAttribute(String attribute, Object value) {
}
/**
* Return a textual description of the breakpoint. It looks something like:
* <p>
* <code>project-name, path/to/file.dart, line 123, 'text.of.line();'</code>
*
* @param bp
* @return
*/
protected String getBreakpointText(DartBreakpoint bp) {
String text = "";
IFile file = bp.getFile();
if (file != null) {
text = file.getProject().getName() + ", " + file.getProjectRelativePath().toPortableString()
+ ", line " + NumberFormat.getNumberInstance().format(bp.getLine());
} else {
text = bp.getName() + ", line " + NumberFormat.getNumberInstance().format(bp.getLine());
}
String lineInfo = getLineExtract(bp);
if (lineInfo != null) {
text = text + ", '" + lineInfo + "'";
}
return text;
}
/**
* Build the text for an {@link DartiumDebugValue}. This can be a long running call since we wait
* for the toString call to get back with the value.
*/
protected String getValueText(DartiumDebugValue value) throws DebugException {
boolean isPrimitive = value.isPrimitive();
boolean isArray = value.isListValue();
final String valueString[] = new String[1];
if (!isPrimitive) {
final CountDownLatch latch = new CountDownLatch(1);
computeDetail(value, new IValueDetailListener() {
@Override
public void detailComputed(IValue value, String result) {
valueString[0] = result;
latch.countDown();
}
});
try {
latch.await(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return null;
}
if (isArray) {
valueString[0] = "[" + valueString[0] + "]";
}
return valueString[0];
} else {
return value.getDisplayString();
}
}
private Image getBreakpointImage(DartBreakpoint bp) {
if (bp.isBreakpointEnabled()) {
return DartDebugUIPlugin.getImage("obj16/brkp_obj.png");
} else {
return DartDebugUIPlugin.getImage("obj16/brkpd_obj.png");
}
}
private String getLineExtract(DartBreakpoint bp) {
if (bp == null) {
return null;
}
return DebuggerUtils.extractFileLine(bp.getContents(), bp.getCharset(), bp.getLine() - 1);
}
}