From 554e074d6e00739fa170e1bcacaf3369097b5b74 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 5 Nov 2014 13:29:28 +0100 Subject: [PATCH 1/2] Numpy array view refactored. --- .../python/debugger/PyFrameAccessor.java | 2 + .../console/PydevConsoleCommunication.java | 5 + .../python/debugger/PyDebugProcess.java | 5 + .../python/debugger/array/ArrayChunk.java | 96 -------- .../array/ArrayTableCellRenderer.java | 2 +- .../python/debugger/array/ArrayTableForm.java | 53 +--- .../debugger/array/ArrayValueProvider.java | 43 ---- .../debugger/array/JBTableWithRowHeaders.java | 48 +++- .../debugger/array/NumpyArraySlice.java | 228 ------------------ ...alueProvider.java => NumpyArrayTable.java} | 175 +++----------- .../debugger/array/PagingTableModel.java | 193 ++++----------- .../debugger/array/PyViewArrayAction.java | 53 ++-- 12 files changed, 169 insertions(+), 734 deletions(-) delete mode 100644 python/src/com/jetbrains/python/debugger/array/ArrayChunk.java delete mode 100644 python/src/com/jetbrains/python/debugger/array/ArrayValueProvider.java delete mode 100644 python/src/com/jetbrains/python/debugger/array/NumpyArraySlice.java rename python/src/com/jetbrains/python/debugger/array/{NumpyArrayValueProvider.java => NumpyArrayTable.java} (79%) diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java b/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java index 01666456f420..98140e6e1577 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java @@ -20,4 +20,6 @@ public interface PyFrameAccessor { @Nullable PyReferrersLoader getReferrersLoader(); + + Object[][] getArrayItems(PyDebugValue var, int colOffset, int rowOffset, int cols, int rows, String format); } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java index 5bf06782d376..9b4ef761058a 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java @@ -531,6 +531,11 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl return null; } + @Override + public Object[][] getArrayItems(PyDebugValue var, int colOffset, int rowOffset, int cols, int rows, String format) { + return new Object[][]{new Object[]{1, 2}, new Object[]{3, 4}}; + } + /** * Request that pydevconsole connect (with pydevd) to the specified port * diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index 532e374d5c74..373f325d888a 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -571,6 +571,11 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr return myReferrersProvider; } + @Override + public Object[][] getArrayItems(PyDebugValue var, int colOffset, int rowOffset, int cols, int rows, String format) { + return new Object[][]{new Object[]{1, 2}, new Object[]{3, 4}}; + } + @Nullable public String loadSource(String path) { return myDebugger.loadSource(path); diff --git a/python/src/com/jetbrains/python/debugger/array/ArrayChunk.java b/python/src/com/jetbrains/python/debugger/array/ArrayChunk.java deleted file mode 100644 index 12f4d9c5cd0e..000000000000 --- a/python/src/com/jetbrains/python/debugger/array/ArrayChunk.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 - * - * 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.jetbrains.python.debugger.array; - -import org.jetbrains.annotations.NotNull; - -/** - * @author amarch - */ -public abstract class ArrayChunk implements Comparable { - private final String myBaseSlice; - private final int myColumns; - private final int myRows; - private final int myColOffset; - private final int myRowOffset; - private Object[][] myData; - - public ArrayChunk(String baseSlice, int rows, int columns, int rOffset, int cOffset) { - myBaseSlice = baseSlice; - myColumns = columns; - myRows = rows; - myRowOffset = rOffset; - myColOffset = cOffset; - } - - public int getRows() { - return myRows; - } - - public int getColumns() { - return myColumns; - } - - public int getColOffset() { - return myColOffset; - } - - public int getRowOffset() { - return myRowOffset; - } - - public Object[][] getData() { - return myData; - } - - public String getBaseSlice() { - return myBaseSlice; - } - - public String getPresentation() { - return ""; - } - - abstract void fillData(Runnable callback); - - public boolean contains(int row, int col) { - return myRowOffset <= row && row < myRowOffset + myRows && myColOffset <= col && col < myColOffset + myColumns; - } - - public boolean equals(Object o) { - return o instanceof ArrayChunk && - myBaseSlice == ((ArrayChunk)o).myBaseSlice && - myColumns == ((ArrayChunk)o).myColumns && - myRows == ((ArrayChunk)o).myRows && - myColOffset == ((ArrayChunk)o).myColOffset && - myRowOffset == ((ArrayChunk)o).myRowOffset; - } - - @Override - public int compareTo(@NotNull ArrayChunk other) { - int compRow = myRowOffset - other.myRowOffset; - int compCol = myColOffset - other.myColOffset; - return compRow != 0 ? compRow : compCol; - } - - public boolean isOneRow() { - return getRows() == 1; - } - - public boolean isOneColumn() { - return getColumns() == 1; - } -} diff --git a/python/src/com/jetbrains/python/debugger/array/ArrayTableCellRenderer.java b/python/src/com/jetbrains/python/debugger/array/ArrayTableCellRenderer.java index 5eebbd18ccb4..21c0ef4cc2c9 100644 --- a/python/src/com/jetbrains/python/debugger/array/ArrayTableCellRenderer.java +++ b/python/src/com/jetbrains/python/debugger/array/ArrayTableCellRenderer.java @@ -62,7 +62,7 @@ class ArrayTableCellRenderer extends DefaultTableCellRenderer { if (myMax != myMin) { if (myColored && value != null) { try { - double rangedValue = NumpyArrayValueProvider.getRangedValue(value.toString(), myType, myMin, myMax, myComplexMax, myComplexMin); + double rangedValue = NumpyArrayTable.getRangedValue(value.toString(), myType, myMin, myMax, myComplexMax, myComplexMin); this.setBackground( new JBColor(new Color((int)Math.round(255 * rangedValue), 0, (int)Math.round(255 * (1 - rangedValue)), 130), new Color((int)Math.round(255 * rangedValue), 0, (int)Math.round(255 * (1 - rangedValue)), 130))); diff --git a/python/src/com/jetbrains/python/debugger/array/ArrayTableForm.java b/python/src/com/jetbrains/python/debugger/array/ArrayTableForm.java index 0f7c1aaab5c3..90b6d1c1b75c 100644 --- a/python/src/com/jetbrains/python/debugger/array/ArrayTableForm.java +++ b/python/src/com/jetbrains/python/debugger/array/ArrayTableForm.java @@ -21,6 +21,7 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.jetbrains.python.PythonFileType; +import com.jetbrains.python.debugger.PyDebugValue; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -42,7 +43,7 @@ public class ArrayTableForm { private JLabel myFormatLabel; private JPanel myFormatPanel; private JPanel myMainPanel; - private JBTable myTable; + private JTable myTable; private JBTable myBusyTable; private final Project myProject; @@ -58,19 +59,8 @@ public class ArrayTableForm { mySliceTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE); myTable = new JBTableWithRowHeaders(); - myTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); - myTable.setRowSelectionAllowed(false); - myTable.setTableHeader(new CustomTableHeader(myTable)); - myTable.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer()); - myTable.getTableHeader().setReorderingAllowed(false); - myScrollPane = PagingTableModel.LazyViewport.createLazyScrollPaneFor(myTable); - JTable rowTable = new JBTableWithRowHeaders.RowHeaderTable(myTable); - myScrollPane.setRowHeaderView(rowTable); - myScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, - rowTable.getTableHeader()); - - ((JBTableWithRowHeaders)myTable).setRowHeaderTable((JBTableWithRowHeaders.RowHeaderTable)rowTable); + myScrollPane = ((JBTableWithRowHeaders)myTable).getScrollPane(); myFormatTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE); @@ -88,7 +78,7 @@ public class ArrayTableForm { return myFormatTextField; } - public JBTable getTable() { + public JTable getTable() { return myTable; } @@ -96,15 +86,15 @@ public class ArrayTableForm { return myColoredCheckbox; } - public void setDefaultStatus() { - if (myTable != null) { - myTable.getEmptyText().setText(DATA_LOADING_IN_PROCESS); - } - } + //public void setDefaultStatus() { + // if (myTable != null) { + //myTable.getEmptyText().setText(DATA_LOADING_IN_PROCESS); + //} + //} - public void setNotApplicableStatus(XValueNodeImpl node) { - myTable.getEmptyText().setText(NOT_APPLICABLE + node.getName()); - } + //public void setNotApplicableStatus(PyDebugValue node) { + // myTable.getEmptyText().setText(NOT_APPLICABLE + node.getName()); + //} public JComponent getMainPanel() { return myMainPanel; @@ -118,25 +108,6 @@ public class ArrayTableForm { myBusyTable.setPaintBusy(busy); } - public static class CustomTableHeader extends JTableHeader { - - public CustomTableHeader(JTable table) { - super(); - setColumnModel(table.getColumnModel()); - table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - repaint(); - } - }); - } - - @Override - public void columnSelectionChanged(ListSelectionEvent e) { - repaint(); - } - } - public static class ColumnHeaderRenderer extends DefaultTableHeaderCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { diff --git a/python/src/com/jetbrains/python/debugger/array/ArrayValueProvider.java b/python/src/com/jetbrains/python/debugger/array/ArrayValueProvider.java deleted file mode 100644 index 136544e598fe..000000000000 --- a/python/src/com/jetbrains/python/debugger/array/ArrayValueProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 - * - * 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.jetbrains.python.debugger.array; - -import com.intellij.xdebugger.frame.XValueNode; -import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; -import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; -import org.jetbrains.annotations.NotNull; - -/** - * @author amarch - */ -abstract class ArrayValueProvider { - - @NotNull protected final XValueNode myBaseNode; - - public ArrayValueProvider(@NotNull XValueNode node) { - myBaseNode = node; - } - - public abstract boolean isNumeric(); - - public String getNodeName() { - return ((XValueNodeImpl)myBaseNode).getName(); - } - - public XDebuggerTree getTree() { - return ((XValueNodeImpl)myBaseNode).getTree(); - } -} diff --git a/python/src/com/jetbrains/python/debugger/array/JBTableWithRowHeaders.java b/python/src/com/jetbrains/python/debugger/array/JBTableWithRowHeaders.java index 581846095bb2..cfd3abbeb0fc 100644 --- a/python/src/com/jetbrains/python/debugger/array/JBTableWithRowHeaders.java +++ b/python/src/com/jetbrains/python/debugger/array/JBTableWithRowHeaders.java @@ -15,10 +15,13 @@ */ package com.jetbrains.python.debugger.array; +import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableCellRenderer; @@ -33,8 +36,30 @@ import java.beans.PropertyChangeListener; * @author amarch */ public class JBTableWithRowHeaders extends JBTable { + private final JBScrollPane myScrollPane; private RowHeaderTable myRowHeaderTable; + public JBScrollPane getScrollPane() { + return myScrollPane; + } + + public JBTableWithRowHeaders() { + setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); + setRowSelectionAllowed(false); + setMaxItemsForSizeCalculation(50); + setTableHeader(new CustomTableHeader(this)); + getTableHeader().setDefaultRenderer(new ArrayTableForm.ColumnHeaderRenderer()); + getTableHeader().setReorderingAllowed(false); + + myScrollPane = new JBScrollPane(this); + JBTableWithRowHeaders.RowHeaderTable rowTable = new JBTableWithRowHeaders.RowHeaderTable(this); + myScrollPane.setRowHeaderView(rowTable); + myScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, + rowTable.getTableHeader()); + + setRowHeaderTable(rowTable); + } + public boolean getScrollableTracksViewportWidth() { return getPreferredSize().width < getParent().getWidth(); } @@ -48,10 +73,10 @@ public class JBTableWithRowHeaders extends JBTable { } public static class RowHeaderTable extends JBTable implements PropertyChangeListener, TableModelListener { - private JBTable myMainTable; + private JTable myMainTable; private int myRowShift = 0; - public RowHeaderTable(JBTable table) { + public RowHeaderTable(JTable table) { myMainTable = table; myMainTable.getModel().addTableModelListener(this); @@ -158,4 +183,23 @@ public class JBTableWithRowHeaders extends JBTable { } } } + + public static class CustomTableHeader extends JTableHeader { + + public CustomTableHeader(JTable table) { + super(); + setColumnModel(table.getColumnModel()); + table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + repaint(); + } + }); + } + + @Override + public void columnSelectionChanged(ListSelectionEvent e) { + repaint(); + } + } } diff --git a/python/src/com/jetbrains/python/debugger/array/NumpyArraySlice.java b/python/src/com/jetbrains/python/debugger/array/NumpyArraySlice.java deleted file mode 100644 index be318d591b50..000000000000 --- a/python/src/com/jetbrains/python/debugger/array/NumpyArraySlice.java +++ /dev/null @@ -1,228 +0,0 @@ -package com.jetbrains.python.debugger.array; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; -import com.intellij.xdebugger.frame.XValue; -import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; -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 org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * @author amarch - */ - -public class NumpyArraySlice extends ArrayChunk { - private NumpyArrayValueProvider myValueProvider; - private DataEvaluator myDataEvaluator; - private String myFormat; - - private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.array.NumpyArraySlice"); - - public NumpyArraySlice(String baseSlice, - int rows, - int columns, - int rOffset, - int cOffset, - String format, - @NotNull NumpyArrayValueProvider valueProvider) { - super(baseSlice, rows, columns, rOffset, cOffset); - myValueProvider = valueProvider; - myDataEvaluator = new DataEvaluator(); - myFormat = format; - } - - public String getPresentation() { - String onlyChunkSlice = - "[" + getRowOffset() + ":" + (getRowOffset() + getRows()) + ", " + getColOffset() + ":" + (getColOffset() + getColumns()) + "]"; - if (isOneColumn() && isOneRow()) { - onlyChunkSlice = ""; - } - else if (isOneRow()) { - onlyChunkSlice = "[" + getColOffset() + ":" + (getColOffset() + getColumns()) + "]"; - } - else if (isOneColumn()) { - onlyChunkSlice = "[" + getRowOffset() + ":" + (getRowOffset() + getRows()) + "]"; - } - - if (getBaseSlice().endsWith(onlyChunkSlice)) { - return getBaseSlice(); - } - return getBaseSlice() + onlyChunkSlice; - } - - @Override - public void fillData(Runnable callback) { - myDataEvaluator.evaluateData(callback); - } - - public NumpyArraySlice getInstance() { - return this; - } - - public boolean dataFilled() { - return myDataEvaluator.dataFilled(); - } - - private class DataEvaluator { - private Object[][] myData; - private int myFilledRows = 0; - private int nextRow = 0; - - public Object[][] getData() { - return myData; - } - - public boolean dataFilled() { - return getRows() > 0 && myFilledRows == getRows(); - } - - public void evaluateData(final Runnable callback) { - final XDebuggerEvaluator.XEvaluationCallback computeChildrenCallback = new XDebuggerEvaluator.XEvaluationCallback() { - @Override - public void evaluated(@NotNull final XValue result) { - final String name = ((PyDebugValue)result).getName(); - DebuggerUIUtil.invokeLater(new Runnable() { - @Override - public void run() { - XValueNodeImpl node = new XValueNodeImpl(myValueProvider.getTree(), null, name, result); - node.startComputingChildren(); - } - }); - } - - @Override - public void errorOccurred(@NotNull String errorMessage) { - myValueProvider.showError(errorMessage, getInstance()); - } - }; - - XDebuggerTreeListener treeListener = new XDebuggerTreeListener() { - @Override - public void nodeLoaded(@NotNull RestorableStateNode node, String name) { - } - - @Override - public void childrenLoaded(@NotNull XDebuggerTreeNode node, @NotNull List> children, boolean last) { - if (!(node instanceof XValueNodeImpl)) { - return; - } - - String fullName = ((XValueNodeImpl)node).getName(); - if (fullName == null || !fullName.contains(getPresentation())) { - return; - } - - int row; - if (isOneRow()) { - row = 0; - } - else { - if (isOneColumn()) { - row = Integer.parseInt(fullName.substring(fullName.lastIndexOf('[') + 1, fullName.length() - 1)); - } - else { - fullName = fullName.substring(0, fullName.lastIndexOf(",")); - row = Integer.parseInt(fullName.substring(fullName.lastIndexOf('[') + 1, fullName.length())); - } - } - - if (row > getRows()) { - LOG.error("Row " + row + " is out of range for " + getPresentation() + "."); - return; - } - - if (row != -1 && myData[row][0] == null) { - for (int i = 0; i < node.getChildCount() - 1; i++) { - String rawValue = ((XValueNodeImpl)node.getChildAt(i + 1)).getRawValue(); - if (rawValue == null) { - return; - } - if (myValueProvider.isNumeric()) { - //remove str quotes in case of numeric - rawValue = rawValue.substring(1, rawValue.length() - 1); - } - myData[row][i] = rawValue; - } - if (node.getChildCount() == 0) { - String rawValue = ((XValueNodeImpl)node).getRawValue(); - if (rawValue == null) { - return; - } - if (myValueProvider.isNumeric()) { - //remove str quotes in case of numeric - rawValue = rawValue.substring(1, rawValue.length() - 1); - } - myData[row][0] = rawValue; - } - myFilledRows += 1; - if (myFilledRows == getRows()) { - node.getTree().removeTreeListener(this); - callback.run(); - } - else { - nextRow += 1; - startEvalNextRow(computeChildrenCallback); - } - } - } - }; - - myData = - new Object[getRows()][getColumns()]; - myValueProvider.getTree().addTreeListener(treeListener); - nextRow = 0; - myFilledRows = 0; - startEvalNextRow(computeChildrenCallback); - } - - private void startEvalNextRow(XDebuggerEvaluator.XEvaluationCallback callback) { - if (nextRow >= getRows()) { - LOG.error("Row " + nextRow + " is out of range for " + getPresentation() + "."); - return; - } - - String evalRowCommand = "map(lambda l: " + myValueProvider.evalTypeFunc(myFormat) + ", list(" + getPresentation(); - if (!isOneRow() && !isOneColumn()) { - evalRowCommand += "[" + nextRow + ", 0:" + getColumns() + "]))"; - } - - if (isOneRow() && isOneColumn()) { - evalRowCommand = "\'" + myFormat + "\'" + " % " + getPresentation(); - } - else if (isOneColumn()) { - evalRowCommand = "\'" + myFormat + "\'" + " % " + getPresentation(); - evalRowCommand += "[" + nextRow + "]"; - } - else if (isOneRow()) { - evalRowCommand += "[0:" + getColumns() + "]))"; - } - myValueProvider.getEvaluator().evaluate(evalRowCommand, callback, null); - } - } - - public Object[][] getData() { - return myDataEvaluator.getData(); - } - - public String getFormat() { - return myFormat; - } - - public void setFormat(String format) { - myFormat = format; - } - - @Override - public String toString() { - return super.toString() + " : " + getPresentation(); - } -} - - diff --git a/python/src/com/jetbrains/python/debugger/array/NumpyArrayValueProvider.java b/python/src/com/jetbrains/python/debugger/array/NumpyArrayTable.java similarity index 79% rename from python/src/com/jetbrains/python/debugger/array/NumpyArrayValueProvider.java rename to python/src/com/jetbrains/python/debugger/array/NumpyArrayTable.java index fd98c5fc7d0d..180378bd9d93 100644 --- a/python/src/com/jetbrains/python/debugger/array/NumpyArrayValueProvider.java +++ b/python/src/com/jetbrains/python/debugger/array/NumpyArrayTable.java @@ -22,20 +22,15 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; -import com.intellij.ui.AppUIUtil; -import com.intellij.ui.table.JBTable; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import com.intellij.xdebugger.frame.XValue; -import com.intellij.xdebugger.frame.XValueNode; import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; -import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; -import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; -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.management.InvalidAttributeValueException; +import javax.swing.*; import javax.swing.table.TableCellEditor; import java.awt.*; import java.awt.event.*; @@ -47,18 +42,17 @@ import java.util.regex.Pattern; /** * @author amarch */ -public class NumpyArrayValueProvider extends ArrayValueProvider { - private PyViewArrayAction.MyDialog myDialog; - private ArrayTableForm myComponent; - private JBTable myTable; +public class NumpyArrayTable { + private final PyDebugValue myValue; + private final PyViewArrayAction.ViewArrayDialog myDialog; + private final ArrayTableForm myComponent; + private final JTable myTable; private Project myProject; private PyDebuggerEvaluator myEvaluator; - private NumpyArraySlice myLastPresentation; private String myDtypeKind; private int[] myShape; private ArrayTableCellRenderer myTableCellRenderer; private PagingTableModel myPagingModel; - private boolean lastSuccessful = false; private final static int COLUMNS_IN_DEFAULT_SLICE = 40; private final static int ROWS_IN_DEFAULT_SLICE = 40; @@ -75,62 +69,28 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.array.NumpyArrayValueProvider"); - public NumpyArrayValueProvider(@NotNull XValueNode node, PyViewArrayAction.MyDialog dialog, @NotNull Project project) { - super(node); + public NumpyArrayTable(@NotNull Project project, + @NotNull PyViewArrayAction.ViewArrayDialog dialog, @NotNull PyDebugValue value) { + myValue = value; myDialog = dialog; - if (dialog != null) { - myComponent = dialog.getComponent(); - myTable = myComponent.getTable(); - } + myComponent = new ArrayTableForm(project); + myTable = myComponent.getTable(); myProject = project; - myEvaluator = new PyDebuggerEvaluator(project, getValueContainer().getFrameAccessor()); + myEvaluator = new PyDebuggerEvaluator(project, getDebugValue().getFrameAccessor()); } - private PagingTableModel getPagingModel(@NotNull int[] shape, boolean rendered, final NumpyArraySlice mainSlice) { + public ArrayTableForm getComponent() { + return myComponent; + } + + private PagingTableModel createTableModel(@NotNull int[] shape) { final int columns = Math.min(getMaxColumn(shape), COLUMNS_IN_DEFAULT_VIEW); int rows = Math.min(getMaxRow(shape), ROWS_IN_DEFAULT_VIEW); if (columns == 0 || rows == 0) { showError("Slice with zero axis shape."); } - return new PagingTableModel(rows, columns, rendered, this, mainSlice) { - - private final String myFormat = mainSlice.getFormat(); - private final String myBaseSlice = mainSlice.getBaseSlice(); - - @Override - protected NumpyArraySlice createChunk(int rows, int columns, int rOffset, int cOffset) { - return new NumpyArraySlice(myBaseSlice, rows, columns, rOffset, cOffset, myFormat, getInstance()); - } - - @Override - protected Runnable getDataEvaluator(final ArrayChunk chunk) { - final NumpyArraySlice arraySlice = - new NumpyArraySlice(chunk.getBaseSlice(), chunk.getRows(), chunk.getColumns(), chunk.getRowOffset(), chunk.getColOffset(), - myFormat, getInstance()); - - if (arraySlice.getFormat().isEmpty()) { - arraySlice.setFormat(getDefaultFormat()); - } - - return new Runnable() { - public void run() { - if (!arraySlice.dataFilled()) { - arraySlice.fillData(new Runnable() { - @Override - public void run() { - //check that we still running on the right model - if (!myBaseSlice.equals(getModelFullChunk().getBaseSlice()) || !myFormat.equals(getModelFullChunk().getFormat())) { - return; - } - notifyChunkLoaded(arraySlice); - } - }); - } - } - }; - } - }; + return new PagingTableModel(rows, columns, this); } private void initComponent() { @@ -232,23 +192,19 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { }); } - public NumpyArrayValueProvider getInstance() { - return this; - } - - public PyDebugValue getValueContainer() { - return (PyDebugValue)((XValueNodeImpl)myBaseNode).getValueContainer(); + public PyDebugValue getDebugValue() { + return myValue; } public PyDebuggerEvaluator getEvaluator() { return myEvaluator; } - public void startFillTable() { + public void init() { Runnable returnToFillTable = new Runnable() { @Override public void run() { - startFillTable(); + init(); } }; @@ -282,9 +238,7 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { } } }); - startFillTable(new NumpyArraySlice(getDefaultPresentation(), Math.min(getMaxRow(myShape), ROWS_IN_DEFAULT_VIEW), - Math.min(getMaxColumn(myShape), COLUMNS_IN_DEFAULT_VIEW), 0, 0, getDefaultFormat(), getInstance()), - false, false); + initTableModel(false); } private static String getTitlePresentation(String slice) { @@ -466,7 +420,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { } } - @Override public boolean isNumeric() { if (myDtypeKind != null) { return "biufc".contains(myDtypeKind.substring(0, 1)); @@ -474,15 +427,8 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { return false; } - private void startFillTable(final NumpyArraySlice arraySlice, boolean rendered, final boolean inPlace) { - if (myLastPresentation != null && - arraySlice.getBaseSlice().equals(myLastPresentation.getBaseSlice()) && - arraySlice.getFormat().equals(myLastPresentation.getFormat()) && lastSuccessful) { - return; - } - - lastSuccessful = false; - myPagingModel = getPagingModel(myShape, rendered, arraySlice); + private void initTableModel(final boolean inPlace) { + myPagingModel = createTableModel(myShape); DebuggerUIUtil.invokeLater(new Runnable() { @Override @@ -544,15 +490,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { myEvaluator.evaluate(changeValExpression(), new XDebuggerEvaluator.XEvaluationCallback() { @Override public void evaluated(@NotNull XValue result) { - AppUIUtil.invokeOnEdt(new Runnable() { - @Override - public void run() { - XDebuggerTree tree = ((XValueNodeImpl)myBaseNode).getTree(); - final XDebuggerTreeState treeState = XDebuggerTreeState.saveState(tree); - tree.rebuildAndRestore(treeState); - } - }); - XDebuggerEvaluator.XEvaluationCallback callback = new XDebuggerEvaluator.XEvaluationCallback() { @Override public void evaluated(@NotNull XValue value) { @@ -616,31 +553,11 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { this.myShape = shape; } - public void completeCurrentLoad() { - setBusy(false); - if (myPagingModel.getColumnCount() < getMaxColumn(myShape) || myPagingModel.getRowCount() < getMaxRow(myShape)) { - String hintMessage = LOAD_SMALLER_SLICE; - if (isNumeric()) { - hintMessage += "\n" + DISABLE_COLOR_FOR_HUGE_ARRAY; - } - showInfoHint(hintMessage); - } - } - public void showError(String message) { myDialog.setError(message); setBusy(false); } - public void showError(String message, NumpyArraySlice chunk) { - if (!chunk.getFormat().equals(getModelFullChunk().getFormat()) || - !chunk.getBaseSlice().equals(getModelFullChunk().getBaseSlice())) { - //ignore error message from previous task - return; - } - showError(message); - } - public void showInfoHint(final String message) { DebuggerUIUtil.invokeLater(new Runnable() { @Override @@ -710,7 +627,7 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { } myShape = shape; - startRefillTable(false); + reset(); } private static String getEvalShapeCommand(@NotNull String slice) { @@ -737,20 +654,12 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { } private void doApplyFormat() { - startRefillTable(true); + reset(); } - private void startRefillTable(boolean inPlace) { - clearTableData(); - Rectangle vr = myTable.getVisibleRect(); - int rOffset = myTable.rowAtPoint(vr.getLocation()); - int cOffset = myTable.columnAtPoint(vr.getLocation()); - startFillTable(new NumpyArraySlice(getSliceText(), 0, 0, rOffset, cOffset, getFormat(), this), true, inPlace); - } - - private void clearTableData() { + private void reset() { clearErrorMessage(); - ((PagingTableModel)myTable.getModel()).clearCached(); + initTableModel(true); } public String evalTypeFunc(String format) { @@ -771,24 +680,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { return 0; } - public void notifyChunkLoaded(final NumpyArraySlice arraySlice) { - myLastPresentation = arraySlice; - lastSuccessful = true; - myPagingModel.addDataInCache(arraySlice.getRowOffset(), arraySlice.getColOffset(), arraySlice.getData()); - myPagingModel.runNextLoadingTask(); - DebuggerUIUtil.invokeLater(new Runnable() { - public void run() { - myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer); - myDialog.setTitle(getTitlePresentation(getSliceText())); - for (int r = 0; r < arraySlice.getRows(); r++) { - for (int c = 0; c < arraySlice.getColumns(); c++) { - myPagingModel.fireTableCellUpdated(r + arraySlice.getRowOffset(), c + arraySlice.getColOffset()); - } - } - } - }); - } - public void setBusy(final boolean busy) { DebuggerUIUtil.invokeLater(new Runnable() { @Override @@ -798,10 +689,6 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { }); } - private NumpyArraySlice getModelFullChunk() { - return (NumpyArraySlice)myPagingModel.getFullChunk(); - } - /** * @return double presentation from [0:1] range */ @@ -852,4 +739,8 @@ public class NumpyArrayValueProvider extends ArrayValueProvider { throw new IllegalArgumentException("Not a valid python complex value: " + pyComplexValue); } } + + public String getNodeName() { + return (myValue).getName(); + } } diff --git a/python/src/com/jetbrains/python/debugger/array/PagingTableModel.java b/python/src/com/jetbrains/python/debugger/array/PagingTableModel.java index 7d020568c15e..3e8f13aa53e7 100644 --- a/python/src/com/jetbrains/python/debugger/array/PagingTableModel.java +++ b/python/src/com/jetbrains/python/debugger/array/PagingTableModel.java @@ -15,74 +15,86 @@ */ package com.jetbrains.python.debugger.array; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Pair; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBViewport; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.Queue; +import com.jetbrains.python.debugger.PyDebugValue; import javax.swing.table.AbstractTableModel; import java.awt.*; -import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.ExecutionException; -public abstract class PagingTableModel extends AbstractTableModel { - private static final int CHUNK_COL_SIZE = 50; - private static final int CHUNK_ROW_SIZE = 50; +/** + * @author traff + */ +public class PagingTableModel extends AbstractTableModel { + private static final int CHUNK_COL_SIZE = 2; //TODO set to 100 + private static final int CHUNK_ROW_SIZE = 2; private static final int DEFAULT_MAX_CACHED_SIZE = 100; public static final String EMPTY_CELL_VALUE = "..."; - private HashMap myCachedData = new HashMap(); - private SortedSet myPendingSet = new TreeSet(); - private Queue cachedChunkKeys = new Queue(DEFAULT_MAX_CACHED_SIZE + 1); + private LoadingCache, Object[][]> myChunkCache = CacheBuilder.newBuilder().build( + new CacheLoader, Object[][]>() { + @Override + public Object[][] load(Pair key) throws Exception { + PyDebugValue value = myProvider.getDebugValue(); + value = new PyDebugValue(myProvider.getSliceText(), value.getType(), value.getValue(), value.isContainer(), value.isErrorOnEval(), + value.getFrameAccessor()); + return value.getFrameAccessor().getArrayItems(value, key.first, key.second, CHUNK_COL_SIZE, CHUNK_ROW_SIZE, myProvider.getFormat()); + } + }); - private ArrayChunk evaluatedChunk; - - private boolean myRendered; private int myRows = 0; private int myColumns = 0; - private NumpyArrayValueProvider myProvider; - private ArrayChunk myFullChunk; + private NumpyArrayTable myProvider; - public PagingTableModel(int rows, int columns, boolean rendered, NumpyArrayValueProvider provider, ArrayChunk fullChunk) { + public PagingTableModel(int rows, int columns, NumpyArrayTable provider) { myRows = rows; myColumns = columns; - myRendered = rendered; myProvider = provider; - myFullChunk = fullChunk; } @Override public boolean isCellEditable(int row, int column) { - if (getValueAt(row, column).equals(EMPTY_CELL_VALUE)) { - return false; - } - return true; + return !getValueAt(row, column).equals(EMPTY_CELL_VALUE); } public Object getValueAt(int row, int col) { - //prevent data evaluation for full table - if (row == (getRowCount() - 1) && col == (getColumnCount() - 1) && !myRendered) { - myRendered = true; - return EMPTY_CELL_VALUE; - } + Pair key = itemToChunkKey(row, col); - if (!myRendered) { - return EMPTY_CELL_VALUE; - } + try { + Object[][] chunk = myChunkCache.get(key); - String key = formMapKey(row, col); - if (!myCachedData.containsKey(key)) { - scheduleChunkAt(row, col); - return EMPTY_CELL_VALUE; - } + int r = row % CHUNK_ROW_SIZE; + int c = col % CHUNK_COL_SIZE; - return myCachedData.get(key)[row % CHUNK_ROW_SIZE][col % CHUNK_COL_SIZE]; + if (r < chunk.length) { + if (c < chunk[r].length) { + return chunk[r][c]; + } + else { + return EMPTY_CELL_VALUE; + } + } + else { + return EMPTY_CELL_VALUE; + } + } + catch (ExecutionException e) { + return EMPTY_CELL_VALUE; //TODO: handle it + } } - private static String formMapKey(int row, int col) { - return "[" + getPageRowStart(row) + "," + getPageColStart(col) + "]"; + private static Pair itemToChunkKey(int row, int col) { + return Pair.create(getPageRowStart(row), getPageColStart(col)); } private static int getPageRowStart(int rowOffset) { @@ -93,78 +105,6 @@ public abstract class PagingTableModel extends AbstractTableModel { return colOffset - (colOffset % CHUNK_COL_SIZE); } - private void scheduleChunkAt(int rOffset, int cOffset) { - if (isPending(rOffset, cOffset)) { - return; - } - - int startROffset = getPageRowStart(rOffset); - int rLength = Math.min(CHUNK_ROW_SIZE, myRows - startROffset); - - int startCOffset = getPageColStart(cOffset); - int cLength = Math.min(CHUNK_COL_SIZE, myColumns - startCOffset); - - scheduleLoadData(startROffset, rLength, startCOffset, cLength); - } - - private boolean isPending(int rOffset, int cOffset) { - int sz = myPendingSet.size(); - if (sz == 0) return false; - if (sz == 1) { - // special case (for speed) - ArrayChunk seg = myPendingSet.first(); - return seg.contains(rOffset, cOffset); - } - - ArrayChunk lo = createChunk(0, 0, getPageRowStart(rOffset), getPageColStart(cOffset)); - ArrayChunk hi = createChunk(0, 0, getPageRowStart(rOffset + CHUNK_ROW_SIZE), getPageColStart(cOffset + CHUNK_COL_SIZE)); - - for (ArrayChunk seg : myPendingSet.subSet(lo, hi)) { - if (seg.contains(rOffset, cOffset)) return true; - } - return false; - } - - protected abstract ArrayChunk createChunk(int rows, int columns, int rOffset, int cOffset); - - protected abstract Runnable getDataEvaluator(final ArrayChunk chunk); - - public void runNextLoadingTask() { - if (evaluatedChunk != null) { - myPendingSet.remove(evaluatedChunk); - evaluatedChunk = null; - } - - if (myPendingSet.size() > 0) { - evaluatedChunk = myPendingSet.first(); - ApplicationManager.getApplication().executeOnPooledThread(getDataEvaluator(evaluatedChunk)); - myProvider.setBusy(true); - } - else { - myProvider.completeCurrentLoad(); - } - } - - private void scheduleLoadData(final int rOffset, final int rLength, final int cOffset, final int cLength) { - final ArrayChunk segment = createChunk(rLength, cLength, rOffset, cOffset); - myPendingSet.add(segment); - - if (evaluatedChunk == null) { - runNextLoadingTask(); - } - } - - protected void addDataInCache(int rOffset, int cOffset, Object[][] newData) { - String key = formMapKey(rOffset, cOffset); - myCachedData.put(key, newData); - cachedChunkKeys.addLast(key); - - if (myCachedData.size() == DEFAULT_MAX_CACHED_SIZE) { - String old = cachedChunkKeys.pullFirst(); - myCachedData.remove(old); - } - } - public int getColumnCount() { return myColumns; } @@ -177,46 +117,13 @@ public abstract class PagingTableModel extends AbstractTableModel { return myRows; } - public void clearCached() { - myCachedData = new HashMap(); - myPendingSet = new TreeSet(); - cachedChunkKeys = new Queue(DEFAULT_MAX_CACHED_SIZE + 1); - evaluatedChunk = null; - } - - public static class LazyViewport extends JBViewport { - public static JBScrollPane createLazyScrollPaneFor(Component view) { - LazyViewport vp = new LazyViewport(); - vp.setView(view); - JBScrollPane scrollpane = new JBScrollPane(); - scrollpane.setViewport(vp); - return scrollpane; - } - - public void setViewPosition(Point p) { - Component parent = getParent(); - if (parent instanceof JBScrollPane && - (((JBScrollPane)parent).getVerticalScrollBar().getValueIsAdjusting() || - ((JBScrollPane)parent).getHorizontalScrollBar().getValueIsAdjusting())) { - return; - } - super.setViewPosition(p); - } - } - public void forcedChange(int row, int col, Object value) { - String key = formMapKey(row, col); - if (myCachedData.containsKey(key)) { - Object[][] cachedData = myCachedData.get(key); - cachedData[row - getPageRowStart(row)][col - getPageColStart(col)] = value; - myCachedData.put(key, cachedData); + Object[][] chunk = myChunkCache.getIfPresent(itemToChunkKey(row, col)); + if (chunk != null) { + chunk[row - getPageRowStart(row)][col - getPageColStart(col)] = value; } else { throw new IllegalArgumentException("Forced to change empty cell in " + row + " row and " + col + "column."); } } - - public ArrayChunk getFullChunk() { - return myFullChunk; - } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/debugger/array/PyViewArrayAction.java b/python/src/com/jetbrains/python/debugger/array/PyViewArrayAction.java index 39a077ef4b6c..2c60afa8bab9 100644 --- a/python/src/com/jetbrains/python/debugger/array/PyViewArrayAction.java +++ b/python/src/com/jetbrains/python/debugger/array/PyViewArrayAction.java @@ -22,7 +22,6 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; -import com.jetbrains.python.console.PydevConsoleCommunication; import com.jetbrains.python.debugger.PyDebugValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,9 +37,11 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase { @Override protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { - final MyDialog dialog = new MyDialog(e.getProject()); - dialog.setValue(node); - dialog.show(); + Project p = e.getProject(); + if (p != null && node != null && node.getValueContainer() instanceof PyDebugValue && node.isComputed()) { + final ViewArrayDialog dialog = new ViewArrayDialog(p, (PyDebugValue)(node.getValueContainer())); + dialog.show(); + } } @Nullable @@ -53,18 +54,14 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase { public void update(AnActionEvent e) { TreePath[] paths = getSelectedNodes(e.getDataContext()); if (paths != null) { - if(paths.length > 1) { + if (paths.length > 1) { e.getPresentation().setVisible(false); return; } - XValueNodeImpl node = getSelectedNode(e.getDataContext()); + XValueNodeImpl node = getSelectedNode(e.getDataContext()); if (node != null && node.getValueContainer() instanceof PyDebugValue && node.isComputed()) { - PyDebugValue debugValue = (PyDebugValue) node.getValueContainer(); - if (debugValue.getFrameAccessor() instanceof PydevConsoleCommunication) { - e.getPresentation().setVisible(false); - return; - } + PyDebugValue debugValue = (PyDebugValue)node.getValueContainer(); String nodeType = debugValue.getType(); if ("ndarray".equals(nodeType)) { @@ -76,43 +73,23 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase { e.getPresentation().setVisible(false); } - protected class MyDialog extends DialogWrapper { + protected static class ViewArrayDialog extends DialogWrapper { private Project myProject; - private ArrayTableForm myComponent; + private NumpyArrayTable myNumpyArrayTable; - private MyDialog(Project project) { + private ViewArrayDialog(@NotNull Project project, PyDebugValue debugValue) { super(project, false); setModal(false); setCancelButtonText("Close"); setCrossClosesWindow(true); myProject = project; - myComponent = new ArrayTableForm(project); + myNumpyArrayTable = new NumpyArrayTable(myProject, this, debugValue); + myNumpyArrayTable.init(); init(); } - public void setValue(XValueNodeImpl node) { - - if (node.getValueContainer() instanceof PyDebugValue) { - PyDebugValue debugValue = (PyDebugValue)node.getValueContainer(); - - if ("ndarray".equals(debugValue.getType())) { - myComponent.setDefaultStatus(); - final NumpyArrayValueProvider valueProvider = new NumpyArrayValueProvider(node, this, myProject); - try { - valueProvider.startFillTable(); - } - catch (Exception e) { - setErrorText(e.getMessage()); - } - } - else { - myComponent.setNotApplicableStatus(node); - } - } - } - public void setError(String text) { //todo: think about this usage setErrorText(text); @@ -131,11 +108,11 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase { @Override protected JComponent createCenterPanel() { - return myComponent.getMainPanel(); + return myNumpyArrayTable.getComponent().getMainPanel(); } public ArrayTableForm getComponent() { - return myComponent; + return myNumpyArrayTable.getComponent(); } } } From 5eb602ccb4ebf7c7e23c9cf14ef2ea8530455e71 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 5 Nov 2014 13:35:06 +0100 Subject: [PATCH 2/2] Provide method to avoid loading all the table data. --- .../src/com/intellij/ui/table/JBTable.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/table/JBTable.java b/platform/platform-api/src/com/intellij/ui/table/JBTable.java index 9e6a7cfb1444..bda0fef97963 100644 --- a/platform/platform-api/src/com/intellij/ui/table/JBTable.java +++ b/platform/platform-api/src/com/intellij/ui/table/JBTable.java @@ -59,6 +59,7 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component private AsyncProcessIcon myBusyIcon; private boolean myBusy; + private int myMaxItemsForSizeCalculation = Integer.MAX_VALUE; public JBTable() { this(new DefaultTableModel()); @@ -153,8 +154,8 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component if (myRowHeight < 0) { try { myRowHeightIsComputing = true; - for (int row = 0; row < getRowCount(); row++) { - for (int column = 0; column < getColumnCount(); column++) { + for (int row = 0; row < Math.min(getRowCount(), myMaxItemsForSizeCalculation); row++) { + for (int column = 0; column < Math.min(getColumnCount(), myMaxItemsForSizeCalculation); column++) { final TableCellRenderer renderer = getCellRenderer(row, column); if (renderer != null) { final Object value = getValueAt(row, column); @@ -818,4 +819,15 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component return resizingAllowed && columnModel.getColumn(columnIdx).getResizable(); } } + + /** + * JTable gets table data from model lazily - only for a table part to be shown. + * JBTable loads all the data on initialization to calculate cell size. + * This methods provides possibility to calculate size without loading all the table data. + * + * @param maxItemsForSizeCalculation maximum number ot items in table to be loaded for size calculation + */ + public void setMaxItemsForSizeCalculation(int maxItemsForSizeCalculation) { + myMaxItemsForSizeCalculation = maxItemsForSizeCalculation; + } }