/*******************************************************************************
* Copyright (c) 2010, 2016 Wind River Systems, Inc. and others.
* 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.cdt.ui.hover;
import java.util.Map;
import org.eclipse.cdt.debug.ui.editors.AbstractDebugTextHover;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHoverExtension2;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tcf.internal.debug.model.TCFContextState;
import org.eclipse.tcf.internal.debug.ui.model.TCFChildren;
import org.eclipse.tcf.internal.debug.ui.model.TCFChildrenStackTrace;
import org.eclipse.tcf.internal.debug.ui.model.TCFNode;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExpression;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeStackFrame;
import org.eclipse.tcf.internal.debug.ui.model.TCFNumberFormat;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.services.IExpressions;
import org.eclipse.tcf.services.IExpressions.DoneCreate;
import org.eclipse.tcf.services.IExpressions.DoneDispose;
import org.eclipse.tcf.services.IExpressions.DoneEvaluate;
import org.eclipse.tcf.services.IExpressions.Expression;
import org.eclipse.tcf.services.IExpressions.Value;
import org.eclipse.tcf.util.TCFDataCache;
import org.eclipse.tcf.util.TCFTask;
/**
* TCF implementation of debug expression hover for the C/C++ Editor.
*/
public class TCFDebugTextHover extends AbstractDebugTextHover implements ITextHoverExtension2 {
private TCFNode node;
private boolean running;
@Override
public IInformationControlCreator getHoverControlCreator() {
if (useExpressionExplorer()) {
return createExpressionInformationControlCreator();
}
else {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent, false);
}
};
}
}
private IInformationControlCreator createExpressionInformationControlCreator() {
return new ExpressionInformationControlCreator();
}
protected boolean useExpressionExplorer() {
return true;
}
private boolean getNode(TCFNode selection, Runnable done) {
node = null;
running = false;
TCFNodeStackFrame frame = null;
TCFNodeExecContext exe = null;
if (selection instanceof TCFNodeStackFrame) {
frame = (TCFNodeStackFrame)selection;
exe = (TCFNodeExecContext)frame.getParent();
}
else if (selection instanceof TCFNodeExecContext) {
exe = (TCFNodeExecContext)selection;
}
else {
return true;
}
TCFDataCache<TCFContextState> state_cache = exe.getState();
if (!state_cache.validate(done)) return false;
TCFContextState state_data = state_cache.getData();
if (state_data == null || !state_data.is_suspended) {
if (exe.getModel().getHoverWhileRunning()) {
running = true;
node = exe;
}
}
else {
if (frame == null) {
TCFChildrenStackTrace stack = exe.getStackTrace();
if (!stack.validate(done)) return false;
frame = stack.getTopFrame();
}
if (frame != null && !frame.isEmulated()) node = frame;
}
return true;
}
@Override
protected boolean canEvaluate() {
IAdaptable context = getSelectionAdaptable();
if (context == null) return false;
final TCFNode selection = (TCFNode)context.getAdapter(TCFNode.class);
if (selection == null) return false;
try {
return new TCFTask<Boolean>(selection.getChannel()) {
public void run() {
if (!getNode(selection, this)) return;
done(node != null);
}
}.get();
}
catch (Exception x) {
// Problem in Eclipse 3.7:
// TextViewerHoverManager calls Thread.interrupt(),
// but it fails to handle InterruptedException.
// We have to catch and ignore the exception.
}
return false;
}
public Object getHoverInfo2(ITextViewer viewer, IRegion region) {
if (!useExpressionExplorer()) return getHoverInfo(viewer, region);
IAdaptable context = getSelectionAdaptable();
if (context == null) return null;
final TCFNode selection = (TCFNode)context.getAdapter(TCFNode.class);
if (selection == null) return null;
final String text = getExpressionText(viewer, region);
if (text == null || text.length() == 0) return null;
try {
return new TCFTask<Object>(selection.getChannel()) {
public void run() {
if (!getNode(selection, this)) return;
if (node != null) {
TCFChildren cache = node.getModel().getHoverExpressionCache(node, text);
if (!cache.validate(this)) return;
Map<String,TCFNode> nodes = cache.getData();
if (nodes != null) {
boolean ok = false;
for (TCFNode n : nodes.values()) {
TCFNodeExpression expr = (TCFNodeExpression)n;
if (running) expr.update(this);
TCFDataCache<IExpressions.Value> value = expr.getValue();
if (!value.validate(this)) return;
if (value.getData() != null) ok = true;
}
if (ok) {
done(node);
return;
}
}
}
done(null);
}
}.get();
}
catch (Exception x) {
// Problem in Eclipse 3.7:
// TextViewerHoverManager calls Thread.interrupt(),
// but it fails to handle InterruptedException.
// We have to catch and ignore the exception.
return null;
}
}
@Override
protected String evaluateExpression(final String expression) {
IAdaptable context = getSelectionAdaptable();
if (context == null) return null;
final TCFNode selection = (TCFNode)context.getAdapter(TCFNode.class);
if (selection == null) return null;
final IChannel channel = selection.getChannel();
return new TCFTask<String>(channel) {
public void run() {
final IExpressions service = channel.getRemoteService(IExpressions.class);
if (!getNode(selection, this)) return;
if (service != null && node != null) {
service.create(node.getID(), null, expression, new DoneCreate() {
public void doneCreate(IToken token, Exception error, final Expression context) {
if (error == null) {
service.evaluate(context.getID(), new DoneEvaluate() {
public void doneEvaluate(IToken token, Exception error, Value value) {
done(error == null && value != null ? getValueText(value) : null);
service.dispose(context.getID(), new DoneDispose() {
public void doneDispose(IToken token, Exception error) {
// no-op
}
});
}
});
}
else {
done(null);
}
}
});
}
else {
done(null);
}
}
}.getE();
}
private static String getValueText(Value value) {
byte[] data = value.getValue();
if (data == null) return "N/A";
switch(value.getTypeClass()) {
case integer:
return TCFNumberFormat.toBigInteger(data, value.isBigEndian(), true).toString();
case real:
return TCFNumberFormat.toFPString(data, value.isBigEndian());
case complex:
return TCFNumberFormat.toComplexFPString(data, value.isBigEndian());
default:
return "0x" + TCFNumberFormat.toBigInteger(data, value.isBigEndian(), false).toString(16);
}
}
}