package org.intellij.plugins.xsltDebugger.impl; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ColoredTextContainer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.PlatformIcons; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import com.intellij.xdebugger.frame.*; import org.intellij.plugins.xsltDebugger.VMPausedException; import org.intellij.plugins.xsltDebugger.XsltDebuggerSession; import org.intellij.plugins.xsltDebugger.rt.engine.Debugger; import org.intellij.plugins.xsltDebugger.rt.engine.DebuggerStoppedException; import org.intellij.plugins.xsltDebugger.rt.engine.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.net.URI; import java.util.List; public class XsltStackFrame extends XStackFrame { private final Debugger.Frame myFrame; private final XsltDebuggerSession myDebuggerSession; private final XSourcePosition myPosition; public XsltStackFrame(Debugger.Frame frame, XsltDebuggerSession debuggerSession) { myFrame = frame; myDebuggerSession = debuggerSession; myPosition = XsltSourcePosition.create(frame); } @Override public Object getEqualityObject() { return XsltStackFrame.class; } @Override public XDebuggerEvaluator getEvaluator() { return myFrame instanceof Debugger.StyleFrame ? new MyEvaluator((Debugger.StyleFrame)myFrame) : null; } @Override public XSourcePosition getSourcePosition() { return myPosition; } @Override public void customizePresentation(@NotNull ColoredTextContainer component) { if (myDebuggerSession.getCurrentState() == Debugger.State.SUSPENDED) { try { _customizePresentation(component); } catch (VMPausedException | DebuggerStoppedException ignore) { } } } private void _customizePresentation(ColoredTextContainer component) { final Debugger.Frame frame = myFrame; if (frame instanceof Debugger.StyleFrame) { component.append(((Debugger.StyleFrame)frame).getInstruction(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); } else if (frame instanceof Debugger.SourceFrame) { component.append(((Debugger.SourceFrame)frame).getXPath(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); } component.append(" ", SimpleTextAttributes.REGULAR_ATTRIBUTES); try { final VirtualFile file = VfsUtil.findFileByURL(new URI(frame.getURI()).toURL()); if (file != null) { component.append(file.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); if (frame.getLineNumber() > 0) { component.append(":" + frame.getLineNumber(), SimpleTextAttributes.REGULAR_ATTRIBUTES); } component.setToolTipText(file.getPresentableUrl()); } else { component.append(frame.getURI() + ":" + frame.getLineNumber(), SimpleTextAttributes.REGULAR_ATTRIBUTES); } } catch (Exception ignored) { component.append(frame.getURI() + ":" + frame.getLineNumber(), SimpleTextAttributes.REGULAR_ATTRIBUTES); } } @Override public void computeChildren(@NotNull XCompositeNode node) { try { if (myFrame instanceof Debugger.StyleFrame) { final List<Debugger.Variable> variables = ((Debugger.StyleFrame)myFrame).getVariables(); final XValueChildrenList list = new XValueChildrenList(); for (final Debugger.Variable variable : variables) { list.add(variable.getName(), new MyValue(variable)); } node.addChildren(list, true); } else { super.computeChildren(node); } } catch (VMPausedException ignored) { node.setErrorMessage(VMPausedException.MESSAGE); } } public Debugger.Frame getFrame() { return myFrame; } private static class MyValue extends XValue { private final Debugger.Variable myVariable; public MyValue(Debugger.Variable variable) { myVariable = variable; } @Override public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { final Debugger.Variable.Kind kind = myVariable.getKind(); Icon icon = null; if (myVariable.isGlobal()) { if (kind == Debugger.Variable.Kind.VARIABLE) { icon = PlatformIcons.FIELD_ICON; } else { icon = PlatformIcons.PROPERTY_ICON; } } else if (kind == Debugger.Variable.Kind.VARIABLE) { icon = PlatformIcons.VARIABLE_ICON; } else if (kind == Debugger.Variable.Kind.PARAMETER) { icon = PlatformIcons.PARAMETER_ICON; } final Value v = myVariable.getValue(); if (v.getType() == Value.XPathType.STRING) { node.setPresentation(icon, v.getType().getName(), "'" + String.valueOf(v.getValue()) + "'", false); } else { final boolean hasChildren = myVariable.getValue().getValue() instanceof Value.NodeSet; node.setPresentation(icon, v.getType().getName(), String.valueOf(v.getValue()), hasChildren); } } @Override public void computeChildren(@NotNull XCompositeNode node) { if (myVariable.getValue().getValue() instanceof Value.NodeSet) { final Value.NodeSet set = (Value.NodeSet)myVariable.getValue().getValue(); final XValueChildrenList list = new XValueChildrenList(); for (final Value.Node n : set.getNodes()) { list.add(n.myXPath, new NodeValue(n)); } node.addChildren(list, false); } super.computeChildren(node); } @Override public String getEvaluationExpression() { return "$" + myVariable.getName(); } @Override public void computeSourcePosition(@NotNull XNavigatable navigatable) { navigatable.setSourcePosition(XsltSourcePosition.create(myVariable)); } static String clipValue(String stringValue) { return stringValue.length() < 100 ? stringValue : stringValue.substring(0, 100) + "..."; } private static class NodeValue extends XValue { private final Value.Node myNode; public NodeValue(Value.Node n) { myNode = n; } @Override public void computeSourcePosition(@NotNull XNavigatable navigatable) { navigatable.setSourcePosition(XsltSourcePosition.create(myNode)); } @Override public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { node.setPresentation(null, "node", myNode.myStringValue, false); } } } private static class MyEvaluator extends XDebuggerEvaluator { private final Debugger.StyleFrame myFrame; public MyEvaluator(Debugger.StyleFrame frame) { myFrame = frame; } @Override public boolean isCodeFragmentEvaluationSupported() { return false; } @Override public void evaluate(@NotNull String expression, @NotNull XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition) { try { final Value eval = myFrame.eval(expression); callback.evaluated(new MyValue(new ExpressionResult(eval))); } catch (VMPausedException ignored) { callback.errorOccurred(VMPausedException.MESSAGE); } catch (Debugger.EvaluationException e) { callback.errorOccurred(e.getMessage() != null ? e.getMessage() : e.toString()); } } private static class ExpressionResult implements Debugger.Variable { private final Value myValue; public ExpressionResult(Value value) { myValue = value; } @Override @SuppressWarnings({ "ConstantConditions" }) public String getURI() { return null; } @Override public int getLineNumber() { return -1; } @Override public boolean isGlobal() { return false; } @Override public Kind getKind() { return Kind.EXPRESSION; } @Override public String getName() { return "result"; } @Override public Value getValue() { return myValue; } } } }