de.codesourcery.eve.skills.ui.components.impl.ImportMarketLogFileComponent.java Source code

Java tutorial

Introduction

Here is the source code for de.codesourcery.eve.skills.ui.components.impl.ImportMarketLogFileComponent.java

Source

/**
 * Copyright 2004-2009 Tobias Gierke <tobias.gierke@code-sourcery.de>
 *
 * 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 de.codesourcery.eve.skills.ui.components.impl;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.plaf.UIResource;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;

import de.codesourcery.eve.skills.datamodel.PriceInfo;
import de.codesourcery.eve.skills.datamodel.PriceInfo.Type;
import de.codesourcery.eve.skills.market.MarketLogEntry;
import de.codesourcery.eve.skills.market.impl.MarketLogFile;
import de.codesourcery.eve.skills.market.impl.MarketLogFile.IMarketLogFilter;
import de.codesourcery.eve.skills.ui.components.AbstractEditorComponent;
import de.codesourcery.eve.skills.ui.config.IAppConfigProvider;
import de.codesourcery.eve.skills.ui.model.AbstractTableModel;
import de.codesourcery.eve.skills.ui.model.DefaultComboBoxModel;
import de.codesourcery.eve.skills.ui.model.TableColumnBuilder;
import de.codesourcery.eve.skills.ui.utils.PopupMenuBuilder;
import de.codesourcery.eve.skills.util.AmountHelper;
import de.codesourcery.eve.skills.utils.DateHelper;
import de.codesourcery.eve.skills.utils.EveDate;
import de.codesourcery.eve.skills.utils.ISystemClock;

public class ImportMarketLogFileComponent extends AbstractEditorComponent {

    private static final Logger log = Logger.getLogger(MyModel.class);

    // table column indices
    private static final int VALID_IDX = 0;
    private static final int TYPE_IDX = 1;
    private static final int DATE_IDX = 2;
    private static final int VOLUME_IDX = 3;
    private static final int REMAINING_VOLUME_IDX = 4;
    private static final int PRICE_IDX = 5;

    // input file
    private final MarketLogFile logFile;

    @Resource(name = "appconfig-provider")
    private IAppConfigProvider configProvider;

    @Resource(name = "system-clock")
    private ISystemClock systemClock;

    // crap filter
    private final JCheckBox priceFilter = new JCheckBox("Filter by min./max. price ?", false);

    private final JTextField minPriceFilter = new JTextField("1.0");
    private final JTextField maxPriceFilter = new JTextField("1.0");

    private final JCheckBox orderVolumeFilter = new JCheckBox("Filter by order volume ?", false);
    private final JTextField minOrderSize = new JTextField("1.0");
    private final JTextField maxOrderSize = new JTextField("1.0");

    private final JComboBox requiredOrderType = new JComboBox();

    // text are showing min / avg. / max price and standard deviation

    //   private final JTextArea infoArea = new JTextArea( 15 , 30 );
    private final JTextArea infoArea = new JTextArea();

    // Table showing imported data
    private final JTable table = new JTable();
    private final MyModel model = new MyModel();

    // other stuff
    private PopupMenuBuilder popupMenuBuilder;

    private final ActionListener actionListener = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {

            importFilter.updateFilter();

            if (e.getSource() == requiredOrderType) {
                model.getRowSorter().setRowFilter(rowFilter);
            }
        }
    };

    private final MyFilter importFilter = new MyFilter();

    private final RowFilter<AbstractTableModel<LogEntryWrapper>, Integer> rowFilter = new RowFilter<AbstractTableModel<LogEntryWrapper>, Integer>() {

        @Override
        public boolean include(
                javax.swing.RowFilter.Entry<? extends AbstractTableModel<LogEntryWrapper>, ? extends Integer> entry) {

            Integer row = entry.getIdentifier();
            LogEntryWrapper wrapper = entry.getModel().getRow(row);

            return wrapper.entry().getType().matches((Type) requiredOrderType.getSelectedItem());
        }
    };

    private final class MyFilter implements IMarketLogFilter {

        private boolean filterByPrice = false;
        private double minPrice;
        private double maxPrice;

        private boolean filterByVolume = false;
        private double minVolume;
        private double maxVolume;

        private PriceInfo.Type type = PriceInfo.Type.ANY;

        public MyFilter() {
        }

        @Override
        public boolean includeInResult(MarketLogEntry entry) {

            final LogEntryWrapper wrapper = model.getWrapperFor(entry);

            if (wrapper != null && wrapper.isEnteredByUser()) {
                return wrapper.valid;
            }

            if (filterByPrice) {
                if (entry.getPrice() < minPrice || entry.getPrice() > maxPrice) {
                    return false;
                }
            }

            if (filterByVolume) {
                if (entry.getVolume() < minVolume || entry.getVolume() > maxVolume) {
                    return false;
                }
            }

            return entry.getType().matches(this.type);
        }

        public void updateFilter() {

            double value;
            boolean filterChanged = false;

            if (priceFilter.isSelected() != filterByPrice) {
                filterChanged = true;
                this.filterByPrice = priceFilter.isSelected();
            }

            if (this.filterByPrice) {
                value = readDouble(minPriceFilter);
                if (value != minPrice) {
                    this.minPrice = value;
                    filterChanged = true;
                }

                value = readDouble(maxPriceFilter);
                if (value != maxPrice) {
                    this.maxPrice = value;
                    filterChanged = true;
                }

                if (this.minPrice > this.maxPrice) {
                    double tmp = this.maxPrice;
                    this.maxPrice = this.minPrice;
                    this.minPrice = tmp;
                }
            }

            if (orderVolumeFilter.isSelected() != filterByVolume) {
                this.filterByVolume = orderVolumeFilter.isSelected();
                filterChanged = true;
            }

            if (filterByVolume) {
                value = readDouble(minOrderSize);
                if (value != minVolume) {
                    this.minVolume = value;
                    filterChanged = true;
                }

                value = readDouble(maxOrderSize);
                if (value != maxVolume) {
                    this.maxVolume = value;
                    filterChanged = true;
                }

                if (this.minVolume > this.maxVolume) {
                    double tmp = this.maxVolume;
                    this.maxVolume = this.minVolume;
                    this.minVolume = tmp;
                }
            }

            if (this.type != getRequiredOrderType()) {
                this.type = getRequiredOrderType();
                filterChanged = true;
            }

            if (filterChanged) {
                model.filterChanged();
            }

            updateInfoTextArea();
        }

        protected Double readDouble(JTextField field) {
            return Double.parseDouble(field.getText());
        }

        protected void assertValidDouble(JTextField textField) {
            final String value = textField.getText();
            if (value != null) {
                String trimmed = value.trim();
                if (trimmed.length() != value.length()) {
                    textField.setText(trimmed);
                }
                try {
                    Double.parseDouble(trimmed);
                } catch (Exception e) {
                    textField.setText("1.0");
                }
            } else {
                textField.setText("1.0");
            }
        }
    }

    public ImportMarketLogFileComponent(MarketLogFile logFile) {
        if (logFile == null) {
            throw new IllegalArgumentException("logFile cannot be NULL");
        }
        this.logFile = logFile;
    }

    protected PriceInfo.Type getRequiredOrderType() {
        return (Type) requiredOrderType.getSelectedItem();
    }

    protected void updateInfoTextArea() {

        final StringBuilder result = new StringBuilder();

        final MarketLogFile logFile = getMarketLogFile();

        result.append("Item: " + logFile.getInventoryType().getName() + " (" + logFile.getInventoryType().getId()
                + ")\n");
        result.append("Region: " + logFile.getRegion().getName() + "\n\n");

        result.append("Start date: " + DateHelper.format(logFile.getStartDate().getLocalTime()) + "\n");
        result.append("End   date: " + DateHelper.format(logFile.getEndDate().getLocalTime()) + "\n\n");

        result.append("Order count: " + logFile.getOrderCount(Type.ANY) + "\n\n");

        double minPrice = this.logFile.getMinPrice(getRequiredOrderType(), IMarketLogFilter.NOP_FILTER);

        double maxPrice = this.logFile.getMaxPrice(getRequiredOrderType(), IMarketLogFilter.NOP_FILTER);

        double avgPrice = this.logFile.getAveragePrice(getRequiredOrderType(), IMarketLogFilter.NOP_FILTER);

        double stdDeviation = this.logFile.getStandardDeviation(getRequiredOrderType(),
                IMarketLogFilter.NOP_FILTER);

        result.append("---- unfiltered ----\n\n");

        result.append("Minimum price: " + AmountHelper.formatISKAmount(minPrice) + " ISK\n");
        result.append("Average price: " + AmountHelper.formatISKAmount(avgPrice) + " ISK\n");
        result.append("Maximum price: " + AmountHelper.formatISKAmount(maxPrice) + " ISK\n\n");

        result.append("Standard deviation: " + AmountHelper.formatISKAmount(stdDeviation) + " ISK\n\n");

        result.append("---- filtered ----\n\n");

        minPrice = this.logFile.getMinPrice(getRequiredOrderType(), this.importFilter);

        maxPrice = this.logFile.getMaxPrice(getRequiredOrderType(), this.importFilter);

        avgPrice = this.logFile.getAveragePrice(getRequiredOrderType(), this.importFilter);

        stdDeviation = this.logFile.getStandardDeviation(getRequiredOrderType(), this.importFilter);

        result.append("Minimum price: " + AmountHelper.formatISKAmount(minPrice) + " ISK\n");
        result.append("Average price: " + AmountHelper.formatISKAmount(avgPrice) + " ISK\n");
        result.append("Maximum price: " + AmountHelper.formatISKAmount(maxPrice) + " ISK\n\n");

        result.append("Standard deviation: " + AmountHelper.formatISKAmount(stdDeviation) + " ISK\n\n");

        this.infoArea.setText(result.toString());
        this.infoArea.setCaretPosition(0);
    }

    @Override
    protected JPanel createPanelHook() {

        final JPanel result = new JPanel();

        result.setLayout(new GridBagLayout());

        final JPanel crapFilterPanel = new JPanel();
        crapFilterPanel.setLayout(new GridBagLayout());
        crapFilterPanel.setBorder(BorderFactory.createTitledBorder("Import filter"));

        crapFilterPanel.add(priceFilter, constraints(0, 0).width(3).anchorWest().noResizing().end());
        priceFilter.addActionListener(actionListener);

        linkComponentEnabledStates(priceFilter, minPriceFilter);
        linkComponentEnabledStates(priceFilter, maxPriceFilter);

        final List<PriceInfo.Type> types = Arrays.asList(PriceInfo.Type.values());
        requiredOrderType.setModel(new DefaultComboBoxModel<Type>(types));
        requiredOrderType.setSelectedItem(PriceInfo.Type.ANY);
        requiredOrderType.addActionListener(actionListener);

        minPriceFilter.setEnabled(false);
        maxPriceFilter.setEnabled(false);

        minPriceFilter.setColumns(10);
        maxPriceFilter.setColumns(10);

        minPriceFilter.addActionListener(actionListener);
        maxPriceFilter.addActionListener(actionListener);

        crapFilterPanel.add(minPriceFilter, constraints(1, 1).anchorWest().noResizing().end());
        crapFilterPanel.add(maxPriceFilter, constraints(2, 1).anchorWest().noResizing().end());

        // order volume filter
        crapFilterPanel.add(orderVolumeFilter, constraints(0, 2).width(3).anchorWest().noResizing().end());
        orderVolumeFilter.addActionListener(actionListener);

        linkComponentEnabledStates(orderVolumeFilter, minOrderSize);
        linkComponentEnabledStates(orderVolumeFilter, maxOrderSize);

        minOrderSize.addActionListener(actionListener);
        maxOrderSize.addActionListener(actionListener);

        minOrderSize.setEnabled(false);
        maxOrderSize.setEnabled(false);

        minOrderSize.setColumns(10);
        maxOrderSize.setColumns(10);

        crapFilterPanel.add(minOrderSize, constraints(1, 3).anchorWest().noResizing().end());
        crapFilterPanel.add(maxOrderSize, constraints(2, 3).anchorWest().noResizing().end());

        crapFilterPanel.add(new JLabel("Imported order types"), constraints(1, 4).anchorWest().noResizing().end());
        crapFilterPanel.add(this.requiredOrderType, constraints(2, 4).anchorWest().noResizing().end());

        result.add(crapFilterPanel,
                constraints(0, 0).width(1).weightX(0.1).weightY(0.1).anchorWest().resizeBoth().end());

        // add text area
        infoArea.setEditable(false);
        infoArea.setLineWrap(true);
        infoArea.setWrapStyleWord(true);

        result.add(new JScrollPane(infoArea), constraints(1, 0).width(1).resizeBoth().end());

        // add table
        table.setModel(model);
        table.setDefaultRenderer(Double.class, new MyRenderer());
        table.setDefaultRenderer(String.class, new MyRenderer());
        table.setDefaultRenderer(EveDate.class, new MyRenderer());
        table.setDefaultRenderer(Boolean.class, new MyBooleanRenderer());
        table.setDefaultRenderer(Date.class, new MyRenderer());

        table.setRowSorter(model.getRowSorter());

        final JPanel splitPane = createSplitPanePanel(result, new JScrollPane(table));

        popupMenuBuilder = new PopupMenuBuilder().attach(table)
                .addItem("Clear user overrides", new AbstractAction() {

                    @Override
                    public boolean isEnabled() {
                        return model.hasUserOverrides();
                    }

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        model.clearUserOverrides();
                    }
                }).addSeparatorIfNotEmpty().addItem("Add selected prices to import", new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        markRowsManually(true);
                    }

                    @Override
                    public boolean isEnabled() {
                        return hasMultipleLinesSelection();
                    }
                }).addItem("Remove selected prices from import", new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        markRowsManually(false);
                    }

                    @Override
                    public boolean isEnabled() {
                        return hasMultipleLinesSelection();
                    }
                });

        updateInfoTextArea();

        return splitPane;
    }

    public List<PriceInfo> getPriceInfosForImport() {

        final ArrayList<PriceInfo> result = new ArrayList<PriceInfo>();

        if (wasCancelled()) {
            return result;
        }

        final IMarketLogFilter removalFilter = new IMarketLogFilter() {

            @Override
            public boolean includeInResult(MarketLogEntry entry) {
                final LogEntryWrapper wrapper = model.getWrapperFor(entry);
                if (wrapper.isEnteredByUser()) {
                    return wrapper.isValid(); // user did override filter
                }
                return importFilter.includeInResult(entry);
            }
        };

        return getMarketLogFile().getAggregatedOrders(removalFilter, systemClock);
    }

    protected boolean hasMultipleLinesSelection() {
        final int[] selectedRows = table.getSelectedRows();
        return selectedRows != null && selectedRows.length > 1;
    }

    protected void markRowsManually(boolean selectForImport) {

        int[] selectedRows = table.getSelectedRows();
        if (ArrayUtils.isEmpty(selectedRows)) {
            return;
        }

        int minRow = -1;
        int maxRow = -1;
        for (int viewRow : selectedRows) {

            final int modelRow = table.convertRowIndexToModel(viewRow);

            model.getRow(modelRow).setEnteredByUser(true);
            model.getRow(modelRow).setValid(selectForImport);

            if (minRow == -1 || viewRow < minRow) {
                minRow = viewRow;
            }

            if (maxRow == -1 || viewRow > maxRow) {
                maxRow = viewRow;
            }
        }

        model.rowsChanged(minRow, maxRow);
    }

    private JPanel createSplitPanePanel(JComponent top, JScrollPane bottom) {

        final JScrollPane resultPane = new JScrollPane(top);

        resultPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);

        resultPane.setMinimumSize(new Dimension(400, 150));
        resultPane.setPreferredSize(new Dimension(600, 200));

        bottom.setMinimumSize(new Dimension(100, 200));
        bottom.setPreferredSize(new Dimension(600, 300));

        final JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, top, bottom);

        //      pane.setResizeWeight( 0.5 );
        pane.setDividerLocation(200);

        pane.setContinuousLayout(true);

        final JPanel contentPanel = new JPanel();

        contentPanel.setLayout(new BorderLayout());
        contentPanel.add(pane);
        return contentPanel;
    }

    private static final class LogEntryWrapper {

        private boolean enteredByUser = false;
        private boolean valid = true;
        private final MarketLogEntry entry;

        public LogEntryWrapper(MarketLogEntry entry) {
            this.entry = entry;
        }

        public void setValid(boolean yesNo) {
            this.valid = yesNo;
        }

        public boolean isValid() {
            return valid;
        }

        public MarketLogEntry entry() {
            return entry;
        }

        public void setEnteredByUser(boolean enteredByUser) {
            this.enteredByUser = enteredByUser;
        }

        public boolean isEnteredByUser() {
            return enteredByUser;
        }
    }

    @Override
    protected void onAttachHook(IComponentCallback callback) {

        if (popupMenuBuilder != null) {
            popupMenuBuilder.attach(table);
        }

        model.modelDataChanged();
    }

    @Override
    protected void onDetachHook() {
        popupMenuBuilder.detach(table);
    }

    @Override
    protected void disposeHook() {
        if (popupMenuBuilder != null) {
            popupMenuBuilder.detach(table);
        }
    }

    public MarketLogFile getMarketLogFile() {
        return logFile;
    }

    private final class MyModel extends AbstractTableModel<LogEntryWrapper> {

        private final List<LogEntryWrapper> data = new ArrayList<LogEntryWrapper>();

        public MyModel() {
            super(new TableColumnBuilder().add("Import ?", Boolean.class).add("Type")
                    .add("Date", EveDate.class, EveDate.COMPARATOR).add("Volume", Double.class)
                    .add("Volume remaining", Double.class).add("Price", Double.class));
        }

        public void rowsChanged(int minRow, int maxRow) {
            fireEvent(
                    new TableModelEvent(this, minRow, maxRow, TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE));
        }

        public void clearUserOverrides() {

            synchronized (data) {
                for (LogEntryWrapper wrapper : data) {
                    wrapper.setEnteredByUser(false);
                }
            }

            filterChanged();
        }

        public boolean hasUserOverrides() {
            synchronized (data) {
                for (LogEntryWrapper wrapper : data) {
                    if (wrapper.isEnteredByUser()) {
                        return true;
                    }
                }
            }
            return false;
        }

        private LogEntryWrapper getWrapperFor(MarketLogEntry entry) {

            synchronized (data) {
                for (LogEntryWrapper wrapper : data) {
                    if (wrapper.entry() == entry) {
                        return wrapper;
                    }
                }
            }
            return null;
        }

        @Override
        protected Object getColumnValueAt(int modelRowIndex, int modelColumnIndex) {
            final LogEntryWrapper wrapper = getRow(modelRowIndex);
            final MarketLogEntry entry = wrapper.entry();

            switch (modelColumnIndex) {
            case VALID_IDX:
                return wrapper.isValid();
            case DATE_IDX:
                return entry.getIssueDate();
            case TYPE_IDX:
                return entry.isBuyOrder() ? "BUY" : "SELL";
            case VOLUME_IDX:
                return entry.getVolume();
            case REMAINING_VOLUME_IDX:
                return entry.getRemainingVolume();
            case PRICE_IDX:
                return entry.getPrice();
            default:
                throw new IllegalArgumentException("Invalid column index " + modelColumnIndex);
            }
        }

        public void filterChanged() {

            int firstRow = -1;
            int lastRow = -1;

            int row = 0;
            synchronized (data) {
                for (LogEntryWrapper wrapper : data) {
                    final boolean valid;

                    if (!wrapper.isEnteredByUser()) {
                        valid = importFilter.includeInResult(wrapper.entry());
                    } else {
                        valid = wrapper.isValid();
                    }

                    if (valid != wrapper.isValid()) {
                        wrapper.setValid(valid);
                        if (firstRow == -1 || row < firstRow) {
                            firstRow = row;
                        }
                        if (lastRow == -1 || row > lastRow) {
                            lastRow = row;
                        }
                    }

                    row++;
                }
            }

            if (firstRow == -1) {
                return; // no rows changed
            }

            final TableModelEvent event = new TableModelEvent(this, firstRow, lastRow, TableModelEvent.ALL_COLUMNS,
                    TableModelEvent.UPDATE);

            fireEvent(event);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex == VALID_IDX;
        }

        @Override
        public void setValueAt(Object value, int rowIndex, int columnIndex) {

            if (columnIndex != VALID_IDX) {
                throw new IllegalArgumentException("Invalid column " + columnIndex);
            }

            LogEntryWrapper wrapper = getRow(rowIndex);
            wrapper.setValid((Boolean) value);
            wrapper.setEnteredByUser(true);
            notifyRowChanged(rowIndex);

            // TODO: Baaad... updateInfoTextArea() should be registered
            // as listener with this model
            updateInfoTextArea();
        }

        @Override
        public LogEntryWrapper getRow(int modelRow) {

            synchronized (data) {
                if (modelRow < 0 || modelRow > data.size()) {
                    throw new IllegalArgumentException("Invalid model row " + modelRow);
                }

                return data.get(modelRow);
            }
        }

        @Override
        public int getRowCount() {
            synchronized (data) {
                return data.size();
            }
        }

        @Override
        public void modelDataChanged() {

            synchronized (data) {

                data.clear();

                for (MarketLogEntry entry : getMarketLogFile().getOrders(importFilter)) {
                    data.add(new LogEntryWrapper(entry));
                }

                Collections.sort(data, new Comparator<LogEntryWrapper>() {

                    @Override
                    public int compare(LogEntryWrapper o1, LogEntryWrapper o2) {
                        return o2.entry().getIssueDate().compareTo(o1.entry().getIssueDate());
                    }
                });

            }

            super.modelDataChanged();
        }

    }

    private final ThreadLocal<NumberFormat> ORDER_VOLUME_FORMAT = new ThreadLocal<NumberFormat>() {
        protected NumberFormat initialValue() {
            return AmountHelper.createOrderVolumeNumberFormat();
        };
    };

    private final class MyRenderer extends DefaultTableCellRenderer {

        public MyRenderer() {
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int viewRow, int viewColumn) {

            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, viewRow, viewColumn);

            final int modelColumn = table.convertColumnIndexToModel(viewColumn);

            final int modelRow = table.convertRowIndexToModel(viewRow);

            if (modelColumn == VOLUME_IDX || modelColumn == REMAINING_VOLUME_IDX) {
                setText(ORDER_VOLUME_FORMAT.get().format(value));
            } else if (modelColumn == DATE_IDX) {
                setText(DateHelper.format(((EveDate) value).getLocalTime()));
            }

            final LogEntryWrapper wrapper = model.getRow(modelRow);

            if (!isSelected) {

                if (wrapper.isValid()) {
                    this.setBackground(table.getBackground());
                } else {
                    this.setBackground(Color.LIGHT_GRAY);
                }

            } else {
                this.setBackground(table.getSelectionBackground());
            }

            return this;
        }

    }

    private class MyBooleanRenderer extends JCheckBox implements TableCellRenderer, UIResource {
        private final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

        public MyBooleanRenderer() {
            super();
            setHorizontalAlignment(JLabel.CENTER);
            setBorderPainted(true);
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int viewRow, int column) {
            if (isSelected) {
                setForeground(table.getSelectionForeground());
                super.setBackground(table.getSelectionBackground());
            } else {

                final int modelRow = table.convertRowIndexToModel(viewRow);

                final LogEntryWrapper wrapper = ImportMarketLogFileComponent.this.model.getRow(modelRow);

                if (wrapper.isEnteredByUser()) {
                    setBackground(Color.ORANGE);
                } else {
                    if (wrapper.isValid()) {
                        setBackground(table.getBackground());
                    } else {
                        setBackground(Color.LIGHT_GRAY);
                    }
                }

                setForeground(table.getForeground());
            }

            setSelected((value != null && ((Boolean) value).booleanValue()));

            if (hasFocus) {
                setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
            } else {
                setBorder(noFocusBorder);
            }

            return this;
        }
    }

    @Override
    protected JButton createCancelButton() {
        return new JButton("Cancel");
    }

    @Override
    protected JButton createOkButton() {
        return new JButton("Import data");
    }

    @Override
    protected boolean hasValidInput() {
        return true;
    }

}