Package com.persistit.ui

Source Code of com.persistit.ui.PersistitTableModel$Row

/**
* Copyright 2005-2012 Akiban Technologies, Inc.
*
* 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.persistit.ui;

import java.awt.Component;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import com.persistit.Exchange;
import com.persistit.Key;
import com.persistit.Value;
import com.persistit.exception.PersistitException;

public class PersistitTableModel extends AbstractTableModel {
    private final static int SCROLL_EXTRA = 100;
    private final static float SCROLL_FACTOR = 1.3f;
    private final static int ROW_CACHE_SIZE = 100;
    private final static int INITIAL_ROW_COUNT_ESTIMATE = 10000;

    private final static String[] COLUMN_HEADER_NAMES = { "Index", "Key", "Type", "Value", };

    private final static int[] COLUMN_HEADER_WIDTHS = { 50, 450, 50, 450, };

    private final static int COLUMN_COUNT = COLUMN_HEADER_NAMES.length;
    /**
     * The Exchange on which the display will be based.
     */
    private Exchange _exchange;
    /**
     * The root key for this display.
     */
    private Key _rootKey;
    /**
     * The Row that corresponds to the root Key.
     */
    private final Row _rootRow = new Row(new byte[0]);
    /**
     * A list of contiguous rows. The first element in the list corresponds with
     * the row index in _rowCacheOffset.
     */
    private final RowCache _rowCache = new RowCache();
    /**
     * There are at least this many rows.
     */
    private int _rowCountActual;
    /**
     * The last row count estimate that was returned by getRowCount().
     */
    private int _rowCountEstimate;
    /**
     * Count of getValue operations satisfied from cache
     */
    private int _rowGetValueHits;
    /**
     * Count of getValue operations
     */
    private int _rowGetValueOperations;

    /**
     * Establishes the supplied Exchange as the root of a display tree.
     *
     * @param exchange
     */
    public void setExchange(final Exchange exchange) {
        _exchange = exchange;
        _rootKey = new Key(_exchange.getKey());
        _rowCache.clear();
        _rowCountActual = -1;
        _rowCountEstimate = INITIAL_ROW_COUNT_ESTIMATE;
        fireTableChanged(new TableModelEvent(this));
    }

    public Exchange getExchange() {
        return _exchange;
    }

    @Override
    public Object getValueAt(final int rowIndex, final int columnIndex) {
        if (rowIndex < 0) {
            throw new IllegalArgumentException("rowIndex=" + rowIndex);
        }

        if (_rowCountActual >= 0 && rowIndex > _rowCountActual) {
            return _rootRow;
        }

        _rowGetValueOperations++;

        try {
            Row row = _rowCache.get(rowIndex);
            if (row != null) {
                _rowGetValueHits++;
                return row;
            }

            int minCachedIndex = _rowCache.getFirstIndex();
            int maxCachedIndex = _rowCache.getLastIndex();

            if (rowIndex < minCachedIndex) {
                // (1) are we going to get the row by backing up
                // or by starting at the beginning?
                //
                if (rowIndex > (minCachedIndex / 2)) {
                    row = _rowCache.get(minCachedIndex);
                    for (int index = minCachedIndex; index > rowIndex;) {
                        if (row == null)
                            return null;
                        row = row.getNextRow(false);
                        index--;
                        if (row == null) {
                            row = _rootRow;
                        }
                        _rowCache.put(index, row);
                    }
                    return row;
                } else {
                    maxCachedIndex = 0;
                    minCachedIndex = 0;
                }
            }
            int index;
            if (maxCachedIndex == 0) {
                index = 0;
                row = _rootRow;
                if (rowIndex == 0)
                    _rowCache.put(index, row);
            } else {
                index = maxCachedIndex - 1;
                row = _rowCache.get(index);
            }
            for (; index < rowIndex;) {
                if (row == null)
                    return null;
                row = row.getNextRow(true);
                index++;

                if (row == null) {
                    final boolean changed = index != _rowCountEstimate;
                    // we are at the end!
                    final int previousEstimate = _rowCountEstimate;
                    _rowCountEstimate = index;
                    _rowCountActual = index;
                    if (changed)
                        fireSizeChanged(previousEstimate);
                    return null;
                } else
                    _rowCache.put(index, row);
            }
            if (_rowCountActual < 0 && _rowCountEstimate - rowIndex < SCROLL_EXTRA) {
                final int previousEstimate = _rowCountEstimate;
                _rowCountEstimate = (int) ((_rowCountEstimate + SCROLL_EXTRA) * SCROLL_FACTOR);
                fireSizeChanged(previousEstimate);
            }
            return row;
        } catch (final PersistitException de) {
            return de;
        }
    }

    @Override
    public int getRowCount() {
        return _rowCountEstimate;
    }

    @Override
    public int getColumnCount() {
        return COLUMN_COUNT;
    }

    /**
     * Sets up the TableColumnModel for a supplied JTable
     *
     * @param table
     */
    public void setupColumns(final JTable table) {
        final TableColumnModel tcm = table.getColumnModel();
        final int count = tcm.getColumnCount();
        final String[] headers = COLUMN_HEADER_NAMES;
        final int[] widths = COLUMN_HEADER_WIDTHS;
        final TableCellRenderer renderer = new ExchangeTableCellRenderer();

        for (int i = 0; i < headers.length; i++) {
            TableColumn tc;
            if (i >= count) {
                tc = new TableColumn();
                tcm.addColumn(tc);
            } else {
                tc = tcm.getColumn(i);
            }
            tc.setHeaderValue(headers[i]);
            tc.setPreferredWidth(widths[i]);
            tc.setCellRenderer(renderer);
        }
    }

    private void fireSizeChanged(final int previousEstimate) {
        if (_rowCountEstimate > previousEstimate) {
            fireTableRowsInserted(previousEstimate, _rowCountEstimate - 1);
        } else if (_rowCountEstimate < previousEstimate) {
            fireTableRowsDeleted(_rowCountEstimate, previousEstimate - 1);
        }
    }

    //
    // Helper classes
    //

    private class Row {
        private final byte[] _keyBytes;
        private int _index = -1; // TODO - debugging

        private Row(final byte[] keyBytes) {
            _keyBytes = keyBytes;
        }

        /**
         * Determines whether children of the Key described by this Row should
         * be displayed.
         *
         * @return <i>true</i> if the children of this Row should be displayed.
         */
        private boolean isExpanded() {
            return true;
        }

        private Row getNextRow(final boolean forward) throws PersistitException {
            final Exchange ex = setupExchange();
            final boolean expanded = (isExpanded());
            if (!ex.traverse(forward ? Key.GT : Key.LT, expanded)) {
                return null;
            }
            final Key key = ex.getKey();
            if (key.compareKeyFragment(_rootKey, 0, _rootKey.getEncodedSize()) != 0) {
                return null;
            }
            final int keyBytesSize = key.getEncodedSize() - _rootKey.getEncodedSize();
            final byte[] keyBytes = new byte[keyBytesSize];
            System.arraycopy(key.getEncodedBytes(), _rootKey.getEncodedSize(), keyBytes, 0, keyBytesSize);
            return new Row(keyBytes);
        }

        private String getValueTypeString() {
            try {
                final Exchange ex = setupExchange();
                if (ex.getKey().getEncodedSize() == 0)
                    return "";
                ex.fetch();
                final Value value = ex.getValue();
                if (!value.isDefined())
                    return "undefined";
                return value.getType().getName();
            } catch (final PersistitException de) {
                return de.toString();
            }
        }

        private String getValueString() {
            try {
                final Exchange ex = setupExchange();
                if (ex.getKey().getEncodedSize() == 0)
                    return "";
                ex.fetch();
                final Value value = ex.getValue();
                if (value.getEncodedSize() > 50000) {
                    return "Too long to display: " + value.getEncodedSize();
                } else {
                    return value.toString();
                }
            } catch (final PersistitException de) {
                return de.toString();
            }
        }

        private Key getKey() {
            final Exchange ex = setupExchange();
            return ex.getKey();
        }

        private Value getValue() throws PersistitException, IOException {
            final Exchange ex = setupExchange();
            ex.fetch();
            return ex.getValue();
        }

        private String getKeyString() {
            final Exchange ex = setupExchange();
            ex.getKey().setIndex(0);
            return ex.getKey().toString();
        }

        private Exchange setupExchange() {
            final Exchange ex = PersistitTableModel.this.getExchange();
            final Key key = ex.getKey();
            System.arraycopy(_rootKey.getEncodedBytes(), 0, key.getEncodedBytes(), 0, _rootKey.getEncodedSize());
            System.arraycopy(_keyBytes, 0, key.getEncodedBytes(), _rootKey.getEncodedSize(), _keyBytes.length);
            key.setEncodedSize(_rootKey.getEncodedSize() + _keyBytes.length);
            return ex;
        }

        @Override
        public String toString() {
            return _index + ": " + setupExchange().getKey().toString() + "-->" + getValueString();
        }
    }

    private static class RowCache {
        /**
         * Array of cached rows, organized as a ring buffer.
         */
        private final Row[] _cache = new Row[ROW_CACHE_SIZE];
        /**
         * Offset of lowest indexed Row in the cache.
         */
        private int _tail = 0;
        /**
         * Offset one past highest indexed Row in the cache.
         */
        private int _head = 0;
        /**
         * The index within the table of the lowest indexed Row in the cache.
         */
        private int _firstCachedIndex = 0;

        private void clear() {
            _tail = 0;
            _head = 0;
            for (int index = 0; index < ROW_CACHE_SIZE; index++) {
                _cache[index] = null;
            }
        }

        private int getFirstIndex() {
            return _firstCachedIndex;
        }

        private int getLastIndex() {
            return _firstCachedIndex + size();
        }

        private int size() {
            int size = _head - _tail;
            if (size < 0)
                size += ROW_CACHE_SIZE;
            return size;
        }

        private Row get(int index) {
            index -= _firstCachedIndex;
            if (index < 0)
                return null;
            if (index >= size())
                return null;
            index = (index + _tail) % ROW_CACHE_SIZE;
            return _cache[index];
        }

        private void put(int index, final Row row) {
            row._index = index;
            index -= _firstCachedIndex;
            final int size = size();
            if (index == -1) {
                _tail--;
                if (_tail < 0)
                    _tail += ROW_CACHE_SIZE;
                if (_head == _tail) {
                    _head--;
                    if (_head < 0)
                        _head += ROW_CACHE_SIZE;
                }
                _firstCachedIndex--;
                _cache[_tail] = row;
            } else if (index == size) {
                _cache[_head] = row;
                _head++;
                if (_head == ROW_CACHE_SIZE)
                    _head = 0;
                if (_head == _tail) {
                    _cache[_tail] = null;
                    _tail++;
                    if (_tail == ROW_CACHE_SIZE)
                        _tail = 0;
                    _firstCachedIndex++;
                }
            } else if (index >= 0 && index < size) {
                index -= _tail;
                if (index < 0)
                    index += ROW_CACHE_SIZE;
                _cache[index] = row;
            } else {
                clear();
                _cache[0] = row;
                _head++;
                _firstCachedIndex += index;
            }
        }
    }

    private static class ExchangeTableCellRenderer extends DefaultTableCellRenderer {
        private ExchangeTableCellRenderer() {
            setFont(new Font("Lucida", Font.PLAIN, 13));
            setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 4));
        }

        @Override
        public Component getTableCellRendererComponent(final JTable table, final Object value,
                final boolean isSelected, final boolean hasFocus, final int rowIndex, final int columnIndex) {

            final JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
                    rowIndex, columnIndex);

            final Row row = (Row) value;
            if (row == null) {
                label.setText("");
            } else
                switch (columnIndex) {
                case 0: {
                    label.setText(Integer.toString(rowIndex));
                    label.setHorizontalAlignment(SwingConstants.RIGHT);
                    break;
                }
                case 1: {
                    label.setText(row.getKeyString());
                    label.setHorizontalAlignment(SwingConstants.LEFT);
                    break;
                }

                case 2: {
                    label.setText(row.getValueTypeString());
                    label.setHorizontalAlignment(SwingConstants.CENTER);
                    break;
                }

                case 3: {
                    label.setText(row.getValueString());
                    label.setHorizontalAlignment(SwingConstants.LEFT);
                    break;
                }
                default: {
                    label.setText("Unknown column " + columnIndex);
                }
                }
            return label;
        }

        @Override
        protected void setValue(final Object value) {
        }

    }

    public JTable createTreeDisplayTable() {
        final TreeDisplayTable tdt = new TreeDisplayTable(this);
        setupColumns(tdt);
        return tdt;
    }

    private static class TreeDisplayTable extends JTable {
        TreeDisplayTable(final PersistitTableModel dtm) {
            super(dtm);
        }

        @Override
        public String getToolTipText(final MouseEvent me) {
            final int row = rowAtPoint(me.getPoint());
            final int col = columnAtPoint(me.getPoint());
            if (row >= 0 && col >= 0) {
                final Object value = getModel().getValueAt(row, col);
                if (value != null) {
                    final TableCellRenderer tcr = getCellRenderer(row, col);
                    final Component rc = tcr.getTableCellRendererComponent(this, value, false, false, row, col);
                    if (rc instanceof JLabel) {
                        final String s = ((JLabel) rc).getText();
                        if (s.length() > 20)
                            return s;
                    }
                }
            }
            return null;
        }
    }

}
TOP

Related Classes of com.persistit.ui.PersistitTableModel$Row

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.