org.pentaho.reporting.designer.core.util.table.ElementMetaDataTable.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.designer.core.util.table.ElementMetaDataTable.java

Source

/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2017 Hitachi Vantara..  All rights reserved.
*/

package org.pentaho.reporting.designer.core.util.table;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.designer.core.DesignerContextComponent;
import org.pentaho.reporting.designer.core.ReportDesignerContext;
import org.pentaho.reporting.designer.core.editor.ReportDocumentContext;
import org.pentaho.reporting.designer.core.editor.ReportRenderContext;
import org.pentaho.reporting.designer.core.settings.SettingsListener;
import org.pentaho.reporting.designer.core.settings.WorkspaceSettings;
import org.pentaho.reporting.designer.core.util.table.expressions.ExpressionCellHandler;
import org.pentaho.reporting.designer.core.util.table.expressions.ReportPreProcessorCellEditor;
import org.pentaho.reporting.designer.core.util.table.expressions.ReportPreProcessorCellRenderer;
import org.pentaho.reporting.designer.core.util.table.expressions.StructureFunctionCellEditor;
import org.pentaho.reporting.engine.classic.core.ReportPreProcessor;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.function.StructureFunction;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.designtime.swing.FormattingTableCellRenderer;
import org.pentaho.reporting.libraries.designtime.swing.GenericCellEditor;
import org.pentaho.reporting.libraries.designtime.swing.GenericCellRenderer;
import org.pentaho.reporting.libraries.designtime.swing.PaintCellRenderer;
import org.pentaho.reporting.libraries.designtime.swing.date.DateCellEditor;
import org.pentaho.reporting.libraries.designtime.swing.date.TimeCellEditor;
import org.pentaho.reporting.libraries.designtime.swing.propertyeditors.PropertyEditorCellEditor;
import org.pentaho.reporting.libraries.designtime.swing.propertyeditors.PropertyEditorCellRenderer;
import org.pentaho.reporting.libraries.designtime.swing.settings.LocaleSettings;

import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import java.awt.SystemColor;
import java.awt.Paint;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * A table implementation that selects the cell-renderer and editor based on some extended rules (and not just based on
 * the current column).
 *
 * @author Thomas Morgner
 */
public class ElementMetaDataTable extends JTable implements DesignerContextComponent {
    private class LocaleSettingsListener implements SettingsListener {
        private LocaleSettingsListener() {
        }

        public void settingsChanged() {
            applyLocaleSettings(WorkspaceSettings.getInstance());
        }
    }

    private class ActiveContextChangeHandler implements PropertyChangeListener {
        private ActiveContextChangeHandler() {
        }

        /**
         * This method gets called when a bound property is changed.
         *
         * @param evt A PropertyChangeEvent object describing the event source and the property that has changed.
         */

        public void propertyChange(final PropertyChangeEvent evt) {
            final ReportRenderContext oldContext = (ReportRenderContext) evt.getOldValue();
            final ReportRenderContext activeContext = (ReportRenderContext) evt.getNewValue();
            updateActiveContext(oldContext, activeContext);
        }
    }

    private static final Log logger = LogFactory.getLog(ElementMetaDataTable.class);

    private GroupingHeaderCellRenderer groupingCellRenderer;
    private PropertyEditorCellRenderer propertyEditorCellRenderer;
    private DesignerPropertyCellEditorWithEllipsis propertyEditorCellEditor;
    private PropertyEditorCellEditor taggedPropertyEditorCellEditor;
    private ArrayCellRenderer arrayCellRenderer;
    private ArrayCellEditor arrayCellEditor;
    private ExpressionCellHandler expressionsCellEditor;
    private ExpressionCellHandler expressionsCellRenderer;
    private StringValueCellEditor stringValueCellEditor;

    private StructureFunctionCellEditor structureFunctionCellEditor;
    private ReportPreProcessorCellEditor reportPreProcessorCellEditor;
    private ReportPreProcessorCellRenderer reportPreProcessorCellRenderer;
    private ActiveContextChangeHandler changeHandler;

    public ElementMetaDataTable() {
        putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

        // This is a hack as Mac chooses white on white for grids.
        this.setShowHorizontalLines(true);
        this.setShowVerticalLines(true);
        this.setGridColor(SystemColor.controlShadow);

        changeHandler = new ActiveContextChangeHandler();
        structureFunctionCellEditor = new StructureFunctionCellEditor();
        reportPreProcessorCellEditor = new ReportPreProcessorCellEditor();
        expressionsCellEditor = new ExpressionCellHandler();
        expressionsCellRenderer = new ExpressionCellHandler();
        reportPreProcessorCellRenderer = new ReportPreProcessorCellRenderer();
        groupingCellRenderer = new GroupingHeaderCellRenderer();
        taggedPropertyEditorCellEditor = new PropertyEditorCellEditor();
        propertyEditorCellEditor = new DesignerPropertyCellEditorWithEllipsis();
        propertyEditorCellRenderer = new PropertyEditorCellRenderer();
        arrayCellRenderer = new ArrayCellRenderer();
        arrayCellEditor = new ArrayCellEditor();
        stringValueCellEditor = new StringValueCellEditor();

        setDefaultEditor(Object.class, null);
        setDefaultEditor(GroupingHeader.class, new GroupingHeaderCellEditor());
        setDefaultEditor(Expression.class, expressionsCellEditor);
        setDefaultEditor(StructureFunction.class, structureFunctionCellEditor);
        setDefaultEditor(ReportPreProcessor.class, reportPreProcessorCellEditor);

        setDefaultEditor(Number.class, new GenericCellEditor(BigDecimal.class));
        setDefaultEditor(Integer.class, new GenericCellEditor(Integer.class));
        setDefaultEditor(Float.class, new GenericCellEditor(Float.class));
        setDefaultEditor(Double.class, new GenericCellEditor(Double.class));
        setDefaultEditor(Short.class, new GenericCellEditor(Short.class));
        setDefaultEditor(Byte.class, new GenericCellEditor(Byte.class));
        setDefaultEditor(Long.class, new GenericCellEditor(Long.class));
        setDefaultEditor(BigInteger.class, new GenericCellEditor(BigInteger.class));
        setDefaultEditor(BigDecimal.class, new GenericCellEditor(BigDecimal.class));
        setDefaultEditor(String.class, stringValueCellEditor);
        setDefaultEditor(Date.class, new DateCellEditor(Date.class));
        setDefaultEditor(java.sql.Date.class, new DateCellEditor(java.sql.Date.class));
        setDefaultEditor(Time.class, new TimeCellEditor(Time.class));
        setDefaultEditor(Timestamp.class, new DateCellEditor(Timestamp.class));

        setDefaultRenderer(GroupingHeader.class, new GroupingHeaderCellRenderer());
        setDefaultRenderer(GroupedName.class, new GroupedNameCellRenderer());
        setDefaultRenderer(StructureFunction.class, expressionsCellRenderer);
        setDefaultRenderer(Expression.class, expressionsCellRenderer);
        setDefaultRenderer(Paint.class, new PaintCellRenderer());
        setDefaultRenderer(Object.class, new GenericCellRenderer());
        setDefaultRenderer(String.class, new GenericCellRenderer());
        setDefaultRenderer(ReportPreProcessor.class, reportPreProcessorCellRenderer);

        final SimpleDateFormat isoDateFormat = new SimpleDateFormat(WorkspaceSettings.DATETIME_FORMAT_DEFAULT,
                Locale.ENGLISH);
        setDefaultRenderer(Date.class, new FormattingTableCellRenderer(isoDateFormat));
        setDefaultRenderer(java.sql.Date.class, new FormattingTableCellRenderer(
                new SimpleDateFormat(WorkspaceSettings.DATE_FORMAT_DEFAULT, Locale.ENGLISH)));
        setDefaultRenderer(Time.class, new FormattingTableCellRenderer(
                new SimpleDateFormat(WorkspaceSettings.TIME_FORMAT_DEFAULT, Locale.ENGLISH)));
        setDefaultRenderer(Timestamp.class, new FormattingTableCellRenderer(isoDateFormat));

        WorkspaceSettings.getInstance().addSettingsListener(new LocaleSettingsListener());
        applyLocaleSettings(WorkspaceSettings.getInstance());
    }

    private static SimpleDateFormat createSafely(final String pattern, final String defaultPattern,
            final Locale locale) {
        try {
            if (StringUtils.isEmpty(pattern) == false) {
                return new SimpleDateFormat(pattern, locale);
            }
        } catch (Exception e) {
            logger.warn("Invalid format string found in locale settings", e); // NON-NLS
        }
        return new SimpleDateFormat(defaultPattern, locale);
    }

    public void applyLocaleSettings(final LocaleSettings localeSettings) {
        final SimpleDateFormat isoDateFormat = createSafely(localeSettings.getDatetimeFormatPattern(),
                WorkspaceSettings.DATETIME_FORMAT_DEFAULT, localeSettings.getLocale());
        final TimeZone timeZone = localeSettings.getTimeZone();
        isoDateFormat.setTimeZone(timeZone);
        setDefaultRenderer(Date.class, new FormattingTableCellRenderer(isoDateFormat));
        setDefaultRenderer(Timestamp.class, new FormattingTableCellRenderer(isoDateFormat));

        final DateCellEditor dateCellEditor = new DateCellEditor(Date.class);
        dateCellEditor.setDateFormat(isoDateFormat);
        setDefaultEditor(Date.class, dateCellEditor);

        final DateCellEditor timestampEditor = new DateCellEditor(Timestamp.class);
        timestampEditor.setDateFormat(isoDateFormat);
        setDefaultEditor(Timestamp.class, timestampEditor);

        final SimpleDateFormat dateFormat = createSafely(localeSettings.getDateFormatPattern(),
                WorkspaceSettings.DATE_FORMAT_DEFAULT, localeSettings.getLocale());
        dateFormat.setTimeZone(timeZone);
        setDefaultRenderer(java.sql.Date.class, new FormattingTableCellRenderer(dateFormat));

        final DateCellEditor sqlDateCellEditor = new DateCellEditor(java.sql.Date.class);
        sqlDateCellEditor.setDateFormat(dateFormat);
        setDefaultEditor(java.sql.Date.class, sqlDateCellEditor);

        final SimpleDateFormat timeFormat = createSafely(localeSettings.getTimeFormatPattern(),
                WorkspaceSettings.TIME_FORMAT_DEFAULT, localeSettings.getLocale());
        timeFormat.setTimeZone(timeZone);
        setDefaultRenderer(Time.class, new FormattingTableCellRenderer(timeFormat));

        final TimeCellEditor timeCellEditor = new TimeCellEditor(Time.class);
        timeCellEditor.setDateFormat(timeFormat);
        setDefaultEditor(Time.class, timeCellEditor);
    }

    /**
     * Returns true if the cell at <code>row</code> and <code>column</code> is editable.  Otherwise, invoking
     * <code>setValueAt</code> on the cell will have no effect.
     * <p/>
     * <b>Note</b>: The column is specified in the table view's display order, and not in the <code>TableModel</code>'s
     * column order.  This is an important distinction because as the user rearranges the columns in the table, the column
     * at a given index in the view will change. Meanwhile the user's actions never affect the model's column ordering.
     *
     * @param row    the row whose value is to be queried
     * @param column the column whose value is to be queried
     * @return true if the cell is editable
     * @see #setValueAt
     */
    public boolean isCellEditable(final int row, final int column) {
        final int columnIndex = convertColumnIndexToModel(column);
        if (getModel().isCellEditable(row, columnIndex)) {
            if (getCellEditor(row, columnIndex) == null) {
                // no editor, so not editable ...
                return false;
            }
            return true;
        }
        return false;
    }

    public TableCellRenderer getCellRenderer(final int row, final int viewColumn) {
        final int column = convertColumnIndexToModel(viewColumn);
        final Object value = getModel().getValueAt(row, column);
        if (value instanceof GroupingHeader) {
            return groupingCellRenderer;
        }

        final ElementMetaDataTableModel model = (ElementMetaDataTableModel) getModel();
        final Class columnClass = model.getClassForCell(row, column);
        if (columnClass.isArray()) {
            return arrayCellRenderer;
        }

        final PropertyEditor propertyEditor = model.getEditorForCell(row, column);
        if (propertyEditor != null) {
            propertyEditorCellRenderer.setPropertyEditor(propertyEditor);
            return propertyEditorCellRenderer;
        }
        final TableColumn tableColumn = getColumnModel().getColumn(column);
        final TableCellRenderer renderer = tableColumn.getCellRenderer();
        if (renderer != null) {
            return renderer;
        }

        final TableCellRenderer defaultRenderer = getDefaultRenderer(columnClass);
        if (defaultRenderer != null) {
            return defaultRenderer;
        }

        if (logger.isTraceEnabled()) {
            logger.trace("No renderer for column class " + columnClass); // NON-NLS
        }
        return getDefaultRenderer(Object.class);
    }

    public TableCellEditor getCellEditor(final int row, final int viewColumn) {
        final int column = convertColumnIndexToModel(viewColumn);
        final Object value = getModel().getValueAt(row, column);
        if (value instanceof GroupingHeader) {
            return getDefaultEditor(GroupingHeader.class);
        }

        final ElementMetaDataTableModel model = (ElementMetaDataTableModel) getModel();
        final PropertyEditor propertyEditor = model.getEditorForCell(row, column);
        final Class columnClass = model.getClassForCell(row, column);

        if (propertyEditor != null) {
            final String[] tags = propertyEditor.getTags();
            if (columnClass.isArray()) {
                arrayCellEditor.setPropertyEditorType(propertyEditor.getClass());
            } else if (tags == null || tags.length == 0) {
                propertyEditorCellEditor.setPropertyEditor(propertyEditor);
                return propertyEditorCellEditor;
            } else {
                taggedPropertyEditorCellEditor.setPropertyEditor(propertyEditor);
                return taggedPropertyEditorCellEditor;
            }
        }

        final TableColumn tableColumn = getColumnModel().getColumn(column);
        final TableCellEditor renderer = tableColumn.getCellEditor();
        if (renderer != null) {
            return renderer;
        }

        if (columnClass.isArray()) {
            return arrayCellEditor;
        }

        final TableCellEditor editor = getDefaultEditor(columnClass);
        if (editor != null && logger.isTraceEnabled()) {
            logger.trace("Using preconfigured default editor for column class " + columnClass + ": " + editor); // NON-NLS
        }
        return editor;
    }

    public void setReportDesignerContext(final ReportDesignerContext newContext) {
        final ReportDesignerContext oldContext = arrayCellEditor.getReportDesignerContext();
        if (oldContext != null) {
            oldContext.removePropertyChangeListener(this.changeHandler);
            final ReportDocumentContext oldActiveContext = getReportRenderContext();
            updateActiveContext(oldActiveContext, null);
        }

        arrayCellEditor.setReportDesignerContext(newContext);
        stringValueCellEditor.setReportDesignerContext(newContext);
        expressionsCellRenderer.setReportDesignerContext(newContext);
        expressionsCellEditor.setReportDesignerContext(newContext);
        structureFunctionCellEditor.setReportDesignerContext(newContext);

        if (newContext != null) {
            newContext.addPropertyChangeListener(ReportDesignerContext.ACTIVE_CONTEXT_PROPERTY, changeHandler);
            updateActiveContext(null, newContext.getActiveContext());
        }
    }

    protected void updateActiveContext(final ReportDocumentContext oldContext,
            final ReportDocumentContext activeContext) {
        structureFunctionCellEditor.setRenderContext(activeContext);
        reportPreProcessorCellEditor.setRenderContext(activeContext);
    }

    public ReportDesignerContext getReportDesignerContext() {
        return arrayCellEditor.getReportDesignerContext();
    }

    public ReportDocumentContext getReportRenderContext() {
        final ReportDesignerContext reportDesignerContext = getReportDesignerContext();
        if (reportDesignerContext == null) {
            return null;
        }
        return reportDesignerContext.getActiveContext();
    }

    public boolean isFormulaFragment() {
        return stringValueCellEditor.isFormulaFragment();
    }

    public void setFormulaFragment(final boolean formulaFragment) {
        stringValueCellEditor.setFormulaFragment(formulaFragment);
    }

    public void stopEditing() {
        final TableCellEditor cellEditor = getCellEditor();
        if (cellEditor != null) {
            cellEditor.stopCellEditing();
        }
    }
}