/*
* 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) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.gui.base;
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.modules.gui.common.GuiContext;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.DefaultGuiContext;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.ExportDialog;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.FormValidator;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.JStatusBar;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingCommonModule;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingGuiContext;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingUtil;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.action.ActionButton;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.action.ActionDowngrade;
import org.pentaho.reporting.engine.classic.core.modules.gui.xls.ExcelExportPlugin;
import org.pentaho.reporting.engine.classic.core.modules.misc.configstore.base.ConfigFactory;
import org.pentaho.reporting.engine.classic.core.modules.misc.configstore.base.ConfigStorage;
import org.pentaho.reporting.engine.classic.core.modules.misc.configstore.base.ConfigStoreException;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterDefinition;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.config.ModifiableConfiguration;
import org.pentaho.reporting.libraries.base.util.Messages;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
public abstract class AbstractExportDialog extends JDialog
implements ExportDialog
{
private static final Log logger = LogFactory.getLog(AbstractExportDialog.class);
/**
* Internal action class to confirm the dialog and to validate the input.
*/
private class ConfirmAction extends AbstractAction
{
/**
* Default constructor.
*/
protected ConfirmAction(final ResourceBundle resources)
{
putValue(Action.NAME, resources.getString("OptionPane.okButtonText")); //$NON-NLS-1$
}
/**
* Receives notification that the action has occurred.
*
* @param e the action event.
*/
public void actionPerformed(final ActionEvent e)
{
if (performValidate() && performConfirm())
{
applyReportParameters();
setConfirmed(true);
setVisible(false);
}
}
private void applyReportParameters()
{
final ReportParameterDefinition theParamterDefinition = reportJob.getParameterDefinition();
if (theParamterDefinition.getParameterCount() > 0)
{
final ReportParameterValues properties = parametersPanel.getReportParameterValues();
final ReportParameterValues reportParameters = reportJob.getParameterValues();
final String[] strings = properties.getColumnNames();
for (int i = 0; i < strings.length; i++)
{
final String propertyName = strings[i];
reportParameters.put(propertyName, properties.get(propertyName));
}
}
}
}
/**
* Internal action class to cancel the report processing.
*/
private class CancelAction extends AbstractAction
{
/**
* Default constructor.
*/
protected CancelAction(final ResourceBundle resources)
{
putValue(Action.NAME, resources.getString("OptionPane.cancelButtonText")); //$NON-NLS-1$
putValue(ActionDowngrade.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
}
/**
* Receives notification that the action has occurred.
*
* @param e the action event.
*/
public void actionPerformed(final ActionEvent e)
{
setConfirmed(false);
setVisible(false);
}
}
private class ExportDialogValidator extends FormValidator
{
protected ExportDialogValidator()
{
super();
}
public boolean performValidate()
{
return AbstractExportDialog.this.performValidate();
}
public Action getConfirmAction()
{
return AbstractExportDialog.this.getConfirmAction();
}
}
private class WindowCloseHandler extends WindowAdapter
{
protected WindowCloseHandler()
{
}
/**
* Invoked when a window is in the process of being closed. The close operation can be overridden at this point.
*/
public void windowClosing(final WindowEvent e)
{
final Action cancelAction = getCancelAction();
if (cancelAction != null)
{
cancelAction.actionPerformed(null);
}
else
{
setConfirmed(false);
setVisible(false);
}
}
}
private Action cancelAction;
private Action confirmAction;
private FormValidator formValidator;
private ResourceBundle resources;
private boolean confirmed;
private MasterReport reportJob;
private GuiContext guiContext;
private GuiContext defaultContext;
private Messages messages;
private JPanel parametersLayoutPanel;
private ParameterReportControllerPane parametersPanel;
/**
* Creates a non-modal dialog without a title and without a specified <code>Frame</code> owner. A shared, hidden
* frame will be set as the owner of the dialog.
*/
protected AbstractExportDialog()
{
initialize();
}
/**
* Creates a non-modal dialog without a title with the specified <code>Frame</code> as its owner. If
* <code>owner</code> is <code>null</code>, a shared, hidden frame will be set as the owner of the dialog.
*
* @param owner the <code>Frame</code> from which the dialog is displayed
*/
protected AbstractExportDialog(final Frame owner)
{
super(owner);
initialize();
}
/**
* Creates a non-modal dialog without a title with the specified <code>Dialog</code> as its owner.
*
* @param owner the non-null <code>Dialog</code> from which the dialog is displayed
*/
protected AbstractExportDialog(final Dialog owner)
{
super(owner);
initialize();
}
private void initialize()
{
defaultContext = new DefaultGuiContext(this, null);
guiContext = defaultContext;
final ResourceBundle resources = ResourceBundle.getBundle(SwingCommonModule.BUNDLE_NAME);
cancelAction = new CancelAction(resources);
confirmAction = new ConfirmAction(resources);
formValidator = new ExportDialogValidator();
setModal(true);
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowCloseHandler());
messages = new Messages(defaultContext.getLocale(), SwingCommonModule.BUNDLE_NAME,
ObjectUtilities.getClassLoader(SwingCommonModule.class));
parametersPanel = new ParameterReportControllerPane();
parametersLayoutPanel = new JPanel(new BorderLayout());
parametersLayoutPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
parametersLayoutPanel.add(parametersPanel, BorderLayout.NORTH);
}
protected JPanel createContentPane(final JComponent realContent)
{
final JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
contentPane.add(createButtonPanel(), BorderLayout.SOUTH);
contentPane.add(realContent, BorderLayout.CENTER);
final JPanel contentWithStatus = new JPanel();
contentWithStatus.setLayout(new BorderLayout());
contentWithStatus.add(contentPane, BorderLayout.CENTER);
contentWithStatus.add(getStatusBar(), BorderLayout.SOUTH);
return contentWithStatus;
}
protected JPanel createButtonPanel()
{
final JButton btnCancel = new ActionButton(getCancelAction());
final JButton btnConfirm = new ActionButton(getConfirmAction());
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 2, 5, 5));
buttonPanel.add(btnConfirm);
buttonPanel.add(btnCancel);
btnConfirm.setDefaultCapable(true);
getRootPane().setDefaultButton(btnConfirm);
buttonPanel.registerKeyboardAction(getConfirmAction(),
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
final JPanel buttonCarrier = new JPanel();
buttonCarrier.setLayout(new FlowLayout(FlowLayout.RIGHT));
buttonCarrier.add(buttonPanel);
return buttonCarrier;
}
public abstract JStatusBar getStatusBar();
protected Action getCancelAction()
{
return cancelAction;
}
protected void setCancelAction(final Action cancelAction)
{
this.cancelAction = cancelAction;
}
protected Action getConfirmAction()
{
return confirmAction;
}
protected void setConfirmAction(final Action confirmAction)
{
this.confirmAction = confirmAction;
}
protected abstract boolean performValidate();
protected FormValidator getFormValidator()
{
return formValidator;
}
protected void initializeFromJob(final MasterReport job,
final GuiContext guiContext)
{
final JStatusBar statusBar = getStatusBar();
if (statusBar != null)
{
statusBar.setIconTheme(guiContext.getIconTheme());
}
}
protected MasterReport getReportJob()
{
return reportJob;
}
protected GuiContext getGuiContext()
{
return guiContext;
}
/**
* Opens the dialog to query all necessary input from the user. This will not start the processing, as this is done
* elsewhere.
*
* @param reportJob the report that should be processed.
* @return true, if the processing should continue, false otherwise.
*/
public boolean performQueryForExport(final MasterReport reportJob,
final SwingGuiContext guiContext)
{
if (reportJob == null)
{
throw new NullPointerException();
}
if (guiContext == null)
{
throw new NullPointerException();
}
this.reportJob = reportJob;
this.guiContext = guiContext;
final Locale locale = guiContext.getLocale();
setLocale(locale);
pack();
clear();
initializeFromJob(reportJob, guiContext);
createParametersPanelContent();
final FormValidator formValidator = getFormValidator();
formValidator.setEnabled(false);
final ModifiableConfiguration repConf = reportJob.getReportConfiguration();
final boolean inputStorageEnabled = isInputStorageEnabled(repConf);
final Configuration loadedConfiguration;
if (inputStorageEnabled)
{
loadedConfiguration = loadFromConfigStore(reportJob, repConf);
}
else
{
loadedConfiguration = repConf;
}
setDialogContents(loadedConfiguration);
formValidator.setEnabled(true);
formValidator.handleValidate();
setModal(true);
SwingUtil.centerDialogInParent(this);
setVisible(true);
if (isConfirmed() == false)
{
this.guiContext = defaultContext;
return false;
}
formValidator.setEnabled(false);
final Configuration fullDialogContents = grabDialogContents(true);
final Enumeration configProperties =
fullDialogContents.getConfigProperties();
while (configProperties.hasMoreElements())
{
final String key = (String) configProperties.nextElement();
repConf.setConfigProperty(key, fullDialogContents.getConfigProperty(key));
}
if (inputStorageEnabled)
{
saveToConfigStore(reportJob, repConf);
}
formValidator.setEnabled(true);
this.reportJob = null;
this.guiContext = defaultContext;
return true;
}
private void createParametersPanelContent()
{
final ReportParameterDefinition theParamterDefinition = reportJob.getParameterDefinition();
if (theParamterDefinition.getParameterCount() > 0)
{
try
{
parametersPanel.hideControls();
parametersPanel.setReport(reportJob);
}
catch (ReportProcessingException e)
{
parametersPanel.setErrorMessage(messages.getString("AbstractExportDialog.ERROR_PARAMETERS"));
}
}
else
{
parametersPanel.setErrorMessage(messages.getString("AbstractExportDialog.NO_PARAMETERS"));
}
}
private void saveToConfigStore(final MasterReport reportJob,
final Configuration reportConfiguration)
{
final String configPath = ConfigFactory.encodePath(
reportJob.getTitle() + getConfigurationSuffix());
try
{
final boolean fullStorageEnabled = isFullInputStorageEnabled(reportConfiguration);
final Configuration dialogContents = grabDialogContents(fullStorageEnabled);
final ConfigStorage storage = ConfigFactory.getInstance().getUserStorage();
storage.store(configPath, dialogContents);
}
catch (ConfigStoreException cse)
{
AbstractExportDialog.logger.debug(messages.getString("AbstractExportDialog.DEBUG_CANT_STORE_DEFAULTS",
String.valueOf(getClass()))); //$NON-NLS-1$//$NON-NLS-2$
}
}
private Configuration loadFromConfigStore(final MasterReport reportJob,
final Configuration defaultConfig)
{
final String configPath = ConfigFactory.encodePath(
reportJob.getTitle() + getConfigurationSuffix());
final ConfigStorage storage = ConfigFactory.getInstance().getUserStorage();
try
{
return storage.load(configPath, defaultConfig);
}
catch (Exception cse)
{
AbstractExportDialog.logger.debug(messages.getString("AbstractExportDialog.DEBUG_CANT_LOAD_DEFAULTS",
String.valueOf(getClass()))); //$NON-NLS-1$ //$NON-NLS-2$
}
return defaultConfig;
}
protected abstract String getConfigurationPrefix();
/**
* Returns a new (and not connected to the default config from the job) configuration containing all properties from
* the dialog.
*
* @param full
* @return
*/
protected abstract Configuration grabDialogContents(boolean full);
protected abstract void setDialogContents(Configuration properties);
protected abstract String getConfigurationSuffix();
/**
* Retrieves the resources for this dialog. If the resources are not initialized, they get loaded on the first call to
* this method.
*
* @return this frames ResourceBundle.
*/
protected ResourceBundle getResources()
{
if (resources == null)
{
resources = ResourceBundle.getBundle(getResourceBaseName());
}
return resources;
}
protected boolean isInputStorageEnabled(final Configuration config)
{
if ("true".equals(config.getConfigProperty(
"org.pentaho.reporting.engine.classic.core.modules.gui.base.StoreExportDialogInputs")) == false)
{
return false;
}
final String confVal = config.getConfigProperty
(getConfigurationPrefix() + "StoreDialogContents"); //$NON-NLS-1$
return "none".equalsIgnoreCase(confVal) == false; //$NON-NLS-1$
}
protected boolean isFullInputStorageEnabled(final Configuration config)
{
if ("true".equals(config.getConfigProperty(
"org.pentaho.reporting.engine.classic.core.modules.gui.base.StoreExportDialogInputs")) == false)
{
return false;
}
final String confVal = config.getConfigProperty
(getConfigurationPrefix() + "StoreDialogContents"); //$NON-NLS-1$
return "all".equalsIgnoreCase(confVal); //$NON-NLS-1$
}
/**
* Returns <code>true</code> if the user confirmed the selection, and <code>false</code> otherwise. The file should
* only be saved if the result is <code>true</code>.
*
* @return A boolean.
*/
public boolean isConfirmed()
{
return confirmed;
}
/**
* Defines whether this dialog has been finished using the 'OK' or the 'Cancel' option.
*
* @param confirmed set to <code>true</code>, if OK was pressed, <code>false</code> otherwise
*/
protected void setConfirmed(final boolean confirmed)
{
this.confirmed = confirmed;
}
protected boolean performConfirm()
{
return true;
}
public abstract void clear();
protected abstract String getResourceBaseName();
/**
* Resolves file names for the exports. An occurence of "~/" at the beginning of the name will be replaced with the
* users home directory.
*
* @param baseDirectory the base directory as specified in the configuration.
* @return the file object pointing to that directory.
* @throws IllegalArgumentException if the base directory is null.
*/
protected File resolvePath(String baseDirectory)
{
if (baseDirectory == null)
{
throw new IllegalArgumentException(messages.getString(
"AbstractExportDialog.ERROR_0001_INVALID_BASE_DIR")); //$NON-NLS-1$
}
if (baseDirectory.startsWith("~/") == false) //$NON-NLS-1$
{
return new File(baseDirectory);
}
else
{
final String homeDirectory = System.getProperty("user.home"); //$NON-NLS-1$
if ("~/".equals(baseDirectory)) //$NON-NLS-1$
{
return new File(homeDirectory);
}
else
{
baseDirectory = baseDirectory.substring(2);
return new File(homeDirectory, baseDirectory);
}
}
}
protected JPanel getParametersPanel()
{
return parametersLayoutPanel;
}
}