001    /**
002     * ========================================
003     * JFreeReport : a free Java report library
004     * ========================================
005     *
006     * Project Info:  http://reporting.pentaho.org/
007     *
008     * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009     *
010     * This library is free software; you can redistribute it and/or modify it under the terms
011     * of the GNU Lesser General Public License as published by the Free Software Foundation;
012     * either version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016     * See the GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public License along with this
019     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020     * Boston, MA 02111-1307, USA.
021     *
022     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023     * in the United States and other countries.]
024     *
025     * ------------
026     * $Id: AbstractExportDialog.java 3525 2007-10-16 11:43:48Z tmorgner $
027     * ------------
028     * (C) Copyright 2000-2005, by Object Refinery Limited.
029     * (C) Copyright 2005-2007, by Pentaho Corporation.
030     */
031    package org.jfree.report.modules.gui.swing.common;
032    
033    import java.awt.Dialog;
034    import java.awt.Frame;
035    import java.awt.event.ActionEvent;
036    import java.awt.event.WindowAdapter;
037    import java.awt.event.WindowEvent;
038    import java.io.File;
039    import java.util.Enumeration;
040    import java.util.Locale;
041    import java.util.ResourceBundle;
042    import javax.swing.AbstractAction;
043    import javax.swing.Action;
044    import javax.swing.JDialog;
045    
046    import org.jfree.base.config.ModifiableConfiguration;
047    import org.jfree.report.flow.ReportJob;
048    import org.jfree.report.modules.gui.common.GuiContext;
049    import org.jfree.report.modules.preferences.base.ConfigFactory;
050    import org.jfree.report.modules.preferences.base.ConfigStorage;
051    import org.jfree.report.modules.preferences.base.ConfigStoreException;
052    import org.jfree.util.Configuration;
053    import org.jfree.util.Log;
054    
055    public abstract class AbstractExportDialog extends JDialog
056      implements ExportDialog
057    {
058      /**
059       * Internal action class to confirm the dialog and to validate the input.
060       */
061      private class ConfirmAction extends AbstractAction
062      {
063        /**
064         * Default constructor.
065         */
066        private ConfirmAction(final ResourceBundle resources)
067        {
068          putValue(Action.NAME, resources.getString("OptionPane.okButtonText"));
069        }
070    
071        /**
072         * Receives notification that the action has occurred.
073         *
074         * @param e the action event.
075         */
076        public void actionPerformed(final ActionEvent e)
077        {
078          if (performValidate() && performConfirm())
079          {
080            setConfirmed(true);
081            setVisible(false);
082          }
083        }
084      }
085    
086      /**
087       * Internal action class to cancel the report processing.
088       */
089      private class CancelAction extends AbstractAction
090      {
091        /**
092         * Default constructor.
093         */
094        private CancelAction(final ResourceBundle resources)
095        {
096          putValue(Action.NAME, resources.getString("OptionPane.cancelButtonText"));
097        }
098    
099        /**
100         * Receives notification that the action has occurred.
101         *
102         * @param e the action event.
103         */
104        public void actionPerformed(final ActionEvent e)
105        {
106          setConfirmed(false);
107          setVisible(false);
108        }
109      }
110    
111      private class ExportDialogValidator extends FormValidator
112      {
113        private ExportDialogValidator()
114        {
115          super();
116        }
117    
118        public boolean performValidate()
119        {
120          return AbstractExportDialog.this.performValidate();
121        }
122    
123        public Action getConfirmAction()
124        {
125          return AbstractExportDialog.this.getConfirmAction();
126        }
127      }
128    
129      private class WindowCloseHandler extends WindowAdapter
130      {
131        private WindowCloseHandler()
132        {
133        }
134    
135        /**
136         * Invoked when a window is in the process of being closed. The close
137         * operation can be overridden at this point.
138         */
139        public void windowClosing(final WindowEvent e)
140        {
141          final Action cancelAction = getCancelAction();
142          if (cancelAction != null)
143          {
144            cancelAction.actionPerformed(null);
145          }
146          else
147          {
148            setConfirmed(false);
149            setVisible(false);
150          }
151        }
152      }
153    
154      private Action cancelAction;
155      private Action confirmAction;
156      private FormValidator formValidator;
157      private ResourceBundle resources;
158      private boolean confirmed;
159      private ReportJob reportJob;
160      private GuiContext guiContext;
161    
162      /**
163       * Creates a non-modal dialog without a title and without a specified
164       * <code>Frame</code> owner.  A shared, hidden frame will be set as the owner
165       * of the dialog.
166       */
167      public AbstractExportDialog()
168      {
169        initialize();
170      }
171    
172      /**
173       * Creates a non-modal dialog without a title with the specified
174       * <code>Frame</code> as its owner.  If <code>owner</code> is
175       * <code>null</code>, a shared, hidden frame will be set as the owner of the
176       * dialog.
177       *
178       * @param owner the <code>Frame</code> from which the dialog is displayed
179       */
180      public AbstractExportDialog(final Frame owner)
181      {
182        super(owner);
183        initialize();
184      }
185    
186    
187      /**
188       * Creates a non-modal dialog without a title with the specified
189       * <code>Dialog</code> as its owner.
190       *
191       * @param owner the non-null <code>Dialog</code> from which the dialog is
192       *              displayed
193       */
194      public AbstractExportDialog(final Dialog owner)
195      {
196        super(owner);
197        initialize();
198      }
199    
200      private void initialize()
201      {
202        final ResourceBundle resources = ResourceBundle.getBundle(SwingCommonModule.BUNDLE_NAME);
203    
204        cancelAction = new CancelAction(resources);
205        confirmAction = new ConfirmAction(resources);
206    
207        formValidator = new ExportDialogValidator();
208        setModal(true);
209        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
210        addWindowListener(new WindowCloseHandler());
211      }
212    
213    
214      public abstract JStatusBar getStatusBar();
215    
216      protected Action getCancelAction()
217      {
218        return cancelAction;
219      }
220    
221      protected void setCancelAction(final Action cancelAction)
222      {
223        this.cancelAction = cancelAction;
224      }
225    
226      protected Action getConfirmAction()
227      {
228        return confirmAction;
229      }
230    
231      protected void setConfirmAction(final Action confirmAction)
232      {
233        this.confirmAction = confirmAction;
234      }
235    
236      protected abstract boolean performValidate();
237    
238      protected FormValidator getFormValidator()
239      {
240        return formValidator;
241      }
242    
243      protected abstract void initializeFromJob(ReportJob job,
244                                                final GuiContext guiContext);
245    
246      protected ReportJob getReportJob()
247      {
248        return reportJob;
249      }
250    
251      protected GuiContext getGuiContext()
252      {
253        return guiContext;
254      }
255    
256      /**
257       * Opens the dialog to query all necessary input from the user. This will not
258       * start the processing, as this is done elsewhere.
259       *
260       * @param report the report that should be processed.
261       * @return true, if the processing should continue, false otherwise.
262       */
263      public boolean performQueryForExport(final ReportJob reportJob,
264                                           final GuiContext guiContext)
265      {
266        this.reportJob = reportJob;
267        this.guiContext = guiContext;
268    
269        final Locale locale = reportJob.getReportStructureRoot().getLocale();
270        setLocale(locale);
271        pack();
272        clear();
273        initializeFromJob(reportJob, guiContext);
274    
275        final FormValidator formValidator = getFormValidator();
276        formValidator.setEnabled(false);
277        final ModifiableConfiguration repConf = reportJob.getConfiguration();
278        final boolean inputStorageEnabled = isInputStorageEnabled(repConf);
279    
280        final Configuration loadedConfiguration;
281        if (inputStorageEnabled)
282        {
283          loadedConfiguration = loadFromConfigStore(reportJob, repConf);
284        }
285        else
286        {
287          loadedConfiguration = repConf;
288        }
289    
290        setDialogContents(loadedConfiguration);
291    
292        formValidator.setEnabled(true);
293        formValidator.handleValidate();
294        setModal(true);
295        setVisible(true);
296        if (isConfirmed() == false)
297        {
298          return false;
299        }
300    
301        formValidator.setEnabled(false);
302    
303        final Configuration fullDialogContents = grabDialogContents(true);
304        final Enumeration configProperties =
305            fullDialogContents.getConfigProperties();
306        while (configProperties.hasMoreElements())
307        {
308          final String key = (String) configProperties.nextElement();
309          repConf.setConfigProperty(key, fullDialogContents.getConfigProperty(key));
310        }
311    
312        if (inputStorageEnabled)
313        {
314          saveToConfigStore(reportJob, repConf);
315        }
316    
317        formValidator.setEnabled(true);
318        this.reportJob = null;
319        return true;
320      }
321    
322      private void saveToConfigStore(final ReportJob reportJob,
323                                     final Configuration reportConfiguration)
324      {
325        final String configPath = ConfigFactory.encodePath(
326            reportJob.getName() + getConfigurationSuffix());
327    
328        try
329        {
330          final boolean fullStorageEnabled = isFullInputStorageEnabled(reportConfiguration);
331          final Configuration dialogContents = grabDialogContents(fullStorageEnabled);
332          final ConfigStorage storage = ConfigFactory.getInstance().getUserStorage();
333          storage.store(configPath, dialogContents);
334        }
335        catch (ConfigStoreException cse)
336        {
337          Log.debug("Unable to store the defaults in Export export dialog. [" + getClass() + "]");
338        }
339      }
340    
341      private Configuration loadFromConfigStore(final ReportJob reportJob,
342                                                final Configuration defaultConfig)
343      {
344        final String configPath = ConfigFactory.encodePath(
345            reportJob.getName() + getConfigurationSuffix());
346        final ConfigStorage storage = ConfigFactory.getInstance().getUserStorage();
347        try
348        {
349          return storage.load(configPath, defaultConfig);
350        }
351        catch (Exception cse)
352        {
353          Log.debug("Unable to load the defaults in Export export dialog. [" + getClass() + "]");
354        }
355        return defaultConfig;
356      }
357    
358      protected abstract String getConfigurationPrefix();
359    
360      /**
361       * Returns a new (and not connected to the default config from the job)
362       * configuration containing all properties from the dialog.
363       *
364       * @param full
365       * @return
366       */
367      protected abstract Configuration grabDialogContents(boolean full);
368    
369      protected abstract void setDialogContents(Configuration properties);
370    
371      protected abstract String getConfigurationSuffix();
372    
373      /**
374       * Retrieves the resources for this dialog. If the resources are not
375       * initialized, they get loaded on the first call to this method.
376       *
377       * @return this frames ResourceBundle.
378       */
379      protected ResourceBundle getResources()
380      {
381        if (resources == null)
382        {
383          resources = ResourceBundle.getBundle(getResourceBaseName());
384        }
385        return resources;
386      }
387    
388      protected boolean isInputStorageEnabled(final Configuration config)
389      {
390        final String confVal = config.getConfigProperty
391            (getConfigurationPrefix() + "StoreDialogContents");
392        return "none".equalsIgnoreCase(confVal) == false;
393      }
394    
395      protected boolean isFullInputStorageEnabled(final Configuration config)
396      {
397        final String confVal = config.getConfigProperty
398            (getConfigurationPrefix() + "StoreDialogContents");
399        return "all".equalsIgnoreCase(confVal);
400      }
401    
402      /**
403       * Returns <code>true</code> if the user confirmed the selection, and
404       * <code>false</code> otherwise.  The file should only be saved if the result
405       * is <code>true</code>.
406       *
407       * @return A boolean.
408       */
409      public boolean isConfirmed()
410      {
411        return confirmed;
412      }
413    
414      /**
415       * Defines whether this dialog has been finished using the 'OK' or the
416       * 'Cancel' option.
417       *
418       * @param confirmed set to <code>true</code>, if OK was pressed,
419       *                  <code>false</code> otherwise
420       */
421      protected void setConfirmed(final boolean confirmed)
422      {
423        this.confirmed = confirmed;
424      }
425    
426      protected boolean performConfirm()
427      {
428        return true;
429      }
430    
431      public abstract void clear();
432    
433      protected abstract String getResourceBaseName();
434    
435    
436      /**
437       * Resolves file names for the exports. An occurence of "~/" at the beginning
438       * of the name will be replaced with the users home directory.
439       *
440       * @param baseDirectory the base directory as specified in the configuration.
441       * @return the file object pointing to that directory.
442       * @throws org.jfree.base.modules.ModuleInitializeException
443       *                                  if an error occured or the directory could
444       *                                  not be created.
445       * @throws IllegalArgumentException if the base directory is null.
446       */
447      protected File resolvePath(String baseDirectory)
448      {
449        if (baseDirectory == null)
450        {
451          throw new IllegalArgumentException("The base directory must not be null");
452        }
453    
454        if (baseDirectory.startsWith("~/") == false)
455        {
456          return new File(baseDirectory);
457        }
458        else
459        {
460          final String homeDirectory = System.getProperty("user.home");
461          if ("~/".equals(baseDirectory))
462          {
463            return new File(homeDirectory);
464          }
465          else
466          {
467            baseDirectory = baseDirectory.substring(2);
468            return new File(homeDirectory, baseDirectory);
469          }
470        }
471      }
472    }