/*!
* 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-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.designer.core.editor.parameters;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.BeanInfo;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.pentaho.reporting.designer.core.ReportDesignerContext;
import org.pentaho.reporting.designer.core.ReportDesignerDocumentContext;
import org.pentaho.reporting.designer.core.actions.global.DeleteAction;
import org.pentaho.reporting.designer.core.actions.report.AddDataFactoryAction;
import org.pentaho.reporting.designer.core.actions.report.EditQueryAction;
import org.pentaho.reporting.designer.core.settings.WorkspaceSettings;
import org.pentaho.reporting.designer.core.util.IconLoader;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition;
import org.pentaho.reporting.engine.classic.core.CompoundDataFactory;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataFactoryContext;
import org.pentaho.reporting.engine.classic.core.designtime.DataFactoryChange;
import org.pentaho.reporting.engine.classic.core.designtime.DataSourcePlugin;
import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeContext;
import org.pentaho.reporting.engine.classic.core.designtime.datafactory.DesignTimeDataFactoryContext;
import org.pentaho.reporting.engine.classic.core.metadata.DataFactoryMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.DataFactoryRegistry;
import org.pentaho.reporting.engine.classic.core.metadata.GroupedMetaDataComparator;
import org.pentaho.reporting.engine.classic.core.wizard.DataSchemaModel;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.designtime.swing.BorderlessButton;
import org.pentaho.reporting.libraries.designtime.swing.LibSwingUtil;
import org.pentaho.reporting.libraries.designtime.swing.settings.LocaleSettings;
public class ProvisionDataSourcePanel extends JPanel
{
private class RemoveDataSourceAction extends AbstractAction implements TreeSelectionListener
{
public RemoveDataSourceAction()
{
putValue(Action.SMALL_ICON, IconLoader.getInstance().getRemoveIcon());
putValue(Action.SHORT_DESCRIPTION, Messages.getString("ParameterDialog.DeleteDataSourceAction"));
setEnabled(false);
}
public void valueChanged(final TreeSelectionEvent e)
{
setEnabled(isDataSourceSelected());
}
public void actionPerformed(final ActionEvent e)
{
final int result = JOptionPane.showConfirmDialog(ProvisionDataSourcePanel.this,
Messages.getString("ParameterDialog.DeleteDataSourceWarningMessage"),
Messages.getString("ParameterDialog.DeleteDataSourceWarningTitle"), JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION)
{
final DataFactory theSelectedDataFactory = getSelectedDataSource();
// Delete data-source from structure panel
reportDesignerContext.getActiveContext().getSelectionModel().setSelectedElements(new Object[]{theSelectedDataFactory});
final DeleteAction deleteAction = new DeleteAction();
deleteAction.setReportDesignerContext(reportDesignerContext);
deleteAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""));
// Delete data-source from the Select data-source dialog
availableDataSourcesModel.remove(theSelectedDataFactory);
}
}
}
private class EditDataSourceAction extends AbstractAction implements TreeSelectionListener
{
private EditDataSourceAction()
{
putValue(Action.SMALL_ICON, IconLoader.getInstance().getEditIcon());
putValue(Action.SHORT_DESCRIPTION, Messages.getString("ParameterDialog.EditDataSourceAction"));
setEnabled(false);
}
public void valueChanged(final TreeSelectionEvent e)
{
if (getSelectedDataSource() == null)
{
setEnabled(false);
return;
}
setEnabled(true);
}
public void actionPerformed(final ActionEvent e)
{
final DataFactory dataFactory = getSelectedDataSource();
if (dataFactory == null)
{
return;
}
final DataFactoryMetaData metadata = dataFactory.getMetaData();
if (metadata.isEditable() == false)
{
return;
}
// Edit data-source from structure panel
reportDesignerContext.getActiveContext().getSelectionModel().setSelectedElements(new Object[]{dataFactory});
final EditQueryAction editQueryAction = new EditQueryAction();
editQueryAction.setReportDesignerContext(reportDesignerContext);
editQueryAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""));
final int idx = availableDataSourcesModel.indexOf(dataFactory);
if (idx == -1)
{
throw new IllegalStateException("DataSource Model is out of sync with the GUI");
}
if (editQueryAction.getEditedDataFactory() != null)
{
availableDataSourcesModel.edit(idx, editQueryAction.getEditedDataFactory());
}
}
}
private final class ShowAddDataSourcePopupAction extends AbstractAction
{
public ShowAddDataSourcePopupAction()
{
putValue(Action.SMALL_ICON, IconLoader.getInstance().getAddIcon());
}
public void actionPerformed(final ActionEvent e)
{
final JPopupMenu menu = new JPopupMenu();
createDataSourceMenu(menu);
final Object source = e.getSource();
if (source instanceof Component)
{
final Component c = (Component) source;
menu.show(c, 0, c.getHeight());
}
else
{
menu.show(ProvisionDataSourcePanel.this, 0, 0);
}
}
}
public boolean isDataSourceSelected()
{
final TreePath selectionPath = availableDataSources.getSelectionPath();
if (selectionPath == null)
{
return false;
}
return selectionPath.getLastPathComponent() instanceof DataFactoryWrapper;
}
public DataFactory getSelectedDataSource()
{
final TreePath selectionPath = availableDataSources.getSelectionPath();
if (selectionPath == null)
{
return null;
}
final int size = selectionPath.getPathCount();
if (size >= 2)
{
final DataFactoryWrapper dataFactoryWrapper =
(DataFactoryWrapper) selectionPath.getPathComponent(1);
return dataFactoryWrapper.getEditedDataFactory();
}
return null;
}
private void createDataSourceMenu(final JComponent insertDataSourcesMenu)
{
final DataFactoryMetaData[] datas = DataFactoryRegistry.getInstance().getAll();
final Map<String, Boolean> groupMap = new HashMap<String, Boolean>();
for (int i = 0; i < datas.length; i++)
{
final DataFactoryMetaData data = datas[i];
if (data.isHidden())
{
continue;
}
if (data.isEditorAvailable() == false)
{
continue;
}
final String currentGrouping = data.getGrouping(Locale.getDefault());
groupMap.put(currentGrouping, groupMap.containsKey(currentGrouping));
}
Arrays.sort(datas, new GroupedMetaDataComparator());
Object grouping = null;
JMenu subMenu = null;
boolean firstElement = true;
for (int i = 0; i < datas.length; i++)
{
final DataFactoryMetaData data = datas[i];
if (data.isHidden())
{
continue;
}
if (data.isEditorAvailable() == false)
{
continue;
}
final String currentGrouping = data.getGrouping(Locale.getDefault());
final Boolean isMultiGrouping = groupMap.get(currentGrouping);
if (firstElement == false)
{
if (ObjectUtilities.equal(currentGrouping, grouping) == false)
{
grouping = currentGrouping;
if (isMultiGrouping)
{
subMenu = new JMenu(currentGrouping);
insertDataSourcesMenu.add(subMenu);
}
}
}
else
{
firstElement = false;
grouping = currentGrouping;
if (isMultiGrouping)
{
subMenu = new JMenu(currentGrouping);
insertDataSourcesMenu.add(subMenu);
}
}
final AddDataSourceAction action = new AddDataSourceAction(data);
if (isMultiGrouping)
{
//noinspection ConstantConditions
subMenu.add(new JMenuItem(action));
}
else
{
insertDataSourcesMenu.add(new JMenuItem(action));
}
}
}
private class AddDataSourceAction extends AbstractAction
{
private DataFactoryMetaData dataSourcePlugin;
private AddDataSourceAction(final DataFactoryMetaData dataSourcePlugin)
{
this.dataSourcePlugin = dataSourcePlugin;
putValue(Action.NAME, dataSourcePlugin.getDisplayName(Locale.getDefault()));
putValue(Action.SHORT_DESCRIPTION, dataSourcePlugin.getDescription(Locale.getDefault()));
final Image image = dataSourcePlugin.getIcon(Locale.getDefault(), BeanInfo.ICON_COLOR_32x32);
if (image != null)
{
putValue(Action.SMALL_ICON, new ImageIcon(image));
}
}
public void actionPerformed(final ActionEvent e)
{
final DataSourcePlugin editor = dataSourcePlugin.createEditor();
if (editor == null)
{
return;
}
final DataFactory dataFactory = editor.performEdit(new DataSourceDesignTimeContext(), null, null, null);
if (dataFactory == null)
{
return;
}
// Add data-source factory into Structure panel.
// This happens after user adds a new data source from the Edit DataSource dialog
AddDataFactoryAction.addDataFactory(reportDesignerContext.getActiveContext(), dataFactory, new DataFactoryChange[0]);
availableDataSourcesModel.add(new DataFactoryWrapper(null, dataFactory));
expandAllNodes();
SwingUtilities.invokeLater(new DataTabSetVisible(reportDesignerContext, reportDesignerContext.getActiveContext()));
}
}
private static class DataTabSetVisible implements Runnable
{
private ReportDesignerContext designerContext;
private ReportDesignerDocumentContext<?> activeContext;
public DataTabSetVisible(final ReportDesignerContext designerContext,
final ReportDesignerDocumentContext<?> activeContext)
{
this.designerContext = designerContext;
this.activeContext = activeContext;
}
public void run()
{
if (this.activeContext == null)
{
return;
}
designerContext.setActiveDocument(activeContext);
designerContext.getView().showDataTree();
}
}
private class DataSourceDesignTimeContext implements DesignTimeContext
{
public DataSourceDesignTimeContext()
{
}
/**
* The currently active report (or subreport).
*
* @return the active report.
*/
public AbstractReportDefinition getReport()
{
return reportDesignerContext.getActiveContext().getContextRoot();
}
/**
* The parent window in the GUI for showing modal dialogs.
*
* @return the window or null, if there is no parent.
*/
public Window getParentWindow()
{
return LibSwingUtil.getWindowAncestor(ProvisionDataSourcePanel.this);
}
public DataSchemaModel getDataSchemaModel()
{
// todo: Filter so that only env- and parameter are visible here.
return reportDesignerContext.getActiveContext().getReportDataSchemaModel();
}
public void error(final Exception e)
{
UncaughtExceptionsModel.getInstance().addException(e);
}
public void userError(final Exception e)
{
UncaughtExceptionsModel.getInstance().addException(e);
}
public LocaleSettings getLocaleSettings()
{
return WorkspaceSettings.getInstance();
}
public boolean isShowExpertItems()
{
return WorkspaceSettings.getInstance().isShowExpertItems();
}
public boolean isShowDeprecatedItems()
{
return WorkspaceSettings.getInstance().isShowDeprecatedItems();
}
public DataFactoryContext getDataFactoryContext()
{
return new DesignTimeDataFactoryContext(reportDesignerContext.getActiveContext().getContextRoot());
}
}
private JTree availableDataSources;
private DataFactoryTreeModel availableDataSourcesModel;
private ReportDesignerContext reportDesignerContext;
public ProvisionDataSourcePanel()
{
availableDataSourcesModel = new DataFactoryTreeModel();
availableDataSources = new JTree(availableDataSourcesModel);
availableDataSources.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
availableDataSources.setCellRenderer(new DataFactoryTreeCellRenderer());
availableDataSources.setRootVisible(false);
init();
}
public ReportDesignerContext getReportDesignerContext()
{
return reportDesignerContext;
}
public void setReportDesignerContext(final ReportDesignerContext reportDesignerContext)
{
this.reportDesignerContext = reportDesignerContext;
}
public void importDataSourcesFromMaster(final CompoundDataFactory cdf)
{
availableDataSourcesModel.importFromReport(cdf);
}
public DataFactoryTreeModel getDataFactoryTreeModel()
{
return availableDataSourcesModel;
}
public JTree getDataSourcesTree()
{
return availableDataSources;
}
public String getSelectedQueryName()
{
final Object node = availableDataSources.getLastSelectedPathComponent();
if (node != null)
{
return node.toString();
}
return "";
}
public void expandAllNodes()
{
for (int i = 0; i < availableDataSources.getRowCount(); i++)
{
availableDataSources.expandRow(i);
}
}
protected void init()
{
final RemoveDataSourceAction removeAction = new RemoveDataSourceAction();
final EditDataSourceAction editDataSourceAction = new EditDataSourceAction();
final ShowAddDataSourcePopupAction showAddDataSourcePopupAction = new ShowAddDataSourcePopupAction();
availableDataSources.addTreeSelectionListener(editDataSourceAction);
availableDataSources.addTreeSelectionListener(removeAction);
final JScrollPane theScrollPanel = new JScrollPane(availableDataSources);
theScrollPanel.setAutoscrolls(true);
final JPanel theDataSetsButtonPanel = new JPanel();
theDataSetsButtonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
theDataSetsButtonPanel.add(new BorderlessButton(showAddDataSourcePopupAction));
theDataSetsButtonPanel.add(new BorderlessButton(editDataSourceAction));
theDataSetsButtonPanel.add(new BorderlessButton(removeAction));
final JPanel theControlsPanel = new JPanel(new BorderLayout());
theControlsPanel.add(new JLabel(Messages.getString("ParameterDialog.DataSources")), BorderLayout.WEST);
theControlsPanel.add(theDataSetsButtonPanel, BorderLayout.EAST);
final JPanel dataSetsPanel = new JPanel(new BorderLayout());
dataSetsPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
dataSetsPanel.add(theScrollPanel, BorderLayout.CENTER);
dataSetsPanel.add(theControlsPanel, BorderLayout.NORTH);
setLayout(new BorderLayout());
add(dataSetsPanel, BorderLayout.CENTER);
}
}