/*!
* 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.styles.styleeditor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.designer.core.editor.styles.Messages;
import org.pentaho.reporting.designer.core.status.ExceptionDialog;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.writer.StyleDefinitionWriter;
import org.pentaho.reporting.engine.classic.core.style.css.ElementStyleDefinition;
import org.pentaho.reporting.libraries.base.util.FilesystemFilter;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.designtime.swing.LibSwingUtil;
import org.pentaho.reporting.libraries.designtime.swing.background.BackgroundCancellableProcessHelper;
import org.pentaho.reporting.libraries.designtime.swing.filechooser.CommonFileChooser;
import org.pentaho.reporting.libraries.designtime.swing.filechooser.FileChooserService;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.io.File;
import java.io.IOException;
public class StyleDefinitionUtilities {
private static final String DEFAULT_EXTENSION = ".prptstyle";
private static final String FILE_CHOOSER_TYPE = "style-definition";
private static final Log logger = LogFactory.getLog( StyleDefinitionUtilities.class );
private StyleDefinitionUtilities() {
}
/**
* Prompts the user for the name of the report file which should be created
*
* @param parent the parent component of which the file chooser dialog will be a child
* @param defaultFile the initially selected file.
* @return The <code>File</code> which the report should be saved into, or <code>null</code> if the user does not want
* to continue with the save operation
*/
public static File promptReportFilename( final Component parent, final File defaultFile ) {
final FileFilter filter = new FilesystemFilter
( new String[] { DEFAULT_EXTENSION },
Messages.getString( "StyleDefinitionUtilities.FileDescription" ), true );
final CommonFileChooser fileChooser = FileChooserService.getInstance().getFileChooser( FILE_CHOOSER_TYPE );
fileChooser.setSelectedFile( defaultFile );
fileChooser.setFilters( new FileFilter[] { filter } );
logger.debug( "Prompting for save filename" ); // NON-NLS
if ( fileChooser.showDialog( parent, JFileChooser.SAVE_DIALOG ) == false ) {
logger.debug( "Save filename - cancel option selected" );// NON-NLS
return null;
}
final File selectedFile = validateFileExtension( fileChooser.getSelectedFile(), parent );
if ( selectedFile == null ) {
// Cancel on another dialog
return null;
}
// Once the filename has stabelized, check for overwrite
if ( selectedFile.exists() ) {
logger.debug( "Selected file exists [" + selectedFile.getName() + "] - prompting for overwrite..." );// NON-NLS
final int overwrite = JOptionPane.showConfirmDialog( parent,
Messages.getString( "StyleDefinitionUtilities.OverwriteDialog.Message",
selectedFile.getAbsolutePath() ), Messages.getString( "StyleDefinitionUtilities.OverwriteDialog.Title" ),
JOptionPane.YES_NO_OPTION );
if ( overwrite == JOptionPane.NO_OPTION ) {
return null;
}
}
return selectedFile;
}
public static boolean saveStyleDefinitionAs( final StyleDefinitionEditorContext activeContext,
final Component parent ) {
// Get the current file target
final File defaultFile = activeContext.getSource();
// Prompt for the filename
final File target = promptReportFilename( parent, defaultFile );
if ( target == null ) {
return false;
}
activeContext.setSource( target );
// Save the report
if ( saveStyleDefinition( activeContext, target ) ) {
return true;
}
final ExceptionDialog exceptionDialog;
final Window window = LibSwingUtil.getWindowAncestor( parent );
if ( window instanceof Dialog ) {
exceptionDialog = new ExceptionDialog( (Dialog) window );
} else if ( window instanceof Frame ) {
exceptionDialog = new ExceptionDialog( (Frame) window );
} else {
exceptionDialog = new ExceptionDialog();
}
exceptionDialog.showDialog();
return false;
}
public static boolean saveStyleDefinition( final StyleDefinitionEditorContext activeContext,
final Component parent ) {
// Get the current file target
File target = activeContext.getSource();
// If there is no target, this file has not been save before ... prompt for a filename
if ( target == null ) {
target = promptReportFilename( parent, null );
} else {
target = validateFileExtension( target, parent );
}
if ( target == null ) {
return false;
}
// if no name has been set for the report, default to the name of the file
activeContext.setSource( target );
// Write the report to the filename
if ( saveStyleDefinition( activeContext, target ) ) {
return true;
}
ExceptionDialog.showDialog( parent );
return false;
}
/**
* Performs the action of saving the report to the specified target file
*
* @param activeContext the active render context
* @param target the target file to which the report will be saved
* @return true, if saving was successful, false otherwise.
*/
private static boolean saveStyleDefinition( final StyleDefinitionEditorContext activeContext,
final File target ) {
if ( activeContext == null ) {
throw new IllegalArgumentException();
}
if ( target == null ) {
throw new IllegalArgumentException();
}
try {
// Save the report to the specified file
logger.debug( "Saving report in filename [" + target.getAbsolutePath() + "]" );// NON-NLS
final StyleDefinitionWriter writer = new StyleDefinitionWriter();
writer.write( target, activeContext.getStyleDefinition() );
return true;
} catch ( Exception e1 ) {
UncaughtExceptionsModel.getInstance().addException
( new IOException( Messages.getString( "StyleDefinitionUtilities.SaveFailed.Message" ), e1 ) );// NON-NLS
logger.error( "Failed to save report", e1 );// NON-NLS
return false;
}
}
/**
* Validates that the extension of the filename is prpt, and prompts the user if it is not.
*
* @param proposedFile the target file to validate
* @param parent the parent component in case we need to display a dialog
* @return the filename based on the validation and optional prompting, or <code>null</code> if the user decided to
* cancel the operaion
*/
public static File validateFileExtension( final File proposedFile, final Component parent ) {
if ( proposedFile == null ) {
return null;
}
// See if we need to change the file extension
final String s = proposedFile.getName();
if ( s.endsWith( DEFAULT_EXTENSION ) ) {
return proposedFile;
}
final String extension = IOUtils.getInstance().getFileExtension( s );
if ( "".equals( extension ) ) {
final File parentFile = proposedFile.getParentFile();
if ( parentFile == null ) {
return new File( IOUtils.getInstance().stripFileExtension( s ) + DEFAULT_EXTENSION );
} else {
return new File( parentFile, IOUtils.getInstance().stripFileExtension( s ) + DEFAULT_EXTENSION );
}
}
logger.debug( "The selected filename does not have the standard extension - " +// NON-NLS
"prompting the user to see if they want to change the extension" );// NON-NLS
final int result = JOptionPane.showConfirmDialog( parent,
Messages.getString( "StyleDefinitionUtilities.VerifyFileExtension.Message",
proposedFile.getAbsolutePath() ), Messages.getString( "StyleDefinitionUtilities.VerifyFileExtension.Title" ),
JOptionPane.YES_NO_CANCEL_OPTION );
if ( result == JOptionPane.CANCEL_OPTION ) {
return null;
}
if ( result == JOptionPane.NO_OPTION ) {
return proposedFile;
}
final File validatedFile =
new File( proposedFile.getParent(), IOUtils.getInstance().stripFileExtension( s ) + DEFAULT_EXTENSION );
logger
.debug( "User has selected YES - the filename has been changed to [" + validatedFile.getName() + "]" );// NON-NLS
return validatedFile;
}
/**
* Invoked when an action occurs.
*/
public static void openStyleDefinition( final StyleDefinitionEditorContext context ) {
final FileFilter filter = new FilesystemFilter
( new String[] { DEFAULT_EXTENSION },
Messages.getString( "StyleDefinitionUtilities.FileDescription" ), true );
final CommonFileChooser fileChooser = FileChooserService.getInstance().getFileChooser( FILE_CHOOSER_TYPE );
fileChooser.setFilters( new FileFilter[] { filter } );
fileChooser.setAllowMultiSelection( true );
if ( fileChooser.showDialog( context.getParent(), JFileChooser.OPEN_DIALOG ) == false ) {
return;
}
final File[] selectedFiles = fileChooser.getSelectedFiles();
for ( int i = 0, selectedFilesLength = selectedFiles.length; i < selectedFilesLength; i++ ) {
final File selectedFile = selectedFiles[ i ];
SwingUtilities.invokeLater( new OpenReportTask( selectedFile, context ) );
}
}
public static class OpenReportTask implements Runnable {
private File selectedFile;
private StyleDefinitionEditorContext context;
public OpenReportTask( final File selectedFile, final StyleDefinitionEditorContext context ) {
this.selectedFile = selectedFile;
this.context = context;
}
public void run() {
openReport( selectedFile, context );
}
private static void openReport( final File selectedFile,
final StyleDefinitionEditorContext context ) {
if ( selectedFile == null ) {
throw new NullPointerException();
}
if ( context == null ) {
throw new NullPointerException();
}
final LoadStyleDefinitionTask target = new LoadStyleDefinitionTask( selectedFile );
final Thread loadThread = new Thread( target );
loadThread.setDaemon( true );
BackgroundCancellableProcessHelper.executeProcessWithCancelDialog
( loadThread, null, context.getParent(), Messages.getString( "StyleDefinitionUtilities.LoadMessage" ) );
final ElementStyleDefinition report = target.getStyleDefinition();
if ( report != null ) {
context.setStyleDefinition( report );
context.setSource( selectedFile );
} else {
final Exception exception = target.getException();
if ( exception instanceof ResourceCreationException ) {
UncaughtExceptionsModel.getInstance().addException( exception );
ExceptionDialog.showDialog( context.getParent() );
} else if ( exception != null ) {
UncaughtExceptionsModel.getInstance().addException( exception );
ExceptionDialog.showDialog( context.getParent() );
}
}
}
}
private static class LoadStyleDefinitionTask implements Runnable {
private File file;
private ElementStyleDefinition styleDefinition;
private Exception exception;
private LoadStyleDefinitionTask( final File file ) {
if ( file == null ) {
throw new NullPointerException();
}
this.file = file;
}
/**
* When an object implementing interface <code>Runnable</code> is used to create a thread, starting the thread
* causes the object's <code>run</code> method to be called in that separately executing thread.
* <p/>
* The general contract of the method <code>run</code> is that it may take any action whatsoever.
*
* @see Thread#run()
*/
public void run() {
try {
final ResourceManager mgr = new ResourceManager();
final Resource directly = mgr.createDirectly( file, ElementStyleDefinition.class );
styleDefinition = (ElementStyleDefinition) directly.getResource();
} catch ( Exception e ) {
this.exception = e;
}
}
public ElementStyleDefinition getStyleDefinition() {
return styleDefinition;
}
public Exception getException() {
return exception;
}
}
}