/*
* 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) 2009 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.reporting.designer.extensions.legacycharts;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.Locale;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import org.pentaho.reporting.designer.core.ReportDesignerContext;
import org.pentaho.reporting.designer.core.editor.ReportRenderContext;
import org.pentaho.reporting.designer.core.editor.expressions.ExpressionPropertiesTableModel;
import org.pentaho.reporting.designer.core.util.ActionToggleButton;
import org.pentaho.reporting.designer.core.util.ExpressionListCellRenderer;
import org.pentaho.reporting.designer.core.util.table.ElementMetaDataTable;
import org.pentaho.reporting.designer.core.util.table.GroupedMetaTableModel;
import org.pentaho.reporting.designer.core.util.table.GroupingModel;
import org.pentaho.reporting.designer.core.util.table.SortHeaderPanel;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.metadata.ExpressionMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ExpressionPropertyMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ExpressionRegistry;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingUtil;
import org.pentaho.reporting.engine.classic.extensions.legacy.charts.LegacyChartElementModule;
import org.pentaho.reporting.libraries.designtime.swing.CommonDialog;
import org.pentaho.reporting.libraries.designtime.swing.SmartComboBox;
/**
* Todo: Document Me Todo: Changes in the name of the datasource-expressions must be copied into the referenced name of
* the chart-expression Todo: Copy the changes back
*
* @author Thomas Morgner
*/
public class LegacyChartEditorDialog extends CommonDialog
{
private class SelectChartExpressionAction extends AbstractAction implements ListDataListener
{
private Class expressionType;
private Icon standardIcon;
private Icon selectedIcon;
private SelectChartExpressionAction(final Class expressionType)
{
this.expressionType = expressionType;
final ExpressionRegistry expressionRegistry = ExpressionRegistry.getInstance();
final ExpressionMetaData metaData = expressionRegistry.getExpressionMetaData(expressionType.getName());
putValue(Action.NAME, metaData.getMetaAttribute("short-name", Locale.getDefault())); // NON-NLS
final String defaultIcon = metaData.getMetaAttribute("icon", Locale.getDefault()); // NON-NLS
final URL defaultIconUrl = LegacyChartEditorDialog.class.getResource(defaultIcon);
if (defaultIconUrl != null)
{
standardIcon = new ImageIcon(defaultIconUrl);
putValue(Action.SMALL_ICON, standardIcon);
}
final String selectedIconProperty = metaData.getMetaAttribute("selected-icon", Locale.getDefault()); // NON-NLS
final URL selectedIconUrl = LegacyChartEditorDialog.class.getResource(selectedIconProperty);
if (selectedIconUrl != null)
{
selectedIcon = new ImageIcon(selectedIconUrl);
}
}
public void actionPerformed(final ActionEvent e)
{
final ExpressionMetaData data = ExpressionRegistry.getInstance().getExpressionMetaData(expressionType.getName());
editModel.getChartExpressionsModel().setSelectedItem(data);
}
public void intervalAdded(final ListDataEvent e)
{
// ignore
}
public void intervalRemoved(final ListDataEvent e)
{
// ignore
}
public void contentsChanged(final ListDataEvent e)
{
final ExpressionMetaData o = (ExpressionMetaData) editModel.getChartExpressionsModel().getSelectedItem();
if (o != null && expressionType.equals(o.getExpressionType()))
{
putValue("selected", Boolean.TRUE); // NON-NLS
putValue(Action.SMALL_ICON, selectedIcon);
}
else
{
putValue("selected", Boolean.FALSE); // NON-NLS
putValue(Action.SMALL_ICON, standardIcon);
}
}
}
private class ChartExpressionChangeHandler implements PropertyChangeListener
{
private ChartExpressionChangeHandler()
{
}
/**
* 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)
{
if (LegacyChartEditModel.CHART_EXPRESSION_PROPERTY.equals(evt.getPropertyName()) == false)
{
return;
}
final Expression o = (Expression) evt.getNewValue();
if (o == null)
{
chartPropertiesTableModel.setData(EMPTY_EXPRESSION);
dataSourceTabbedPane.setEnabledAt(1, false);
dataSourceTabbedPane.setSelectedIndex(0);
return;
}
chartPropertiesTableModel.setData(new Expression[]{o});
final ChartType type = ChartType.getTypeByChartExpression(o.getClass());
if (type == null)
{
dataSourceTabbedPane.setEnabledAt(1, false);
dataSourceTabbedPane.setSelectedIndex(0);
return;
}
dataSourceTabbedPane.setEnabledAt(1, type.getSecondaryDataSourceProperty() != null);
}
}
private class PrimaryDataSourceChangeHandler implements PropertyChangeListener
{
private PrimaryDataSourceChangeHandler()
{
}
/**
* 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)
{
if (LegacyChartEditModel.PRIMARY_DATA_SOURCE_PROPERTY.equals(evt.getPropertyName()) == false)
{
return;
}
final Expression o = (Expression) evt.getNewValue();
if (o == null)
{
primaryDataSourcePropertiesTableModel.setData(EMPTY_EXPRESSION);
return;
}
primaryDataSourcePropertiesTableModel.setData(new Expression[]{o});
}
}
private class SecondaryDataSourceChangeHandler implements PropertyChangeListener
{
private SecondaryDataSourceChangeHandler()
{
}
/**
* 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)
{
if (LegacyChartEditModel.SECONDARY_DATA_SOURCE_PROPERTY.equals(evt.getPropertyName()) == false)
{
return;
}
final Expression o = (Expression) evt.getNewValue();
if (o == null)
{
secondaryDataSourcePropertiesTableModel.setData(EMPTY_EXPRESSION);
return;
}
secondaryDataSourcePropertiesTableModel.setData(new Expression[]{o});
}
}
protected static class ChartExpressionPropertiesTableModel extends ExpressionPropertiesTableModel
{
public ChartExpressionPropertiesTableModel()
{
setFilterInlineExpressionProperty(true);
}
protected boolean isFiltered(final ExpressionPropertyMetaData metaData)
{
if ("linesDataSource".equals(metaData.getName())) // NON-NLS
{
return true;
}
if ("secondaryDataSet".equals(metaData.getName())) // NON-NLS
{
return true;
}
if ("dataSource".equals(metaData.getName())) // NON-NLS
{
return true;
}
return super.isFiltered(metaData);
}
}
private ExpressionPropertiesTableModel chartPropertiesTableModel;
private ExpressionPropertiesTableModel primaryDataSourcePropertiesTableModel;
private ExpressionPropertiesTableModel secondaryDataSourcePropertiesTableModel;
private JTabbedPane dataSourceTabbedPane;
private ElementMetaDataTable chartTable;
private ElementMetaDataTable primaryDataSourceTable;
private ElementMetaDataTable secondaryDataSourceTable;
private LegacyChartEditModel editModel;
private static final Expression[] EMPTY_EXPRESSION = new Expression[0];
public LegacyChartEditorDialog() throws HeadlessException
{
init();
}
public LegacyChartEditorDialog(final Frame owner) throws HeadlessException
{
super(owner);
init();
}
public LegacyChartEditorDialog(final Dialog owner) throws HeadlessException
{
super(owner);
init();
}
protected void init()
{
setTitle(Messages.getInstance().getString("ChartEditorTitle"));
editModel = new LegacyChartEditModel();
editModel.addPropertyChangeListener(LegacyChartEditModel.CHART_EXPRESSION_PROPERTY, new ChartExpressionChangeHandler());
editModel.addPropertyChangeListener(LegacyChartEditModel.PRIMARY_DATA_SOURCE_PROPERTY, new PrimaryDataSourceChangeHandler());
editModel.addPropertyChangeListener(LegacyChartEditModel.SECONDARY_DATA_SOURCE_PROPERTY, new SecondaryDataSourceChangeHandler());
chartTable = new ElementMetaDataTable();
chartPropertiesTableModel = new ChartExpressionPropertiesTableModel();
primaryDataSourceTable = new ElementMetaDataTable();
primaryDataSourcePropertiesTableModel = new ExpressionPropertiesTableModel();
primaryDataSourcePropertiesTableModel.setFilterInlineExpressionProperty(true);
secondaryDataSourceTable = new ElementMetaDataTable();
secondaryDataSourcePropertiesTableModel = new ExpressionPropertiesTableModel();
secondaryDataSourcePropertiesTableModel.setFilterInlineExpressionProperty(true);
dataSourceTabbedPane = new JTabbedPane();
dataSourceTabbedPane.add(Messages.getInstance().getString("PrimaryDataSource"), createPrimaryDataSourcePanel());
dataSourceTabbedPane.add(Messages.getInstance().getString("SecondaryDataSource"), createSecondaryDataSourcePanel());
super.init();
SwingUtil.centerDialogInParent(this);
}
protected Component createContentPane()
{
final JSplitPane expressionsPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
expressionsPane.setLeftComponent(createChartPanel());
expressionsPane.setRightComponent(dataSourceTabbedPane);
final JSplitPane previewPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
previewPane.setBottomComponent(expressionsPane);
final JPanel contentPane = new JPanel();
final Border border = contentPane.getBorder();
final Border margin = new EmptyBorder(5, 20, 0, 0);
contentPane.setBorder(new CompoundBorder(border, margin));
contentPane.setLayout(new BorderLayout());
contentPane.add(createChartSelectorButtonPane(), BorderLayout.NORTH);
contentPane.add(previewPane, BorderLayout.CENTER);
previewPane.setDividerLocation(300);
expressionsPane.setDividerLocation(350);
return contentPane;
}
private JPanel createSecondaryDataSourcePanel()
{
final JPanel innerSecondaryDataSourcePanel = new JPanel(new BorderLayout());
innerSecondaryDataSourcePanel.add(new SortHeaderPanel(secondaryDataSourcePropertiesTableModel),
BorderLayout.NORTH);
final JComboBox comboBox = new SmartComboBox(editModel.getSecondaryDataSourcesModel());
comboBox.setRenderer(new ExpressionListCellRenderer());
innerSecondaryDataSourcePanel.add(comboBox, BorderLayout.CENTER);
final JPanel secondaryDataSourcePanel = new JPanel(new BorderLayout());
secondaryDataSourcePanel.add(innerSecondaryDataSourcePanel, BorderLayout.NORTH);
secondaryDataSourcePanel.add(configureExpressionTable(secondaryDataSourceTable,
secondaryDataSourcePropertiesTableModel), BorderLayout.CENTER);
return secondaryDataSourcePanel;
}
private JPanel createPrimaryDataSourcePanel()
{
final JPanel innerPrimaryDataSourcePanel = new JPanel(new BorderLayout());
innerPrimaryDataSourcePanel.add(new SortHeaderPanel(primaryDataSourcePropertiesTableModel), BorderLayout.NORTH);
final JComboBox comboBox = new SmartComboBox(editModel.getPrimaryDataSourcesModel());
comboBox.setRenderer(new ExpressionListCellRenderer());
innerPrimaryDataSourcePanel.add(comboBox, BorderLayout.CENTER);
final JPanel primaryDataSourcePanel = new JPanel(new BorderLayout());
primaryDataSourcePanel.add(innerPrimaryDataSourcePanel, BorderLayout.NORTH);
primaryDataSourcePanel.add(configureExpressionTable(primaryDataSourceTable, primaryDataSourcePropertiesTableModel),
BorderLayout.CENTER);
return primaryDataSourcePanel;
}
private JPanel createChartPanel()
{
final JPanel innerChartExpressionPanel = new JPanel(new BorderLayout());
innerChartExpressionPanel.add(new SortHeaderPanel(chartPropertiesTableModel), BorderLayout.NORTH);
final JComboBox comboBox = new SmartComboBox(editModel.getChartExpressionsModel());
comboBox.setRenderer(new ExpressionListCellRenderer());
innerChartExpressionPanel.add(comboBox, BorderLayout.CENTER);
final JPanel chartExpressionPanel = new JPanel(new BorderLayout());
chartExpressionPanel.add(innerChartExpressionPanel, BorderLayout.NORTH);
chartExpressionPanel.add
(configureExpressionTable(chartTable, chartPropertiesTableModel), BorderLayout.CENTER);
return chartExpressionPanel;
}
private JComponent configureExpressionTable(final ElementMetaDataTable table,
final GroupingModel dataModel)
{
table.setModel(new GroupedMetaTableModel(dataModel));
return new JScrollPane(table);
}
private Component createChartSelectorButtonPane()
{
final ChartType[] types = ChartType.values();
final ButtonGroup buttonGroup = new ButtonGroup();
final JPanel buttonCarrier = new JPanel();
buttonCarrier.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
for (int i = 0; i < types.length; i++)
{
final ChartType type = types[i];
final SelectChartExpressionAction action = new SelectChartExpressionAction(type.getExpressionType());
editModel.getChartExpressionsModel().addListDataListener(action);
final ActionToggleButton button = new ActionToggleButton();
button.putClientProperty("hideActionText", Boolean.TRUE); // NON-NLS
button.setAction(action);
button.setBorder(new EmptyBorder(0, 0, 0, 0));
buttonGroup.add(button);
buttonCarrier.add(button);
}
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(buttonCarrier, BorderLayout.NORTH);
return panel;
}
public ChartEditingResult performEdit(final Element element,
final ReportDesignerContext reportDesignerContext) throws CloneNotSupportedException
{
if (element == null)
{
throw new NullPointerException();
}
if (reportDesignerContext == null)
{
throw new NullPointerException();
}
if (LegacyChartsUtil.isLegacyChartElement(element) == false)
{
return null;
}
try
{
final ReportRenderContext activeContext = reportDesignerContext.getActiveContext();
editModel.setActiveContext(activeContext);
chartTable.setReportDesignerContext(reportDesignerContext);
primaryDataSourceTable.setReportDesignerContext(reportDesignerContext);
secondaryDataSourceTable.setReportDesignerContext(reportDesignerContext);
final Element editableElement = element.derive();
final Expression chartExpression =
editableElement.getAttributeExpression(AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE);
final Expression originalPrimaryDataSourceExpression;
final Expression originalSecondaryDataSourceExpression;
if (chartExpression != null)
{
originalPrimaryDataSourceExpression = extractPrimaryDatasource(element);
originalSecondaryDataSourceExpression = extractSecondaryDatasource(element);
editModel.setChartExpression(chartExpression.getInstance());
if (originalPrimaryDataSourceExpression != null)
{
editModel.setPrimaryDataSource(originalPrimaryDataSourceExpression.getInstance());
}
else
{
editModel.setPrimaryDataSource(null);
}
if (originalSecondaryDataSourceExpression != null)
{
editModel.setSecondaryDataSource(originalSecondaryDataSourceExpression.getInstance());
}
else
{
editModel.setSecondaryDataSource(null);
}
}
else
{
editModel.setChartExpression(null);
editModel.setPrimaryDataSource(null);
editModel.setSecondaryDataSource(null);
originalPrimaryDataSourceExpression = null;
originalSecondaryDataSourceExpression = null;
}
if (editModel.getCurrentChartType() != null)
{
final ChartType chartType = editModel.getCurrentChartType();
if (editModel.getPrimaryDataSource() == null)
{
final Class dataSourceImplementation = chartType.getPreferredPrimaryDataSourceImplementation();
final ExpressionMetaData data =
ExpressionRegistry.getInstance().getExpressionMetaData(dataSourceImplementation.getName());
editModel.getPrimaryDataSourcesModel().setSelectedItem(data);
}
if (editModel.getSecondaryDataSource() == null)
{
final Class dataSourceImplementation = chartType.getPreferredSecondaryDataSourceImplementation();
if (dataSourceImplementation != null)
{
final ExpressionMetaData data =
ExpressionRegistry.getInstance().getExpressionMetaData(dataSourceImplementation.getName());
editModel.getSecondaryDataSourcesModel().setSelectedItem(data);
}
}
}
if (performEdit() == false)
{
return null;
}
return new ChartEditingResult
(chartExpression, originalPrimaryDataSourceExpression, originalSecondaryDataSourceExpression,
editModel.getChartExpression(), editModel.getPrimaryDataSource(), editModel.getSecondaryDataSource());
}
finally
{
this.editModel.setActiveContext(null);
chartTable.setReportDesignerContext(null);
primaryDataSourceTable.setReportDesignerContext(null);
secondaryDataSourceTable.setReportDesignerContext(null);
}
}
private Expression extractSecondaryDatasource(final Element element)
{
final Expression originalSecondaryDataSourceExpression;
final Object secondaryDataSource = element.getAttribute
(LegacyChartElementModule.NAMESPACE, LegacyChartElementModule.SECONDARY_DATA_COLLECTOR_FUNCTION_ATTRIBUTE);
if (secondaryDataSource instanceof Expression)
{
originalSecondaryDataSourceExpression = (Expression) secondaryDataSource;
}
else
{
originalSecondaryDataSourceExpression = null;
}
return originalSecondaryDataSourceExpression;
}
private Expression extractPrimaryDatasource(final Element element)
{
final Expression originalPrimaryDataSourceExpression;
final Object primaryDataSourceRaw = element.getAttribute
(LegacyChartElementModule.NAMESPACE, LegacyChartElementModule.PRIMARY_DATA_COLLECTOR_FUNCTION_ATTRIBUTE);
if (primaryDataSourceRaw instanceof Expression)
{
originalPrimaryDataSourceExpression = (Expression) primaryDataSourceRaw;
}
else
{
originalPrimaryDataSourceExpression = null;
}
return originalPrimaryDataSourceExpression;
}
}