/*!
* 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.engine.classic.wizard.ui.xul.steps;
import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeUtil;
import org.pentaho.reporting.engine.classic.wizard.WizardProcessorUtil;
import org.pentaho.reporting.engine.classic.wizard.model.DefaultWizardSpecification;
import org.pentaho.reporting.engine.classic.wizard.model.WizardSpecification;
import org.pentaho.reporting.engine.classic.wizard.ui.xul.WizardEditorModel;
import org.pentaho.reporting.engine.classic.wizard.ui.xul.components.AbstractWizardStep;
import org.pentaho.reporting.libraries.base.util.DebugLog;
import org.pentaho.reporting.libraries.base.util.FilesystemFilter;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.docbundle.BundleUtilities;
import org.pentaho.reporting.libraries.docbundle.DocumentBundle;
import org.pentaho.reporting.libraries.docbundle.MemoryDocumentBundle;
import org.pentaho.reporting.libraries.docbundle.ODFMetaAttributeNames;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.ui.xul.XulDomContainer;
import org.pentaho.ui.xul.XulException;
import org.pentaho.ui.xul.binding.Binding;
import org.pentaho.ui.xul.binding.BindingConvertor;
import org.pentaho.ui.xul.components.XulFileDialog;
import org.pentaho.ui.xul.components.XulRadio;
import org.pentaho.ui.xul.components.XulTextbox;
import org.pentaho.ui.xul.containers.XulListbox;
import org.pentaho.ui.xul.impl.AbstractXulEventHandler;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LookAndFeelStep extends AbstractWizardStep {
private static class BooleanToIntegerBindingConverter extends BindingConvertor<Boolean, Integer> {
@Override
public Integer sourceToTarget( final Boolean value ) {
return value ? 1 : 0;
}
@Override
public Boolean targetToSource( final Integer value ) {
if ( value == null ) {
return null;
}
if ( 1 == value ) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
}
/**
* @author wseyler
*/
private class SelectedTemplateToImageConverter extends BindingConvertor<Integer, String> {
private SelectedTemplateToImageConverter() {
}/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object)
*/
@Override
public String sourceToTarget( final Integer value ) {
if ( value != -1 ) {
final File srcPath = templateHash.get( value );
final File homeDir = computeInstallationDirectory();
if ( homeDir == null ) {
return null;
}
final File templatesDir = new File( homeDir, "templates" );
if ( templatesDir.exists() ) {
final String simpleName = IOUtils.getInstance().stripFileExtension( srcPath.getName() );
final File file = new File( templatesDir, simpleName + PAINTING_EXTENSION );
try {
return file.getCanonicalPath();
} catch ( IOException e ) {
return file.getAbsolutePath();
}
}
}
return null;
}
/* (non-Javadoc)
* @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object)
*/
@Override
public Integer targetToSource( final String value ) {
return null;
}
}
protected class SelectTemplateStepHandler extends AbstractXulEventHandler {
private File file;
public String getName() {
return HANDLER_NAME;
}
public void selectFile() {
try {
final XulFileDialog fd = (XulFileDialog) document.createElement( "filedialog" ); //$NON-NLS-1$
fd.setModalParent( LookAndFeelStep.this.getDesignTimeContext().getParentWindow() );
fd.showOpenDialog();
file = (File) fd.getFile();
if ( file != null ) { // If the file is null then the user hit cancel
final String filePath = file.getAbsolutePath();
final XulTextbox fileTextBox = (XulTextbox) document.getElementById( WIZARD_FILENAME_TB_ID );
fileTextBox.setValue( filePath );
LookAndFeelStep.this.setFileName( filePath );
}
} catch ( XulException e ) {
getDesignTimeContext().error( e );
}
}
public File getFile() {
return file;
}
}
private static final String LOOK_AND_FEEL_STEP_OVERLAY =
"org/pentaho/reporting/engine/classic/wizard/ui/xul/res/look_and_feel_step_Overlay.xul"; //$NON-NLS-1$
private static final String HANDLER_NAME = "look_and_feel_handler"; //$NON-NLS-1$
private static final String SELECTED_PROPERTY_NAME = "selected"; //$NON-NLS-1$
private static final String SELECTED_INDEX_PROPERTY_NAME = "selectedIndex"; //$NON-NLS-1$
private static final String START_FROM_FILE_PROPERTY_NAME = "startFromFile"; //$NON-NLS-1$
private static final String NOT_SELECTED_PROPERTY_NAME = "!selected"; //$NON-NLS-1$
private static final String FILENAME_PROPERTY_NAME = "fileName"; //$NON-NLS-1$
private static final String VALUE_PROPERTY_NAME = "value"; //$NON-NLS-1$
private static final String ELEMENTS_PROPERTY_NAME = "elements"; //$NON-NLS-1$
private static final String SELECTED_TEMPLATE_PROPERTY_NAME = "selectedTemplate"; //$NON-NLS-1$
private static final String TEMPLATES_PROPERTY_NAME = "templates"; //$NON-NLS-1$
private static final String SOURCE_PROPERTY_NAME = "src"; //$NON-NLS-1$
private static final String NEW_WIZARD_FILE_RB_ID = "new_wizard_file_rb"; //$NON-NLS-1$
private static final String SELECT_LOOK_AND_FEEL_DECK_ID = "select_lf_deck"; //$NON-NLS-1$
private static final String WIZARD_FILENAME_TB_ID = "wizard_filename_tb"; //$NON-NLS-1$
private static final String AVAILABLE_TEMPLATES_LB_ID = "avail_template_lb"; //$NON-NLS-1$
private static final String TEMPLATE_IMAGE_ID = "template_image"; //$NON-NLS-1$
private static final String REPORT_EXTENSION = ".prpt"; //$NON-NLS-1$
private static final String PAINTING_EXTENSION = ".png"; //$NON-NLS-1$
private String fileName;
private boolean startFromFile;
private List<String> templates = new ArrayList<String>();
private Map<Integer, File> templateHash = new HashMap<Integer, File>();
private Integer selectedTemplate = -1;
private boolean selectedTemplateChanged = false;
private ResourceManager resourceManager;
public LookAndFeelStep() {
super();
this.resourceManager = new ResourceManager();
loadTemplates();
}
/**
*
*/
private void loadTemplates() {
final File homeDir = computeInstallationDirectory();
if ( homeDir == null ) {
return;
}
final File templatesDir = new File( homeDir, "templates" );
if ( templatesDir.exists() ) {
final File[] reportFiles =
templatesDir.listFiles( new FilesystemFilter( REPORT_EXTENSION, REPORT_EXTENSION, false ) );
final List<String> templateNameList = new ArrayList<String>();
for ( final File file : reportFiles ) {
final String reportName = computeReportName( file );
if ( reportName == null ) {
continue;
}
templateHash.put( templateNameList.size(), file );
templateNameList.add( reportName );
}
setTemplates( templateNameList );
if ( templateNameList.size() > 0 ) {
final boolean oldSelectedTemplateChanged = this.selectedTemplateChanged;
if ( selectedTemplate < 0 ) {
setSelectedTemplate( 0 ); //Select the first template by default.
}
this.selectedTemplateChanged = oldSelectedTemplateChanged;
}
}
}
private String computeReportName( final File reportFile ) {
try {
final ResourceKey resourceKey = resourceManager.createKey( reportFile );
final String reportName = computeNameFromMetadata( resourceManager, resourceKey );
if ( StringUtils.isEmpty( reportName ) ) {
return computeNameFromReport( resourceManager, resourceKey );
}
return reportName;
} catch ( ResourceException re ) {
return null;
}
}
private String computeNameFromMetadata( final ResourceManager resourceManager, final ResourceKey key ) {
try {
final Resource res = resourceManager.create( key, null, new Class[] { DocumentBundle.class } );
final DocumentBundle rawResource = (DocumentBundle) res.getResource();
final Object possibleTitle = rawResource.getMetaData().getBundleAttribute
( ODFMetaAttributeNames.DublinCore.NAMESPACE, ODFMetaAttributeNames.DublinCore.TITLE );
if ( possibleTitle != null ) {
return possibleTitle.toString();
}
return null;
} catch ( ResourceException re ) {
return null;
}
}
private String computeNameFromReport( final ResourceManager resourceManager, final ResourceKey key ) {
try {
final Resource res = resourceManager.create( key, null, new Class[] { MasterReport.class } );
final MasterReport rawResource = (MasterReport) res.getResource();
final Object possibleTitle = rawResource.getName();
if ( possibleTitle != null ) {
return possibleTitle.toString();
}
return null;
} catch ( ResourceException re ) {
return null;
}
}
public static File computeInstallationDirectory() {
final URL location = LookAndFeelStep.class.getProtectionDomain().getCodeSource().getLocation();
DebugLog.log( "InstallationDirectory: Protection-Domain: " + location );
if ( location == null ) {
return null;
}
if ( "file".equals( location.getProtocol() ) == false ) {
DebugLog.log( "InstallationDirectory: Protection-Domain: Protocol failure." );
return null;
}
try {
File jarPositon = new File( location.getFile() );
if ( jarPositon.isFile() == false ) {
final String file = URLDecoder.decode( location.getFile(),
ClassicEngineBoot.getInstance().getGlobalConfig().getConfigProperty( "file.encoding", "ISO-8859-1" ) );
jarPositon = new File( file );
}
DebugLog.log( "InstallationDirectory: JAR file: " + jarPositon );
if ( jarPositon.isFile() ) {
// secret knowledge here: We know all jars are in the lib-directory.
final File libDirectory = jarPositon.getCanonicalFile().getParentFile();
if ( libDirectory == null ) {
DebugLog.log( "InstallationDirectory: No lib directory." );
return null;
}
DebugLog.log( "InstallationDirectory: Work directory: " + libDirectory.getParentFile() );
return libDirectory.getParentFile();
}
} catch ( IOException ioe ) {
// ignore, but log.
DebugLog.log( "InstallationDirectory: Failed to decode URL: ", ioe );
}
// a directory, so we are running in an IDE.
// hope for the best by using the current working directory.
DebugLog.log( "InstallationDirectory: Work directory: Defaulting to current work directory." );
return new File( "." );
}
/* (non-Javadoc)
* @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#initialize()
*/
public void setBindings() {
// update the gui based on the selection
getBindingFactory().setBindingType( Binding.Type.ONE_WAY );
final Binding typeBinding = getBindingFactory()
.createBinding( NEW_WIZARD_FILE_RB_ID, SELECTED_PROPERTY_NAME, SELECT_LOOK_AND_FEEL_DECK_ID,
SELECTED_INDEX_PROPERTY_NAME, new BooleanToIntegerBindingConverter() );
final Binding templatesBinding = getBindingFactory()
.createBinding( this, TEMPLATES_PROPERTY_NAME, AVAILABLE_TEMPLATES_LB_ID, ELEMENTS_PROPERTY_NAME );
getBindingFactory().createBinding( this, SELECTED_TEMPLATE_PROPERTY_NAME, TEMPLATE_IMAGE_ID, SOURCE_PROPERTY_NAME,
new SelectedTemplateToImageConverter() );
// update both the gui and the model
getBindingFactory().setBindingType( Binding.Type.BI_DIRECTIONAL );
final Binding selectedTemplateBinding = getBindingFactory()
.createBinding( AVAILABLE_TEMPLATES_LB_ID, SELECTED_INDEX_PROPERTY_NAME, this, SELECTED_TEMPLATE_PROPERTY_NAME );
getBindingFactory()
.createBinding( this, START_FROM_FILE_PROPERTY_NAME, NEW_WIZARD_FILE_RB_ID, NOT_SELECTED_PROPERTY_NAME );
getBindingFactory().createBinding( this, FILENAME_PROPERTY_NAME, WIZARD_FILENAME_TB_ID, VALUE_PROPERTY_NAME );
try {
typeBinding.fireSourceChanged();
templatesBinding.fireSourceChanged();
selectedTemplateBinding.fireSourceChanged();
} catch ( Exception e ) {
if ( getDesignTimeContext() != null ) {
getDesignTimeContext().error( e );
}
}
}
public boolean stepDeactivating() {
super.stepDeactivating();
final WizardEditorModel editorModel = getEditorModel();
try {
if ( startFromFile && fileName != null ) {
final AbstractReportDefinition substituteReportDefinition = loadDefinitionFromFile( new File( fileName ) );
if ( substituteReportDefinition == null ) {
return false;
}
substituteReportDefinition.setName( editorModel.getReportDefinition().getName() );
substituteReportDefinition
.setAttribute( "http://reporting.pentaho.org/namespaces/engine/attributes/wizard", "template", fileName );
editorModel.setReportDefinition( substituteReportDefinition, false );
return true;
}
if ( editorModel.isEditing() ) {
if ( selectedTemplateChanged ) {
final AbstractReportDefinition report;
if ( selectedTemplate == -1 ) {
report = editorModel.getEmptyTemplate();
} else {
final File file = templateHash.get( selectedTemplate );
if ( file != null ) {
fileName = file.getAbsolutePath();
report = loadDefinitionFromFile( file );
if ( report == null ) {
return false;
}
} else {
return false;
}
}
final WizardSpecification specification = editorModel.getReportSpec();
final DataFactory dataFactory = editorModel.getReportDefinition().getDataFactory().derive();
final String oldName = editorModel.getReportDefinition().getName();
report
.setAttribute( "http://reporting.pentaho.org/namespaces/engine/attributes/wizard", "template", fileName );
report.setDataFactory( dataFactory );
report.setName( oldName );
WizardProcessorUtil.applyWizardSpec( report, specification );
editorModel.setReportDefinition( report, true );
return true;
}
return true;
}
if ( !startFromFile && selectedTemplate == -1 ) {
final AbstractReportDefinition report = editorModel.getEmptyTemplate();
final DefaultWizardSpecification specification = new DefaultWizardSpecification();
WizardProcessorUtil.applyWizardSpec( report, specification );
editorModel.setReportDefinition( report, false );
return true;
}
if ( selectedTemplate != -1 ) {
final File reportFile = templateHash.get( selectedTemplate );
if ( reportFile != null ) {
this.fileName = reportFile.getAbsolutePath();
} else {
this.fileName = null;
}
}
if ( fileName == null || fileName.length() < 1 ) {
return false;
}
final AbstractReportDefinition fileReportDefinition = loadDefinitionFromFile( new File( fileName ) );
if ( fileReportDefinition == null ) {
return false;
}
if ( fileReportDefinition instanceof MasterReport ) {
DesignTimeUtil.resetTemplate( (MasterReport) fileReportDefinition );
}
fileReportDefinition
.setAttribute( "http://reporting.pentaho.org/namespaces/engine/attributes/wizard", "template", fileName );
editorModel.setReportDefinition( fileReportDefinition );
return true;
} catch ( Exception e ) {
getDesignTimeContext().error( e );
return false;
}
}
private AbstractReportDefinition loadDefinitionFromFile( final File filename ) {
try {
final ResourceKey selectedFile = resourceManager.createKey( filename );
final Resource directly = resourceManager.create( selectedFile, null, new Class[] { MasterReport.class } );
final MasterReport resource = (MasterReport) directly.getResource();
final DocumentBundle bundle = resource.getBundle();
if ( bundle == null ) {
// Ok, that should not happen if we work with the engine's parsers, but better safe than sorry.
final MemoryDocumentBundle documentBundle = new MemoryDocumentBundle( resource.getContentBase() );
documentBundle.getWriteableDocumentMetaData().setBundleType( ClassicEngineBoot.BUNDLE_TYPE );
resource.setBundle( documentBundle );
resource.setContentBase( documentBundle.getBundleMainKey() );
} else {
final MemoryDocumentBundle mem = new MemoryDocumentBundle( resource.getContentBase() );
BundleUtilities.copyStickyInto( mem, bundle );
BundleUtilities.copyMetaData( mem, bundle );
resource.setBundle( mem );
resource.setContentBase( mem.getBundleMainKey() );
}
return (AbstractReportDefinition) resource.derive();
} catch ( Exception ex ) {
getDesignTimeContext().error( ex );
return null;
}
}
public void stepActivating() {
super.stepActivating();
selectedTemplateChanged = false;
if ( getEditorModel().isEditing() == false ) {
if ( ( (XulRadio) getDocument().getElementById( NEW_WIZARD_FILE_RB_ID ) ).isSelected() &&
( (XulListbox) getDocument().getElementById( AVAILABLE_TEMPLATES_LB_ID ) ).getSelectedIndex() == -1 ) {
( (XulListbox) getDocument().getElementById( AVAILABLE_TEMPLATES_LB_ID ) ).setSelectedIndex( 0 );
}
}
setValid( validateStep() );
}
private boolean validateStep() {
if ( getEditorModel().isEditing() ) {
return true;
}
if ( startFromFile ) {
if ( fileName == null || fileName.length() < 1 ) {
return false;
}
} else { // This is where we check for a selected template
return true;
}
return true;
}
public String getFileName() {
return fileName;
}
public void setFileName( final String fileName ) {
final String oldFileName = this.fileName;
this.fileName = fileName;
if ( ObjectUtilities.equal( oldFileName, fileName ) == false ) {
setValid( validateStep() );
}
}
public boolean isStartFromFile() {
return startFromFile;
}
public void setStartFromFile( final boolean startFromFile ) {
final boolean oldStartFromFile = this.startFromFile;
this.startFromFile = startFromFile;
if ( oldStartFromFile != startFromFile ) {
setValid( validateStep() );
}
}
/* (non-Javadoc)
* @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#createPresentationComponent(org
* .pentaho.ui.xul.XulDomContainer)
*/
public void createPresentationComponent( final XulDomContainer mainWizardContainer ) throws XulException {
super.createPresentationComponent( mainWizardContainer );
// Add the overlay
mainWizardContainer.loadOverlay( LOOK_AND_FEEL_STEP_OVERLAY );
// Add event handlers
mainWizardContainer.addEventHandler( new SelectTemplateStepHandler() );
}
/* (non-Javadoc)
* @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#getStepName()
*/
public String getStepName() {
return messages.getString( "LOOK_AND_FEEL_STEP.Step_Name" ); //$NON-NLS-1$
}
public List<String> getTemplates() {
return templates;
}
public void setTemplates( final List<String> templates ) {
final List<String> oldTemplates = this.templates;
this.templates = templates;
this.firePropertyChange( TEMPLATES_PROPERTY_NAME, oldTemplates, this.templates );
}
private Integer getIndexForTemplatePath( String absolutePath ) {
if ( absolutePath == null || absolutePath.length() < 1 ) {
return -1;
}
for ( int i = 0; i < templateHash.size(); i++ ) {
File file = templateHash.get( i );
if ( file.getAbsolutePath().equals( absolutePath ) ) {
return i;
}
}
return -1;
}
public void setSelectedTemplateByPath( String path ) {
setSelectedTemplate( getIndexForTemplatePath( path ) );
}
public Integer getSelectedTemplate() {
return selectedTemplate;
}
public void setSelectedTemplate( final Integer selectedTemplate ) {
final Integer oldSelectedTemplate = this.selectedTemplate;
this.selectedTemplate = selectedTemplate;
this.selectedTemplateChanged = true;
this.firePropertyChange( SELECTED_TEMPLATE_PROPERTY_NAME, oldSelectedTemplate, this.selectedTemplate );
}
}