org.paxle.desktop.impl.dialogues.cconsole.CrawlingConsole.java Source code

Java tutorial

Introduction

Here is the source code for org.paxle.desktop.impl.dialogues.cconsole.CrawlingConsole.java

Source

/**
 * This file is part of the Paxle project.
 * Visit http://www.paxle.net for more information.
 * Copyright 2007-2010 the original author or authors.
 *
 * Licensed under the terms of the Common Public License 1.0 ("CPL 1.0").
 * Any use, reproduction or distribution of this program constitutes the recipient's acceptance of this agreement.
 * The full license text is available under http://www.opensource.org/licenses/cpl1.0.txt
 * or in the file LICENSE.txt in the root directory of the Paxle distribution.
 *
 * Unless required by applicable law or agreed to in writing, this software is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

package org.paxle.desktop.impl.dialogues.cconsole;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Vector;
import java.util.WeakHashMap;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;

import org.paxle.core.IMWComponent;
import org.paxle.core.MWComponentEvent;
import org.paxle.core.doc.CommandEvent;
import org.paxle.core.doc.ICommand;
import org.paxle.core.doc.ICommandTracker;
import org.paxle.core.filter.CommandFilterEvent;
import org.paxle.core.prefs.Properties;
import org.paxle.desktop.impl.Messages;
import org.paxle.desktop.impl.ServiceManager;
import org.paxle.desktop.impl.Utilities;
import org.paxle.desktop.impl.ServiceManager.MWComponents;
import org.paxle.desktop.impl.dialogues.DIServicePanel;

public class CrawlingConsole extends DIServicePanel implements EventHandler, ActionListener {

    private static final long serialVersionUID = 1L;

    private static final Dimension DIM_CCONSOLE = new Dimension(500, 400);

    private static final String PROP_DISPLAYED_MWCOMP = "displayedMWComp"; //$NON-NLS-1$
    private static final String PROP_TABLE_DISPLAY = "tableDisplay"; //$NON-NLS-1$
    private static final String PROP_SHOW_ENQUEUED = "showEnqueued"; //$NON-NLS-1$
    private static final String PROP_SHOW_DESTROYED = "showDestroyed"; //$NON-NLS-1$
    private static final String PROP_COL_WIDTHS = "columnWidths"; //$NON-NLS-1$

    private static final String AC_CLEAR = new String();
    private static final String AC_CRAWL = new String();
    private static final String AC_SELECT = new String();
    private static final String AC_ENQUEUED = new String();
    private static final String AC_DESTROYED = new String();
    private static final String AC_SETTINGS = new String();

    private static final String LBL_PAUSE_ = Messages.getString("crawlingConsole.pause"); //$NON-NLS-1$
    private static final String LBL_RESUME_ = Messages.getString("crawlingConsole.resume"); //$NON-NLS-1$

    static interface TableColumnSpecs {
        public Object[] getColumnHeaders();

        public String name();

        public void insertValues(final Vector<String> row, final Event event, final ICommand cmd,
                final String lastFilter);
    };

    private static enum Events {
        IN_QUEUE, OUT_QUEUE, MWCOMP_STATE
    };

    private final WeakHashMap<ICommand, String> lastFilters = new WeakHashMap<ICommand, String>();

    private final Log logger = LogFactory.getLog(CrawlingConsole.class);
    private final JScrollPane scroll = new JScrollPane();
    private final JButton clear = Utilities.instance.setButtonProps(new JButton(),
            Messages.getString("crawlingConsole.clear"), this, AC_CLEAR, KeyEvent.VK_C, null); //$NON-NLS-1$
    private final JCheckBox cbEnq = Utilities.instance.setButtonProps(new JCheckBox(),
            Messages.getString("crawlingConsole.enqueued"), this, AC_ENQUEUED, -1, null); //$NON-NLS-1$
    private final JCheckBox cbDstr = Utilities.instance.setButtonProps(new JCheckBox(),
            Messages.getString("crawlingConsole.rejected"), this, AC_DESTROYED, -1, null); //$NON-NLS-1$

    private final JToggleButton cpb = Utilities.instance.setButtonProps(new JToggleButton(), null, this, AC_CRAWL,
            KeyEvent.VK_S, null);
    private final JTable table;
    private final ConsoleTableModel model;
    private final JComboBox cbox;
    private final ICommandTracker tracker;
    private final JPanel options = new JPanel();

    private MWComponents currentComp;
    private boolean currentEnq, currentDstr;

    public CrawlingConsole(final ServiceManager services) {
        super(services, DIM_CCONSOLE);

        cbox = new JComboBox(MWComponents.humanReadableNames());
        cbox.addActionListener(this);
        tracker = services.getService(ICommandTracker.class);

        final Properties props = services.getServiceProperties();
        final String mwcomp = props.getProperty(PROP_DISPLAYED_MWCOMP, MWComponents.CRAWLER.name());
        final String tableDisplay = props.getProperty(PROP_TABLE_DISPLAY,
                // TableDisplay.WORKING_ON.name()
                Integer.toString(TableColumns.DEFAULT));
        final String showEnqueued = props.getProperty(PROP_SHOW_ENQUEUED, Boolean.TRUE.toString());
        final String showDestroyed = props.getProperty(PROP_SHOW_DESTROYED, Boolean.FALSE.toString());

        model = new ConsoleTableModel();
        table = new JTable(model, new ConsoleTableColumnModel());
        table.setAutoCreateColumnsFromModel(true);
        model.setCols((tableDisplay.matches("\\d+")) ? Integer.parseInt(tableDisplay)
                //      : TableDisplay.valueOf(tableDisplay)
                : TableColumns.DEFAULT);
        init(MWComponents.valueOf(mwcomp), Boolean.parseBoolean(showEnqueued), Boolean.parseBoolean(showDestroyed));
    }

    private int[] readColumnWidths(final TableColumnSpecs specs) {
        final Properties props = services.getServiceProperties();
        final String widthsProp = props.getProperty(PROP_COL_WIDTHS + specs.name(), null);
        if (widthsProp == null || widthsProp.length() == 0)
            return null;
        final String[] propSplit = widthsProp.split("[,]");
        final int[] widths = new int[propSplit.length];
        for (int i = 0; i < propSplit.length; i++)
            widths[i] = Integer.parseInt(propSplit[i]);
        return widths;
    }

    private void saveColumnWidths(final TableColumnSpecs specs, final int[] widths) {
        if (widths == null || widths.length == 0)
            return;
        final StringBuilder sb = new StringBuilder();
        for (int i = 0; i < widths.length;) {
            sb.append(Integer.toString(widths[i]));
            if (++i < widths.length)
                sb.append(',');
        }
        final Properties props = services.getServiceProperties();
        props.put(PROP_COL_WIDTHS + specs.name(), sb.toString());
    }

    @Override
    public void close() {
        final Properties props = super.services.getServiceProperties();
        props.put(PROP_DISPLAYED_MWCOMP, MWComponents.valueOfHumanReadable((String) cbox.getSelectedItem()).name());
        props.put(PROP_TABLE_DISPLAY, model.type.name());
        props.put(PROP_SHOW_ENQUEUED, Boolean.toString(cbEnq.isSelected()));
        props.put(PROP_SHOW_DESTROYED, Boolean.toString(cbDstr.isSelected()));
        saveColumnWidths(model.type, getCurrentWidths());
        super.close();
    }

    private void updateListeners(final MWComponents comp, final boolean enqueued, final boolean destroyed) {
        final String id = comp.getID();

        final boolean compUpdate = currentComp != comp;
        final boolean enqUpdate = compUpdate || (currentEnq ^ enqueued);
        final boolean dstrUpdate = compUpdate || (currentDstr ^ destroyed);

        if (compUpdate) {
            super.unregisterService(Events.MWCOMP_STATE);
            registerEventListener(Events.MWCOMP_STATE, MWComponentEvent.PROP_COMPONENT_ID, id,
                    MWComponentEvent.TOPIC_ALL);
            currentComp = comp;
        }
        if (enqUpdate) {
            if (currentEnq)
                super.unregisterService(Events.IN_QUEUE);
            if (enqueued)
                registerEventListener(Events.IN_QUEUE, CommandEvent.PROP_COMPONENT_ID, id + ".in", //$NON-NLS-1$
                        CommandEvent.TOPIC_DEQUEUED);
            currentEnq = enqueued;
        }
        if (dstrUpdate) {
            if (currentDstr)
                super.unregisterService(Events.OUT_QUEUE);
            if (destroyed)
                registerEventListener(Events.OUT_QUEUE, CommandEvent.PROP_COMPONENT_ID, id + ".out", //$NON-NLS-1$
                        CommandFilterEvent.TOPIC_POST_FILTER, CommandEvent.TOPIC_DESTROYED);
            currentDstr = destroyed;
        }
    }

    @SuppressWarnings("unchecked")
    private void registerEventListener(final Events key, final String filterKey, final String filterVal,
            final String... topics) {
        final Hashtable<String, Object> props = new Hashtable<String, Object>();
        if (topics.length > 0)
            props.put(EventConstants.EVENT_TOPIC, topics);
        props.put(EventConstants.EVENT_FILTER, String.format("(%s=%s)", filterKey, filterVal)); //$NON-NLS-1$
        super.registerService(key, this, props, EventHandler.class);
    }

    @Override
    public String getTitle() {
        return Messages.getString("crawlingConsole.title"); //$NON-NLS-1$
    }

    private void updateCpb(final boolean paused, final boolean getState, final boolean setState) {
        if (getState && setState)
            throw new IllegalArgumentException("cannot set and get state at the same time"); //$NON-NLS-1$
        final String item = (String) cbox.getSelectedItem();
        final IMWComponent<?> mwComp = services.getMWComponent(MWComponents.valueOfHumanReadable(item));
        final boolean state = (getState && mwComp != null) ? mwComp.isPaused() : paused;

        final class PausedUpdateRunnable implements Runnable {
            public void run() {
                cpb.setVisible(mwComp != null);
                cpb.setSelected(state);
                cpb.setText(String.format((state) ? LBL_RESUME_ : LBL_PAUSE_, item));
            }
        }
        ;

        SwingUtilities.invokeLater(new PausedUpdateRunnable());

        if (setState && mwComp != null) {
            if (paused) {
                mwComp.pause();
            } else {
                mwComp.resume();
            }
        }
    }

    public void actionPerformed(ActionEvent e) {
        final String ac = e.getActionCommand();
        if (ac == AC_CLEAR) {
            clear();
        } else if (ac == AC_CRAWL) {
            updateCpb(cpb.isSelected(), false, true);
        } else if (ac == AC_SELECT) {
            updateCpb(false, true, false);
        } else if (ac == AC_SETTINGS) {
            final JToggleButton btn = (JToggleButton) e.getSource();
            final boolean sel = btn.isSelected();
            btn.setText(((sel) ? '\u2191' : '\u2193') + btn.getText().substring(1));
            final boolean notInitialized = sel && options.getComponentCount() == 0;
            if (notInitialized)
                initOptions();
            final Runnable sr = new Runnable() {
                public void run() {
                    int height = options.getHeight();
                    frame.setSize(frame.getWidth(), frame.getHeight() + ((sel) ? height : -height));
                }
            };
            if (!notInitialized)
                sr.run();
            options.setVisible(sel);
            if (notInitialized)
                SwingUtilities.invokeLater(sr);

        }
        if (ac == AC_SELECT || ac == AC_ENQUEUED || ac == AC_DESTROYED) {
            final String compItem = (String) cbox.getSelectedItem();
            final boolean enq = cbEnq.isSelected();
            final boolean dstr = cbDstr.isSelected();
            if (logger.isDebugEnabled())
                logger.debug("received ActionEvent, item: '" + compItem + "', enq: " + enq + ", dstr: " + dstr);
            updateListeners(MWComponents.valueOfHumanReadable(compItem), enq, dstr);
            /*
            model.setType((cbDstr.isSelected()) ? TableDisplay.REJECTED : (cbEnq.isSelected()) ? TableDisplay.WORKING_ON
                  : new TableColumns());
                  */
        }
    }

    private void clear() {
        model.clear();
        clear.setEnabled(false);
    }

    private void init(final MWComponents comp, final boolean showEnqueued, final boolean showDestroyed) {
        scroll.setViewportView(table);
        updateCpb(false, true, false);

        cbEnq.setSelected(showEnqueued);
        cbDstr.setSelected(showDestroyed);

        cbox.setActionCommand(AC_SELECT);
        cbox.setSelectedIndex(comp.ordinal());

        final JPanel bbLeft = new JPanel(new FlowLayout(FlowLayout.LEFT));
        bbLeft.add(cpb);
        bbLeft.add(cbox);
        bbLeft.add(cbEnq);
        bbLeft.add(cbDstr);

        final JPanel bbRight = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        clear.setEnabled(false);
        bbRight.add(Utilities.instance.setButtonProps(new JToggleButton(),
                "\u2193 " + Messages.getString("crawlingConsole.settings"), this, AC_SETTINGS, KeyEvent.VK_S,
                null));
        bbRight.add(clear);

        final JPanel b = new JPanel(new BorderLayout());
        b.add(bbLeft, BorderLayout.WEST);
        b.add(bbRight, BorderLayout.EAST);
        options.setVisible(false);
        b.add(options, BorderLayout.SOUTH);

        super.setLayout(new BorderLayout());
        super.add(scroll, BorderLayout.CENTER);
        super.add(b, BorderLayout.SOUTH);
    }

    private final int[] getCurrentWidths() {
        final TableColumnModel tcm = table.getTableHeader().getColumnModel();
        final int[] widths = new int[tcm.getColumnCount()];
        for (int i = 0; i < widths.length; i++)
            widths[i] = tcm.getColumn(i).getWidth();
        return widths;
    }

    private final class ConsoleTableModel extends DefaultTableModel {

        private static final long serialVersionUID = 1L;

        private final TableColumns type = new TableColumns(0);
        private int maxSize = 100;

        public ConsoleTableModel() {
        }

        public void setCols(final int cols) {
            if (cols == type.showCmd)
                return;

            if (type.showCmd != 0) {
                // save old widths
                final int[] oldWidths = getCurrentWidths();
                saveColumnWidths(type, oldWidths);
                logger.debug("Saving old widths: " + Arrays.toString(oldWidths));
            }

            // set identifiers
            type.showCmd = cols;
            super.setColumnIdentifiers(type.getColumnHeaders());

            // set new widths
            final int[] newWidths = readColumnWidths(type);
            logger.debug("New widhts: " + Arrays.toString(newWidths));
            final TableColumnModel headerCols = table.getTableHeader().getColumnModel();
            if (newWidths != null && headerCols.getColumnCount() > 0)
                for (int i = 0; i < newWidths.length; i++)
                    headerCols.getColumn(i).setPreferredWidth(newWidths[i]);
        }

        /*
        public void setType(final TableColumnSpecs type) {
           if (this.type != type) {
        if (type != null) {
           if (this.type != null) {
              // save old widths
              final int[] oldWidths = getCurrentWidths();
              saveColumnWidths(type, oldWidths);
              logger.debug("Saving old widths: " + Arrays.toString(oldWidths));
           }
               
           // set identifiers
           super.setColumnIdentifiers(type.getColumnHeaders());
           // set new widths
           final int[] newWidths = readColumnWidths(type);
           logger.debug("New widhts: " + Arrays.toString(newWidths));
           final TableColumnModel headerCols = table.getTableHeader().getColumnModel();
           if (newWidths != null && headerCols.getColumnCount() > 0) {
              for (int i=0; i<newWidths.length; i++)
                 headerCols.getColumn(i).setPreferredWidth(newWidths[i]);
           }
        }
        this.type = type;
           }
        }*/

        public void setMaxSize(int maxSize) {
            final int current = super.getRowCount();
            if (maxSize >= current) {
                super.setRowCount(maxSize);
            } else {
                for (int i = 0; i < current - maxSize; i++)
                    dataVector.remove(0);
                super.fireTableRowsDeleted(0, current - maxSize);
            }
            this.maxSize = maxSize;
        }

        public void clear() {
            super.dataVector.clear();
            super.fireTableDataChanged();
        }

        public void addEventLine(final Event event, final ICommand cmd) {
            if (type == null)
                return;

            final Vector<String> row = new Vector<String>();
            type.insertValues(row, event, cmd, lastFilters.get(cmd));

            try {
                SwingUtilities.invokeAndWait(new Scroller(scroll, table, this).init(row, clear, maxSize));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    private void initOptions() {
        options.setLayout(new GridBagLayout());
        final GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = Utilities.INSETS_ZERO;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.gridx = gbc.gridy = 0;
        final int show = model.type.showCmd;
        for (final Columns col : Columns.values()) {
            final int mask = 1 << col.ordinal();
            final JCheckBox cbox = new JCheckBox();
            cbox.setSelected((show & mask) != 0);

            // TODO: make static by setting an action-command which represents the respective cbox
            final class CboxAC implements ActionListener {
                public void actionPerformed(ActionEvent e) {
                    if (cbox.isSelected()) {
                        model.setCols(model.type.showCmd | mask);
                    } else {
                        model.setCols(model.type.showCmd & ~mask);
                    }
                }
            }
            ;

            options.add(Utilities.instance.setButtonProps(cbox, col.getL10n(), new CboxAC(), null, -1, null), gbc);

            if (gbc.gridy < 5) {
                gbc.gridy++;
            } else {
                gbc.gridx++;
                gbc.gridy = 0;
            }
        }
    }

    private void handleCommandEvent(final Event event) {
        final String topic = event.getTopic();
        final Long id = (Long) event.getProperty(CommandEvent.PROP_COMMAND_ID);
        final ICommand cmd = tracker.getCommandByID(id);

        if (topic.equals(CommandFilterEvent.TOPIC_POST_FILTER)) {
            lastFilters.put(cmd, (String) event.getProperty(CommandFilterEvent.PROP_FILTER_NAME));
        } else {
            model.addEventLine(event, cmd);
        }
    }

    private void handleMWComponentEvent(final Event event) {
        updateCpb(event.getTopic() == MWComponentEvent.TOPIC_PAUSED, false, false);
    }

    /*
     * (non-Javadoc)
     * @see org.osgi.service.event.EventHandler#handleEvent(Event)
     */
    public void handleEvent(Event event) {
        final String topic = event.getTopic();
        if (logger.isDebugEnabled())
            logger.debug("Received event '" + event + "', topic: '" + topic + "', keys: "
                    + Arrays.toString(event.getPropertyNames()) + ", componentID: '"
                    + event.getProperty("componentID"));
        if (topic.startsWith(CommandEvent.TOPIC_)) {
            handleCommandEvent(event);
        } else if (topic.startsWith(MWComponentEvent.TOPIC_)) {
            handleMWComponentEvent(event);
        }
    }
};