Package com.jetbrains.lang.dart.ide.runner.server.frame

Source Code of com.jetbrains.lang.dart.ide.runner.server.frame.DartValue

package com.jetbrains.lang.dart.ide.runner.server.frame;

import com.intellij.icons.AllIcons;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import com.intellij.util.containers.SortedList;
import com.intellij.xdebugger.frame.*;
import com.intellij.xdebugger.frame.presentation.XNumericValuePresentation;
import com.intellij.xdebugger.frame.presentation.XRegularValuePresentation;
import com.intellij.xdebugger.frame.presentation.XStringValuePresentation;
import com.intellij.xdebugger.frame.presentation.XValuePresentation;
import com.jetbrains.lang.dart.ide.runner.server.DartCommandLineDebugProcess;
import com.jetbrains.lang.dart.ide.runner.server.google.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

// todo navigate to source, type
public class DartValue extends XNamedValue {
  public static final String NODE_NAME_RESULT = "result";
  public static final String NODE_NAME_EXCEPTION = "exception";

  private final @NotNull DartCommandLineDebugProcess myDebugProcess;
  private final @NotNull VmValue myVmValue;
  private final boolean myIsException;

  private Ref<Integer> myListOrMapChildrenAlreadyShown = new Ref<Integer>(0);

  static final String OBJECT_OF_TYPE_PREFIX = "object of type ";

  public DartValue(@NotNull final DartCommandLineDebugProcess debugProcess,
                   @Nullable final String rawNodeName,
                   @NotNull final VmValue vmValue,
                   final boolean isException) {
    super(StringUtil.notNullize(DebuggerUtils.demangleVmName(rawNodeName), "<unknown>"));
    myDebugProcess = debugProcess;
    myVmValue = vmValue;
    myIsException = isException;
  }

  @Override
  public void computePresentation(final @NotNull XValueNode node, final @NotNull XValuePlace place) {
    final String value = myVmValue.isList() ? "size = " + myVmValue.getLength()
                                            : StringUtil.notNullize(myVmValue.getText(), "null");
    final String objectIdPostfix = "[id=" + myVmValue.getObjectId() + "]";

    final XValuePresentation presentation;

    if (myVmValue.isNull()) {
      presentation = new XRegularValuePresentation("null", null);
    }
    else if (myVmValue.isString()) {
      presentation = new XStringValuePresentation(StringUtil.stripQuotesAroundValue(value));
    }
    else if (myVmValue.isNumber()) {
      presentation = new XNumericValuePresentation(value);
    }
    else if ("boolean".equals(myVmValue.getKind())) {
      presentation = new XRegularValuePresentation(value, null);
    }
    else if (myVmValue.isList()) {
      presentation = new XRegularValuePresentation(value, "List" + objectIdPostfix); // VmValue doesn't contain real List subclass name
    }
    else {
      if (value.startsWith(OBJECT_OF_TYPE_PREFIX)) {
        presentation = new XRegularValuePresentation("", value.substring(OBJECT_OF_TYPE_PREFIX.length()) + objectIdPostfix);
      }
      else {
        presentation = new XRegularValuePresentation(value, DebuggerUtils.demangleVmName(myVmValue.getKind()) + objectIdPostfix);
      }
    }

    final boolean neverHasChildren = myVmValue.isPrimitive() ||
                                     myVmValue.isNull() ||
                                     myVmValue.isFunction() ||
                                     myVmValue.isList() && myVmValue.getLength() == 0;
    node.setPresentation(getIcon(), presentation, !neverHasChildren);

    if (!myVmValue.isList() && !myVmValue.isPrimitive() && !myVmValue.isNull() && !myVmValue.isFunction()) {
      scheduleToStringOrCollectionSizePresentation(node, presentation.getType());
    }
  }

  private void scheduleToStringOrCollectionSizePresentation(@NotNull final XValueNode node, final String objectType) {
    DartCommandLineDebugProcess.LOG.assertTrue(!myVmValue.isList(), myVmValue);

    try {
      final String expression = "(this is Iterable || this is Map) ? ('IntelliJ marker:${this.length}') : toString()";
      myDebugProcess.getVmConnection().evaluateObject(myVmValue.getIsolate(), myVmValue, expression, new VmCallback<VmValue>() {
        public void handleResult(final VmResult<VmValue> result) {
          if (node.isObsolete() || result.isError() || result.getResult() == null || !"string".equals(result.getResult().getKind())) {
            return; // stay with existing presentation returned in computePresentation()
          }

          final String text = StringUtil.stripQuotesAroundValue(result.getResult().getText());
          if (text.startsWith("IntelliJ marker:")) {
            try {
              final int collectionSize = Integer.parseInt(text.substring("IntelliJ marker:".length()));
              if (collectionSize >= 0) {
                node.setPresentation(AllIcons.Debugger.Db_array, objectType, "size = " + collectionSize, collectionSize > 0);
              }
            }
            catch (NumberFormatException ignore) {/**/}
          }
          // default toString() implementation returns "Instance of 'ClassName'" - do not show it
          else if (!text.startsWith("Instance of '") || !text.endsWith("'")) {
            node.setPresentation(getIcon(), objectType, text, true);
          }
        }
      });
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }

  private Icon getIcon() {
    if (myIsException) return AllIcons.Debugger.Db_exception_breakpoint;
    if (myVmValue.isList()) return AllIcons.Debugger.Db_array;
    if (myVmValue.isPrimitive()) return AllIcons.Debugger.Db_primitive;
    if (myVmValue.isFunction()) return AllIcons.Nodes.Function;

    return AllIcons.Debugger.Value;
  }

  @Override
  public void computeChildren(@NotNull final XCompositeNode node) {
    try {
      if (myVmValue.isList()) {
        computeListChildren(node, myListOrMapChildrenAlreadyShown);
        return;
      }

      final String expression = "this is Iterable ? 'Iterable' : this is Map ? 'Map' : ''";
      myDebugProcess.getVmConnection().evaluateObject(myVmValue.getIsolate(), myVmValue, expression, new VmCallback<VmValue>() {
        @Override
        public void handleResult(@NotNull final VmResult<VmValue> result) {
          if (node.isObsolete()) return;

          if (!result.isError() && result.getResult() != null && "string".equals(result.getResult().getKind())) {
            final String text = StringUtil.stripQuotesAroundValue(result.getResult().getText());
            if ("Map".equals(text)) {
              computeMapChildren(node);
              return;
            }
            else if ("Iterable".equals(text)) {
              computeIterableChildren(node);
              return;
            }
          }

          computeObjectChildren(node);
        }
      });
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }

  private void computeListChildren(@NotNull final XCompositeNode node,
                                   @NotNull final Ref<Integer> listChildrenAlreadyShown) throws IOException {
    DartCommandLineDebugProcess.LOG.assertTrue(myVmValue.isList(), myVmValue);

    final Integer fromIndex = listChildrenAlreadyShown.get();
    final int childrenToShow = Math.min(myVmValue.getLength() - fromIndex, XCompositeNode.MAX_CHILDREN_TO_SHOW);
    if (childrenToShow == 0) {
      node.addChildren(XValueChildrenList.EMPTY, true);
      return;
    }


    computeListChildren(node, myDebugProcess, myVmValue, fromIndex, childrenToShow, new Consumer<List<DartValue>>() {
      @Override
      public void consume(final List<DartValue> listChildren) {
        final XValueChildrenList resultList = new XValueChildrenList(listChildren.size());
        for (DartValue value : listChildren) {
          resultList.add(value);
        }

        node.addChildren(resultList, true);
        listChildrenAlreadyShown.set(listChildrenAlreadyShown.get() + listChildren.size());

        if (myVmValue.getLength() > listChildrenAlreadyShown.get()) {
          node.tooManyChildren(myVmValue.getLength() - listChildrenAlreadyShown.get());
        }
      }
    });
  }

  private static void computeListChildren(@NotNull final XCompositeNode node,
                                          @NotNull final DartCommandLineDebugProcess debugProcess,
                                          @NotNull final VmValue listValue,
                                          final int fromIndex,
                                          final int childrenAmount,
                                          @NotNull final Consumer<List<DartValue>> listChildrenConsumer) throws IOException {
    DartCommandLineDebugProcess.LOG.assertTrue(listValue.isList(), listValue);

    final AtomicInteger handledResponsesAmount = new AtomicInteger(0);

    final SortedList<DartValue> sortedChildren = new SortedList<DartValue>(new Comparator<DartValue>() {
      public int compare(DartValue o1, DartValue o2) {
        return StringUtil.naturalCompare(o1.getName(), o2.getName());
      }
    });

    for (int listIndex = fromIndex; listIndex < fromIndex + childrenAmount; listIndex++) {
      final String nodeName = String.valueOf(listIndex);
      debugProcess.getVmConnection()
        .getListElements(listValue.getIsolate(), listValue.getObjectId(), listIndex, new VmCallbackAdapter<VmValue>(node) {
          @Override
          public void handleResult(final VmResult<VmValue> result) {
            synchronized (node) {
              handledResponsesAmount.addAndGet(1);
              super.handleResult(result);
            }
          }

          @Override
          protected void handleGoodResult(@NotNull final VmValue result) {
            sortedChildren.add(new DartValue(debugProcess, nodeName, result, false));

            if (handledResponsesAmount.get() == childrenAmount) {
              listChildrenConsumer.consume(sortedChildren);
            }
          }
        });
    }
  }

  private void computeMapChildren(@NotNull final XCompositeNode node) {
    try {
      myDebugProcess.getVmConnection()
        .evaluateObject(myVmValue.getIsolate(), myVmValue, "keys.toList()", new VmCallback<VmValue>() {
          @Override
          public void handleResult(@NotNull final VmResult<VmValue> result) {
            if (node.isObsolete()) return;

            if (!result.isError() && result.getResult() != null && result.getResult().isList()) {
              computeMapChildrenForKeys(node, result.getResult());
            }
            else {
              computeObjectChildren(node);
            }
          }
        });
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }

  private void computeMapChildrenForKeys(@NotNull final XCompositeNode node, @NotNull final VmValue mapKeysList) {
    DartCommandLineDebugProcess.LOG.assertTrue(mapKeysList.isList());

    try {
      myDebugProcess.getVmConnection()
        .evaluateObject(myVmValue.getIsolate(), myVmValue, "values.toList()", new VmCallback<VmValue>() {
          @Override
          public void handleResult(@NotNull final VmResult<VmValue> result) {
            if (node.isObsolete()) return;

            if (!result.isError() && result.getResult() != null && result.getResult().isList()) {
              computeMapChildrenForKeysAndValues(node, mapKeysList, result.getResult());
            }
            else {
              computeObjectChildren(node);
            }
          }
        });
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }

  private void computeMapChildrenForKeysAndValues(@NotNull final XCompositeNode node,
                                                  @NotNull final VmValue mapKeysList,
                                                  @NotNull final VmValue mapValuesList) {
    try {
      final Integer fromIndex = myListOrMapChildrenAlreadyShown.get();
      final int childrenToShow = Math.min(mapKeysList.getLength() - fromIndex, XCompositeNode.MAX_CHILDREN_TO_SHOW);
      DartCommandLineDebugProcess.LOG.assertTrue(childrenToShow > 0);

      computeListChildren(node, myDebugProcess, mapKeysList, fromIndex, childrenToShow, new Consumer<List<DartValue>>() {
        @Override
        public void consume(final List<DartValue> mapKeys) {
          try {
            computeListChildren(node, myDebugProcess, mapValuesList, fromIndex, childrenToShow, new Consumer<List<DartValue>>() {
              @Override
              public void consume(final List<DartValue> mapValues) {
                addMapChildrenToNode(node, mapKeys, mapValues, mapKeysList.getLength());
              }
            });
          }
          catch (IOException e) {
            DartCommandLineDebugProcess.LOG.error(e);
          }
        }
      });
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }

  private void addMapChildrenToNode(@NotNull final XCompositeNode node,
                                    @NotNull final List<DartValue> mapKeys,
                                    @NotNull final List<DartValue> mapValues,
                                    final int mapSize) {
    if (mapKeys.size() != mapValues.size()) {
      DartCommandLineDebugProcess.LOG.warn(mapKeys.size() + " keys, " + mapValues.size() + " values");
      node.setErrorMessage("failed to show Map contents");
      return;
    }

    final XValueChildrenList resultList = new XValueChildrenList(mapKeys.size());

    for (int i = 0; i < mapKeys.size(); i++) {
      final DartValue mapKey = mapKeys.get(i);
      final DartValue mapValue = mapValues.get(i);
      resultList.add(new DartMapEntryValue(myDebugProcess, mapKey.getName(), mapKey.myVmValue, mapValue.myVmValue));
    }

    node.addChildren(resultList, true);

    myListOrMapChildrenAlreadyShown.set(myListOrMapChildrenAlreadyShown.get() + mapKeys.size());
    if (mapSize > myListOrMapChildrenAlreadyShown.get()) {
      node.tooManyChildren(mapSize - myListOrMapChildrenAlreadyShown.get());
    }
  }

  private void computeIterableChildren(@NotNull final XCompositeNode node) {
    try {
      myDebugProcess.getVmConnection().evaluateObject(myVmValue.getIsolate(), myVmValue, "toList()", new VmCallback<VmValue>() {
        @Override
        public void handleResult(final VmResult<VmValue> result) {
          if (node.isObsolete()) return;

          if (!result.isError() && result.getResult() != null && result.getResult().isList()) {
            try {
              new DartValue(myDebugProcess, "fake node", result.getResult(), false)
                .computeListChildren(node, myListOrMapChildrenAlreadyShown);
            }
            catch (IOException e) {
              DartCommandLineDebugProcess.LOG.error(e);
            }
          }
        }
      });
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }

  private void computeObjectChildren(@NotNull final XCompositeNode node) {
    try {
      myDebugProcess.getVmConnection()
        .getObjectProperties(myVmValue.getIsolate(), myVmValue.getObjectId(), new VmCallbackAdapter<VmObject>(node) {
                               @Override
                               protected void handleGoodResult(@NotNull final VmObject result) {
                                 final List<VmVariable> fields = result.getFields();
                                 if (fields == null) {
                                   node.addChildren(XValueChildrenList.EMPTY, true);
                                   return;
                                 }

                                 // todo sort somehow?
                                 final XValueChildrenList childrenList = new XValueChildrenList(fields.size());
                                 for (final VmVariable field : fields) {
                                   final VmValue vmValue = field.getValue();
                                   if (vmValue != null) {
                                     childrenList.add(new DartValue(myDebugProcess, field.getName(), vmValue, false));
                                   }
                                 }

                                 node.addChildren(childrenList, true);
                               }
                             }
        );
    }
    catch (IOException e) {
      DartCommandLineDebugProcess.LOG.error(e);
    }
  }
}
TOP

Related Classes of com.jetbrains.lang.dart.ide.runner.server.frame.DartValue

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.