*/
package com.jetbrains.python.actions.view.array;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
+import com.intellij.xdebugger.frame.XValue;
+import com.intellij.xdebugger.frame.XValueNode;
+import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeListener;
+import com.intellij.xdebugger.impl.ui.tree.nodes.RestorableStateNode;
+import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
+import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode;
+import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
+import com.jetbrains.python.debugger.PyDebugValue;
+import com.jetbrains.python.debugger.PyDebuggerEvaluator;
+import org.jetbrains.annotations.NotNull;
+
+import javax.naming.directory.InvalidAttributeValueException;
+import javax.swing.*;
+import javax.swing.table.DefaultTableModel;
+import java.util.List;
+
/**
-* @author amarch
-*/
+ * @author amarch
+ */
class NumpyArrayValueProvider extends ArrayValueProvider {
+ private ArrayTableComponent myComponent;
+ private JTable myTable;
+ private Project myProject;
+ private PyDebuggerEvaluator myEvaluator;
+ private NumpyArrayPresentation myLastPresentation;
+
+ public NumpyArrayValueProvider(XValueNode node, ArrayTableComponent component, Project project) {
+ super(node);
+ myComponent = component;
+ myProject = project;
+ myTable = component.getTable();
+ myEvaluator = new PyDebuggerEvaluator(project, ((PyDebugValue)((XValueNodeImpl)node).getValueContainer()).getFrameAccessor());
+ myLastPresentation = new NumpyArrayPresentation(((XValueNodeImpl)node).getName());
+ }
+
+ private class NumpyArrayPresentation {
+ private Object[][] myData;
+ private String mySlice;
+ private String myArrayName;
+ private int[] myShape;
+ private int myRows = 0;
+ private int myFilledRows = 0;
+ private int nextRow = 0;
+ private String myDtype;
+
+ public NumpyArrayPresentation(String slice, String name) {
+ mySlice = slice;
+ myArrayName = name;
+ }
+
+ public NumpyArrayPresentation(String name) {
+ myArrayName = name;
+ }
+
+ public NumpyArrayPresentation getInstance() {
+ return this;
+ }
+
+ public int[] getShape() {
+ return myShape;
+ }
+
+ public void setShape(int[] shape) {
+ myShape = shape;
+ }
+
+ public String getDtype() {
+ return myDtype;
+ }
+
+ public void setDtype(String dtype) {
+ myDtype = dtype;
+ }
+
+ public Object[][] getData() {
+ return myData;
+ }
+
+ public void fillShape(final boolean stop) {
+ XDebuggerEvaluator.XEvaluationCallback callback = new XDebuggerEvaluator.XEvaluationCallback() {
+ @Override
+ public void evaluated(@NotNull XValue result) {
+ try {
+ myShape = parseShape(((PyDebugValue)result).getValue());
+
+ if (myShape.length == 1) {
+ myShape = new int[]{1, myShape[0]};
+ }
+
+ myData = new Object[myShape[myShape.length - 2]][myShape[myShape.length - 1]];
+ myRows = myShape[myShape.length - 2];
+ if (!stop) {
+ startFillTable(getInstance());
+ }
+ }
+ catch (InvalidAttributeValueException e) {
+ errorOccurred(e.getMessage());
+ }
+ }
+
+ @Override
+ public void errorOccurred(@NotNull String errorMessage) {
+ myComponent.setErrorSpinnerText(errorMessage);
+ }
+ };
+ fillShape(stop, callback);
+ }
+
+ public void fillShape(final boolean stop, XDebuggerEvaluator.XEvaluationCallback callback) {
+ String evalShapeCommand = myArrayName + ".shape";
+ myEvaluator.evaluate(evalShapeCommand, callback, null);
+ }
+
+ public void fillSliceShape(final XDebuggerEvaluator.XEvaluationCallback callback) {
+ if (mySlice == null) {
+ callback.errorOccurred("Null slice");
+ return;
+ }
+ XDebuggerEvaluator.XEvaluationCallback innerCallback = new XDebuggerEvaluator.XEvaluationCallback() {
+ @Override
+ public void evaluated(@NotNull XValue result) {
+ try {
+ myShape = parseShape(((PyDebugValue)result).getValue());
+
+ if (myShape.length > 2) {
+ errorOccurred("Slice not present valid 2d array.");
+ return;
+ }
+
+ if (myShape.length == 1) {
+ myShape = new int[]{1, myShape[0]};
+ }
+ myData = new Object[myShape[myShape.length - 2]][myShape[myShape.length - 1]];
+ myRows = myShape[myShape.length - 2];
+ callback.evaluated(result);
+ }
+ catch (InvalidAttributeValueException e) {
+ errorOccurred(e.getMessage());
+ }
+ }
+
+ @Override
+ public void errorOccurred(@NotNull String errorMessage) {
+ callback.errorOccurred(errorMessage);
+ }
+ };
+
+ String evalShapeCommand = mySlice + ".shape";
+ myEvaluator.evaluate(evalShapeCommand, innerCallback, null);
+ }
+
+ private int[] parseShape(String shape) throws InvalidAttributeValueException {
+ String[] dimensions = shape.substring(1, shape.length() - 1).trim().split(",");
+ if (dimensions.length > 0) {
+ int[] result = new int[dimensions.length];
+ for (int i = 0; i < dimensions.length; i++) {
+ result[i] = Integer.parseInt(dimensions[i].trim());
+ }
+ return result;
+ }
+ else {
+ throw new InvalidAttributeValueException("Invalid shape string for " + ((XValueNodeImpl)myBaseNode).getName());
+ }
+ }
+
+ public void fillType(final boolean stop) {
+ XDebuggerEvaluator.XEvaluationCallback callback = new XDebuggerEvaluator.XEvaluationCallback() {
+ @Override
+ public void evaluated(@NotNull XValue result) {
+ myDtype = ((PyDebugValue)result).getValue();
+ if (!stop) {
+ startFillTable(getInstance());
+ }
+ }
+
+ @Override
+ public void errorOccurred(@NotNull String errorMessage) {
+
+ }
+ };
+ String evalTypeCommand = myArrayName + ".dtype.kind";
+ myEvaluator.evaluate(evalTypeCommand, callback, null);
+ }
+
+ public boolean dataFilled() {
+ return myRows > 0 && myFilledRows == myRows;
+ }
+
+ public void fillData(final boolean stop) {
+ final XDebuggerEvaluator.XEvaluationCallback callback = new XDebuggerEvaluator.XEvaluationCallback() {
+ @Override
+ public void evaluated(@NotNull XValue result) {
+ String name = ((PyDebugValue)result).getName();
+ XValueNodeImpl node = new XValueNodeImpl(((XValueNodeImpl)myBaseNode).getTree(), null, name, result);
+ node.startComputingChildren();
+ }
+
+ @Override
+ public void errorOccurred(@NotNull String errorMessage) {
+ }
+ };
+
+ XDebuggerTreeListener treeListener = new XDebuggerTreeListener() {
+ @Override
+ public void nodeLoaded(@NotNull RestorableStateNode node, String name) {
+ return;
+ }
+
+ @Override
+ public void childrenLoaded(@NotNull XDebuggerTreeNode node, @NotNull List<XValueContainerNode<?>> children, boolean last) {
+ String fullName = ((XValueNodeImpl)node).getName();
+ int row = 0;
+ if (fullName.contains("[")) {
+ row = Integer.parseInt(fullName.substring(fullName.lastIndexOf('[') + 1, fullName.length() - 2));
+ }
+ if (myData[row][0] == null) {
+ for (int i = 0; i < node.getChildCount() - 1; i++) {
+ myData[row][i] = ((XValueNodeImpl)node.getChildAt(i + 1)).getRawValue();
+ }
+ myFilledRows += 1;
+ }
+ if (myFilledRows == myRows) {
+ node.getTree().removeTreeListener(this);
+ if (!stop) {
+ startFillTable(getInstance());
+ }
+ }
+ else {
+ nextRow += 1;
+ startEvalNextRow(callback);
+ }
+ }
+ };
+
+ ((XValueNodeImpl)myBaseNode).getTree().addTreeListener(treeListener);
+ nextRow = 0;
+ startEvalNextRow(callback);
+ }
+
+ private void startEvalNextRow(XDebuggerEvaluator.XEvaluationCallback callback) {
+ String evalRowCommand = "list(" + myArrayName;
+ if (myShape.length > 2) {
+ evalRowCommand += new String(new char[myShape.length - 2]).replace("\0", "[0]");
+ }
+
+ if (myShape[0] > 1) {
+ evalRowCommand += "[" + nextRow + "])";
+ }
+ else {
+ evalRowCommand += ")";
+ }
+ myEvaluator.evaluate(evalRowCommand, callback, null);
+ }
+
+ public String getSlice() {
+ return mySlice;
+ }
+
+ public void setSlice(String slice) {
+ mySlice = slice;
+ }
+
+ public void computeSlice() {
+ String presentation = "";
+
+ if (myBaseNode != null) {
+ presentation += ((XValueNodeImpl)myBaseNode).getName();
+
+ if (myShape != null) {
+ presentation += new String(new char[myShape.length - 2]).replace("\0", "[0]");
+ if (myShape[0] == 1) {
+ presentation += "[0:" + myShape[1] + "]";
+ }
+ else {
+ presentation += "[0:" + myShape[myShape.length - 2] + "][0:" + myShape[myShape.length - 1] + "]";
+ }
+ }
+ }
+ setSlice(presentation);
+ }
+ }
+
@Override
- public Object[][] parseValues(String rawValues) {
- return null;
+ public boolean isNumeric() {
+ if (myLastPresentation.getDtype() != null) {
+ return "biufc".contains(myLastPresentation.getDtype().substring(0, 1));
+ }
+ return false;
+ }
+
+ public void startFillTable(NumpyArrayPresentation presentation) {
+
+ if (presentation == null) {
+ presentation = new NumpyArrayPresentation(((XValueNodeImpl)myBaseNode).getName());
+ }
+
+ if (presentation.getShape() == null) {
+ presentation.fillShape(false);
+ return;
+ }
+
+ if (presentation.getDtype() == null) {
+ presentation.fillType(false);
+ return;
+ }
+
+ if (!presentation.dataFilled()) {
+ presentation.fillData(false);
+ return;
+ }
+
+ if (presentation.getSlice() == null) {
+ presentation.computeSlice();
+ }
+
+ Object[][] data = presentation.getData();
+
+ if (myLastPresentation == null || !presentation.getSlice().equals(myLastPresentation.getSlice())) {
+ myLastPresentation = presentation;
+ }
+
+ DefaultTableModel model = new DefaultTableModel(data, range(0, data[0].length - 1));
+ myTable.setModel(model);
+ myTable.setDefaultEditor(myTable.getColumnClass(0), new ArrayTableCellEditor(myProject));
+ enableColor(data);
+ myComponent.getTextField().setText(myLastPresentation.getSlice());
}
- private boolean isNumeric(String value) {
- return true;
+ private String[] range(int min, int max) {
+ String[] array = new String[max - min + 1];
+ for (int i = min; i <= max; i++) {
+ array[i] = Integer.toString(i);
+ }
+ return array;
+ }
+
+ private void enableColor(Object[][] data) {
+ if (isNumeric()) {
+ double min = Double.MAX_VALUE;
+ double max = Double.MIN_VALUE;
+ if (data.length > 0) {
+ try {
+ for (int i = 0; i < data.length; i++) {
+ for (int j = 0; j < data[0].length; j++) {
+ double d = Double.parseDouble(data[i][j].toString());
+ min = min > d ? d : min;
+ max = max < d ? d : max;
+ }
+ }
+ }
+ catch (NumberFormatException e) {
+ min = 0;
+ max = 0;
+ }
+ }
+ else {
+ min = 0;
+ max = 0;
+ }
+
+ myTable.setDefaultRenderer(myTable.getColumnClass(0), new ArrayTableCellRenderer(min, max));
+ }
+ else {
+ myComponent.getColored().setSelected(false);
+ myComponent.getColored().setVisible(false);
+ }
+ }
+
+ public void refreshTable(final NumpyArrayPresentation presentation) {
+
+ if (presentation.getSlice() != null) {
+ presentation.fillSliceShape(new XDebuggerEvaluator.XEvaluationCallback() {
+ @Override
+ public void evaluated(@NotNull XValue result) {
+ refreshTable(presentation);
+ }
+
+ @Override
+ public void errorOccurred(@NotNull String errorMessage) {
+ myComponent.setErrorSpinnerText(errorMessage);
+ }
+ });
+ return;
+ }
+
+ presentation.fillData(false);
}
}