com.jaspersoft.studio.property.dataset.dialog.DataPreviewTable.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.studio.property.dataset.dialog.DataPreviewTable.java

Source

/*******************************************************************************
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 * 
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 * 
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 ******************************************************************************/
package com.jaspersoft.studio.property.dataset.dialog;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import net.sf.jasperreports.eclipse.ui.util.UIUtils;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.design.JRDesignDataset;
import net.sf.jasperreports.engine.design.JRDesignField;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;

import com.jaspersoft.studio.data.IDataPreviewInfoProvider;
import com.jaspersoft.studio.data.reader.DatasetReader;
import com.jaspersoft.studio.data.reader.DatasetReaderListener;
import com.jaspersoft.studio.messages.Messages;

/**
 * Data preview table widget.
 * 
 * @author Massimo Rabbi (mrabbi@users.sourceforge.net)
 *
 */
public class DataPreviewTable implements DatasetReaderListener {

    // Costants
    private static final int FILLER_THREAD_SLEEPTIME = 30;
    private static final int RECORDS_NUM_ALL = -1;
    private static final int RECORDS_NUM_1000 = 1000;
    private static final int RECORDS_NUM_500 = 500;
    private static final int RECORDS_NUM_100 = 100;

    // Widget stuff
    private TableViewer tviewer;
    private Table wtable;
    private Composite composite;
    private Composite tableContainer;
    private Combo recordsNumCombo;
    private Button refreshPreviewBtn;
    private Button cancelPreviewBtn;
    private ProgressBar progressBar;
    private Label infoMsg;
    private Composite infoComposite;
    private Color background;

    // Additional support fields
    private IDataPreviewInfoProvider previewInfoProvider;
    private Job refreshPrevieDataJob;
    private DatasetReader dataReader;
    private boolean statusOK;
    private List<DataPreviewBean> previewItems;
    private TableFillerThread tableFiller;
    private int readItems = 0;

    public DataPreviewTable(Composite parent, IDataPreviewInfoProvider previewInfoProvider, Color background) {
        this.previewInfoProvider = previewInfoProvider;
        this.previewItems = new ArrayList<DataPreviewTable.DataPreviewBean>(RECORDS_NUM_100);
        this.background = background;
        createControl(parent);
    }

    /*
     * Creates the widget area.
     */
    private void createControl(Composite parent) {
        composite = new Composite(parent, SWT.NONE);
        composite.setBackground(background);
        composite.setLayout(new GridLayout(4, false));
        composite.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                statusOK = false;
                if (refreshPrevieDataJob != null) {
                    refreshPrevieDataJob.cancel();
                }
            }
        });

        refreshPreviewBtn = new Button(composite, SWT.PUSH);
        refreshPreviewBtn.setText(Messages.DataPreviewTable_PreviewButton);
        refreshPreviewBtn.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
        refreshPreviewBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (canRefreshDataPreview()) {
                    refreshDataPreview();
                    refreshPreviewBtn.setEnabled(false);
                    cancelPreviewBtn.setEnabled(true);
                }
            }

            private boolean canRefreshDataPreview() {
                // No data preview when no fields are selected.
                if (previewInfoProvider.getFieldsForPreview() == null
                        || previewInfoProvider.getFieldsForPreview().isEmpty()) {
                    MessageDialog.openError(composite.getShell(), Messages.DataPreviewTable_ErrorTitle,
                            Messages.DataPreviewTable_ErrorMsgNoFields);
                    return false;
                }
                // No data preview when no data adapter is selected
                if (previewInfoProvider.getDataAdapterDescriptor() == null) {
                    MessageDialog.openError(composite.getShell(), Messages.DataPreviewTable_ErrorTitle,
                            Messages.DataPreviewTable_ErrorMsgNoDataAdapter);
                    return false;
                }
                return true;
            }
        });

        cancelPreviewBtn = new Button(composite, SWT.PUSH);
        cancelPreviewBtn.setText(Messages.DataPreviewTable_CancelButton);
        cancelPreviewBtn.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
        cancelPreviewBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                cancelDataPreview();
                cancelPreviewBtn.setEnabled(false);
                refreshPreviewBtn.setEnabled(true);
            }
        });
        cancelPreviewBtn.setEnabled(false);

        recordsNumCombo = new Combo(composite, SWT.READ_ONLY);
        recordsNumCombo.setItems(
                new String[] { Messages.DataPreviewTable_RecordsNum100, Messages.DataPreviewTable_RecordsNum500,
                        Messages.DataPreviewTable_RecordsNum1000, Messages.DataPreviewTable_RecordsNumAll });
        recordsNumCombo.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
        recordsNumCombo.select(0);

        infoComposite = new Composite(composite, SWT.NONE);
        GridLayout infoCmpGL = new GridLayout(2, false);
        infoCmpGL.marginHeight = 0;
        infoCmpGL.marginWidth = 0;
        infoComposite.setBackground(background);
        infoComposite.setLayout(infoCmpGL);
        infoComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        infoMsg = new Label(infoComposite, SWT.NONE);
        infoMsg.setBackground(background);
        infoMsg.setText(Messages.DataPreviewTable_Ready);
        infoMsg.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));

        progressBar = new ProgressBar(infoComposite, SWT.INDETERMINATE | SWT.BORDER);
        GridData progBarGD = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
        progBarGD.horizontalIndent = 5;
        progBarGD.widthHint = 100;
        progBarGD.exclude = true;
        progressBar.setLayoutData(progBarGD);
        progressBar.setVisible(false);

        tableContainer = new Composite(composite, SWT.NONE);
        tableContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
        tableContainer.setLayout(new TableColumnLayout());

        tviewer = new TableViewer(tableContainer, SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
        wtable = tviewer.getTable();
        wtable.setHeaderVisible(true);
        wtable.setLinesVisible(true);

        tviewer.setContentProvider(ArrayContentProvider.getInstance());
    }

    /**
     * @return the main control
     */
    public Composite getControl() {
        return this.composite;
    }

    /*
     * Notifies the need of a table refresh due 
     * to information modification, i.e. table columns
     * modification.
     */
    private void refreshDataPreview() {
        // Refresh layout for the table      
        updateTableLayout();
        final int recordsCountSelected = getRecordsCountSelected();
        refreshPrevieDataJob = new Job(Messages.DataPreviewTable_PreviewDataJobTitle) {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                if (dataReader != null) {
                    dataReader.removeDatasetReaderListener(DataPreviewTable.this);
                    dataReader = null;
                }
                dataReader = new DatasetReader();
                dataReader.setColumns(getColumns());
                dataReader.setDataAdapterDescriptor(previewInfoProvider.getDataAdapterDescriptor());
                // FIXME - TEMPORARY FIX THAT SHOULD BE REMOVED!
                // Using JFace Databinding for fields list of the dataset 
                // makes the internal fields map to be out of synch.
                // Modifications should occur using the JRDesignDataset#add and #remove methods.
                JRDesignDataset clonedDS = (JRDesignDataset) previewInfoProvider.getDesignDataset().clone();
                clonedDS.getFieldsList().clear();
                clonedDS.getFieldsMap().clear();
                for (JRDesignField f : previewInfoProvider.getFieldsForPreview()) {
                    try {
                        clonedDS.addField(f);
                    } catch (JRException e) {
                        // Do not care, duplication should never happen.
                        e.printStackTrace();
                    }
                }

                dataReader.setDesignDataset(clonedDS);
                dataReader.setMaxRecords(recordsCountSelected);
                dataReader.addDatasetReaderListener(DataPreviewTable.this);
                dataReader.start(previewInfoProvider.getJasperReportsConfig());
                return Status.OK_STATUS;
            }
        };

        tableFiller = new TableFillerThread();

        statusOK = true;
        infoMsg.setText(Messages.DataPreviewTable_GettingData);
        ((GridData) progressBar.getLayoutData()).exclude = false;
        progressBar.setVisible(true);
        refreshPrevieDataJob.schedule();
        tableFiller.start();
        infoComposite.layout();
    }

    /*
     * Cancel a pending data preview task.
     */
    private void cancelDataPreview() {
        // Clean up
        invalidate();
        if (refreshPrevieDataJob != null) {
            refreshPrevieDataJob.cancel();
        }
        if (dataReader.isRunning()) {
            dataReader.stop();
        }
        // Remove all table items if any
        wtable.removeAll();
        tviewer.setInput(null);
        readItems = 0;
        infoMsg.setText(Messages.DataPreviewTable_Ready);
        ((GridData) progressBar.getLayoutData()).exclude = true;
        progressBar.setVisible(false);
    }

    /*
     * Gets the number of max records for the output preview. 
     */
    private int getRecordsCountSelected() {
        switch (recordsNumCombo.getSelectionIndex()) {
        case 0:
            return RECORDS_NUM_100;
        case 1:
            return RECORDS_NUM_500;
        case 2:
            return RECORDS_NUM_1000;
        case 3:
            return RECORDS_NUM_ALL;
        default:
            return RECORDS_NUM_100;
        }
    }

    /*
     * Gets the column names.
     */
    private List<String> getColumns() {
        List<String> columns = new ArrayList<String>();
        for (JRDesignField f : previewInfoProvider.getFieldsForPreview()) {
            columns.add(f.getName());
        }
        return columns;
    }

    /*
     * Update the table layout.
     */
    private void updateTableLayout() {
        if (composite.isVisible()) {
            // Remove all table items if any
            wtable.removeAll();
            tviewer.setInput(null);

            // Dispose old columns if any
            for (TableColumn col : wtable.getColumns()) {
                col.dispose();
            }

            TableColumnLayout tColLayout = new TableColumnLayout();
            tableContainer.setLayout(tColLayout);

            List<JRDesignField> fields = previewInfoProvider.getFieldsForPreview();
            if (fields.size() > 0) {
                for (JRDesignField f : fields) {
                    TableViewerColumn tvc = new TableViewerColumn(tviewer, SWT.NONE);
                    tvc.getColumn().setText(f.getName());
                    tvc.setLabelProvider(new ColumnLabelProvider());
                    tColLayout.setColumnData(tvc.getColumn(),
                            new ColumnWeightData(1, ColumnWeightData.MINIMUM_WIDTH, true));
                    tvc.setLabelProvider(new CellLabelProvider() {
                        @Override
                        public void update(ViewerCell cell) {
                            DataPreviewBean element = (DataPreviewBean) cell.getElement();
                            Object value = element.getValue(cell.getColumnIndex());
                            if (value != null) {
                                cell.setText(value.toString());
                            } else {
                                cell.setText(""); //$NON-NLS-1$
                            }
                        }
                    });
                }

            }

            tableContainer.layout();
        }
    }

    /*
     * (non-Javadoc)
     * @see com.jaspersoft.studio.data.reader.DatasetReaderListener#newRecord(java.lang.Object[])
     */
    public void newRecord(final Object[] values) {
        previewItems.add(new DataPreviewBean(values));
        readItems++;
    }

    /*
     * (non-Javadoc)
     * @see com.jaspersoft.studio.data.reader.DatasetReaderListener#finished()
     */
    public void finished() {
        UIUtils.getDisplay().syncExec(new Runnable() {
            public void run() {
                if (tableFiller != null) {
                    tableFiller.done();
                    tableFiller = null;
                }
                flushPreviewItems();
                progressBar.setVisible(false);
                cancelPreviewBtn.setEnabled(false);
                ((GridData) progressBar.getLayoutData()).exclude = true;
                if (isValidStatus()) {
                    infoMsg.setText(MessageFormat.format(Messages.DataPreviewTable_ReadyReadData,
                            new Object[] { readItems }));
                }
                refreshPreviewBtn.setEnabled(true);
                infoComposite.layout();
                readItems = 0;
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see com.jaspersoft.studio.data.reader.DatasetReaderListener#isValidStatus()
     */
    public boolean isValidStatus() {
        return statusOK;
    }

    /*
     * (non-Javadoc)
     * @see com.jaspersoft.studio.data.reader.DatasetReaderListener#invalidate()
     */
    public void invalidate() {
        this.statusOK = false;
    }

    /*
     * Bean to represent the read record for previewing.
     */
    private class DataPreviewBean {
        private Object[] values;

        public DataPreviewBean(Object[] values) {
            this.values = values;
        }

        public Object getValue(int index) {
            return this.values[index];
        }
    }

    /*
     * This thread is responsible to update the table with 
     * chunks of data read from the dataset. 
     */
    private class TableFillerThread extends Thread {
        private boolean done = false;

        public void done() {
            done = true;
        }

        @Override
        public void run() {
            while (!done) {
                flushPreviewItems();
                try {
                    sleep(FILLER_THREAD_SLEEPTIME);
                } catch (InterruptedException e) {

                }
            }
        }
    }

    /*
     * Flush buffered items.
     */
    private void flushPreviewItems() {
        Object[] tmpItems = new Object[0];
        synchronized (previewItems) {
            tmpItems = previewItems.toArray();
            previewItems.clear();
        }
        final Object[] items = tmpItems;
        UIUtils.getDisplay().syncExec(new Runnable() {
            public void run() {
                if (!wtable.isDisposed() && statusOK) {
                    tviewer.add(items);
                }
            }
        });

    }

}