/*
* 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 - 2016 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.gui.base;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.DefaultReportEnvironment;
import org.pentaho.reporting.engine.classic.core.DefaultResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ParameterDataRow;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ReportEnvironment;
import org.pentaho.reporting.engine.classic.core.ReportEnvironmentDataRow;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.TableDataFactory;
import org.pentaho.reporting.engine.classic.core.cache.CachingDataFactory;
import org.pentaho.reporting.engine.classic.core.designtime.datafactory.DesignTimeDataFactoryContext;
import org.pentaho.reporting.engine.classic.core.modules.gui.base.parameters.ButtonParameterComponent;
import org.pentaho.reporting.engine.classic.core.modules.gui.base.parameters.DefaultParameterComponentFactory;
import org.pentaho.reporting.engine.classic.core.modules.gui.base.parameters.ParameterComponent;
import org.pentaho.reporting.engine.classic.core.modules.gui.base.parameters.ParameterComponentFactory;
import org.pentaho.reporting.engine.classic.core.modules.gui.base.parameters.ParameterUpdateContext;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.ExceptionDialog;
import org.pentaho.reporting.engine.classic.core.parameters.CompoundDataRow;
import org.pentaho.reporting.engine.classic.core.parameters.DefaultParameterContext;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterAttributeNames;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterContext;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterDefinitionEntry;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterDefinition;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterValidator;
import org.pentaho.reporting.engine.classic.core.parameters.ValidationMessage;
import org.pentaho.reporting.engine.classic.core.parameters.ValidationResult;
import org.pentaho.reporting.engine.classic.core.states.PerformanceMonitorContext;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.Messages;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.docbundle.DocumentMetaData;
import org.pentaho.reporting.libraries.docbundle.MemoryDocumentMetaData;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
public class ParameterReportControllerPane extends JPanel {
private boolean isUpdating;
private static class InternalParameterContext implements ParameterContext {
private DataRow parameterData;
private DataFactory dataFactory;
private ResourceBundleFactory resourceBundleFactory;
private Configuration configuration;
private ResourceKey contentBase;
private ResourceManager resourceManager;
private boolean closed;
private DocumentMetaData documentMetaData;
private ReportEnvironment reportEnvironment;
private InternalParameterContext() {
this.resourceManager = new ResourceManager();
dataFactory = new TableDataFactory();
resourceBundleFactory = new DefaultResourceBundleFactory();
configuration = ClassicEngineBoot.getInstance().getGlobalConfig();
documentMetaData = new MemoryDocumentMetaData();
reportEnvironment = new DefaultReportEnvironment( configuration );
final ReportEnvironmentDataRow envDataRow = new ReportEnvironmentDataRow( reportEnvironment );
parameterData = new CompoundDataRow( envDataRow, new ParameterDataRow() );
}
public PerformanceMonitorContext getPerformanceMonitorContext() {
return ClassicEngineBoot.getInstance().getObjectFactory().get( PerformanceMonitorContext.class );
}
/**
* the document metadata of the report. Can be null, if the report does not have a bundle associated or if this
* context is not part of a report-processing.
*/
public DocumentMetaData getDocumentMetaData() {
return documentMetaData;
}
public ReportEnvironment getReportEnvironment() {
return reportEnvironment;
}
public ResourceKey getContentBase() {
return contentBase;
}
public DataRow getParameterData() {
return parameterData;
}
public DataFactory getDataFactory() {
return dataFactory;
}
public ResourceBundleFactory getResourceBundleFactory() {
return resourceBundleFactory;
}
public void close() throws ReportDataFactoryException {
closed = true;
dataFactory.close();
}
public Configuration getConfiguration() {
return configuration;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
public void update( final MasterReport report ) throws ReportProcessingException {
if ( closed == false ) {
close();
}
if ( report == null ) {
this.resourceManager = new ResourceManager();
dataFactory = new TableDataFactory();
resourceBundleFactory = new DefaultResourceBundleFactory();
configuration = ClassicEngineBoot.getInstance().getGlobalConfig();
contentBase = null;
documentMetaData = new MemoryDocumentMetaData();
reportEnvironment = new DefaultReportEnvironment( configuration );
final ReportEnvironmentDataRow envDataRow = new ReportEnvironmentDataRow( reportEnvironment );
parameterData = new CompoundDataRow( envDataRow, new ParameterDataRow() );
} else {
this.resourceManager = report.getResourceManager();
this.contentBase = report.getContentBase();
final Object dataCacheEnabledRaw =
report.getAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.DATA_CACHE );
final boolean dataCacheEnabled = Boolean.FALSE.equals( dataCacheEnabledRaw ) == false;
this.dataFactory = new CachingDataFactory( report.getDataFactory().derive(), dataCacheEnabled );
this.resourceBundleFactory =
MasterReport.computeAndInitResourceBundleFactory( report.getResourceBundleFactory(), report
.getReportEnvironment() );
this.reportEnvironment = report.getReportEnvironment();
this.configuration = report.getConfiguration();
final ReportEnvironmentDataRow envDataRow = new ReportEnvironmentDataRow( reportEnvironment );
this.parameterData = new CompoundDataRow( envDataRow, new ParameterDataRow( report.getParameterValues() ) );
dataFactory.initialize( new DesignTimeDataFactoryContext( report ) );
if ( report.getBundle() != null ) {
documentMetaData = report.getBundle().getMetaData();
} else {
documentMetaData = new MemoryDocumentMetaData();
}
}
}
public void update( final ReportParameterValues properties ) {
final ReportEnvironmentDataRow envDataRow = new ReportEnvironmentDataRow( reportEnvironment );
this.parameterData = new CompoundDataRow( envDataRow, new ParameterDataRow( properties ) );
}
}
private class UpdateAction extends AbstractAction {
/**
* Defines an <code>Action</code> object with a default description string and default icon.
*/
private UpdateAction() {
putValue( Action.NAME, messages.getString( "ParameterReportControllerPane.Update" ) );
}
/**
* Invoked when an action occurs.
*/
public void actionPerformed( final ActionEvent e ) {
validateParameter();
performUpdate();
}
}
private class InternalParameterUpdateHandler implements ParameterUpdateContext {
public void setParameterValue( final String name, final Object value ) {
setParameterValue( name, value, true );
}
public void setParameterValue( final String name, final Object value, final boolean autoUpdate ) {
updateParameterValue( name, value, autoUpdate );
}
public Object getParameterValue( final String name ) {
return reportParameterValues.get( name );
}
public void addChangeListener( final ChangeListener changeListener ) {
ParameterReportControllerPane.this.addInternalChangeListener( changeListener );
}
public void removeChangeListener( final ChangeListener changeListener ) {
ParameterReportControllerPane.this.removeInternalChangeListener( changeListener );
}
}
private static class ParameterCarrierPanel extends JPanel implements Scrollable {
/**
* Creates a new <code>JPanel</code> with a double buffer and a flow layout.
*/
private ParameterCarrierPanel() {
setLayout( new GridBagLayout() );
}
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
public int getScrollableUnitIncrement( final Rectangle visibleRect, final int orientation, final int direction ) {
return 10;
}
public int getScrollableBlockIncrement( final Rectangle visibleRect, final int orientation, final int direction ) {
return 20;
}
public boolean getScrollableTracksViewportWidth() {
return true;
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
public static final Color ERROR_COLOR = new Color( 251, 191, 191, 255 );
private MasterReport report;
private ReportParameterValues reportParameterValues;
private ReportParameterValidator validator;
private ArrayList<ChangeListener> changeListeners;
private ArrayList<ChangeListener> internalChangeListeners;
private JCheckBox autoUpdateCheckbox;
private JButton updateButton;
private ParameterCarrierPanel carrierPanel;
private InternalParameterContext parameterContext;
private HashMap<String, JLabel> errorLabels;
private JLabel globalErrorMessage;
private ParameterComponentFactory parameterEditorFactory;
private boolean inUpdate;
private ParameterUpdateContext updateContext;
private Messages messages;
private ArrayList<ParameterComponent> parameterComponents;
public ParameterReportControllerPane() {
messages =
new Messages( Locale.getDefault(), SwingPreviewModule.BUNDLE_NAME, ObjectUtilities
.getClassLoader( ParameterReportControllerPane.class ) );
changeListeners = new ArrayList<ChangeListener>();
internalChangeListeners = new ArrayList<ChangeListener>();
parameterComponents = new ArrayList<ParameterComponent>();
carrierPanel = new ParameterCarrierPanel();
parameterContext = new InternalParameterContext();
errorLabels = new HashMap<String, JLabel>();
globalErrorMessage = new JLabel();
autoUpdateCheckbox = new JCheckBox( messages.getString( "ParameterReportControllerPane.AutoUpdate" ) );
updateButton = new JButton( new UpdateAction() );
setLayout( new GridBagLayout() );
parameterEditorFactory = new DefaultParameterComponentFactory();
updateContext = new InternalParameterUpdateHandler();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.gridx = 0;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.WEST;
add( globalErrorMessage, gbc );
gbc = new GridBagConstraints();
gbc.gridy = 1;
gbc.gridx = 0;
gbc.gridwidth = 2;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.WEST;
final JScrollPane scrollPane = new JScrollPane( carrierPanel );
scrollPane.getViewport().setBackground( carrierPanel.getBackground() );
add( scrollPane, gbc );
final JPanel buttonPane = new JPanel();
buttonPane.setLayout( new GridLayout( 1, 1 ) );
buttonPane.add( updateButton );
gbc = new GridBagConstraints();
gbc.gridy = 2;
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.EAST;
add( buttonPane, gbc );
final JPanel cbPane = new JPanel();
cbPane.setLayout( new GridLayout( 1, 1 ) );
cbPane.add( autoUpdateCheckbox );
gbc = new GridBagConstraints();
gbc.gridy = 2;
gbc.gridx = 0;
gbc.anchor = GridBagConstraints.WEST;
add( cbPane, gbc );
}
public MasterReport getReport() {
return report;
}
public void setReport( final MasterReport report ) throws ReportProcessingException {
// final MasterReport oldReport = this.report;
this.report = report;
if ( !isUpdating ) {
reinit();
}
}
public void hideControls() {
autoUpdateCheckbox.setVisible( false );
updateButton.setVisible( false );
}
private void reinit() throws ReportProcessingException {
internalChangeListeners.clear();
carrierPanel.removeAll();
errorLabels.clear();
globalErrorMessage.setText( null );
parameterComponents.clear();
if ( report == null ) {
this.reportParameterValues = null;
this.parameterContext.update( (MasterReport) null );
this.validator = null;
this.autoUpdateCheckbox.setSelected( false );
return;
}
final Object autoUpdate =
report.getAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.AUTO_SUBMIT_PARAMETER );
final boolean showAutoSubmitCheckbox;
final boolean autoSubmitDefault;
if ( autoUpdate == null ) {
showAutoSubmitCheckbox = true;
autoSubmitDefault =
Boolean.FALSE.equals( report.getAttribute( AttributeNames.Core.NAMESPACE,
AttributeNames.Core.AUTO_SUBMIT_DEFAULT ) ) == false;
} else {
showAutoSubmitCheckbox = false;
autoSubmitDefault = Boolean.TRUE.equals( autoUpdate );
}
this.autoUpdateCheckbox.setVisible( showAutoSubmitCheckbox );
this.autoUpdateCheckbox.setSelected( autoSubmitDefault );
final ReportParameterDefinition parameterDefinition = report.getParameterDefinition();
if ( parameterDefinition == null ) {
this.reportParameterValues = null;
this.parameterContext.update( (MasterReport) null );
this.validator = null;
return;
}
try {
final ReportParameterDefinition parameters = report.getParameterDefinition();
final DefaultParameterContext parameterContext = new DefaultParameterContext( report );
try {
final ReportParameterValidator reportParameterValidator = parameters.getValidator();
final ValidationResult validationResult =
reportParameterValidator.validate( new ValidationResult(), parameters, parameterContext );
// first compute the default values ...
this.reportParameterValues = validationResult.getParameterValues();
} finally {
parameterContext.close();
}
} catch ( ReportDataFactoryException e ) {
e.printStackTrace();
// this may fail if the datasource is not there or the report is really messed up ..
this.reportParameterValues = new ReportParameterValues( report.getParameterValues() );
}
parameterComponents = new ArrayList<ParameterComponent>();
// we are using a very simple model here (for now).
parameterContext.update( report );
parameterContext.update( this.reportParameterValues );
validator = parameterDefinition.getValidator();
final ParameterDefinitionEntry[] entries = parameterDefinition.getParameterDefinitions();
for ( int i = 0; i < entries.length; i++ ) {
final ParameterDefinitionEntry entry = entries[i];
if ( "true".equals( entry.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.HIDDEN, parameterContext ) ) ) {
continue;
}
final ParameterComponent parameterComponent =
parameterEditorFactory.create( entry, parameterContext, updateContext );
addToPanel( entry, 1 + i * 2, parameterComponent.getUIComponent() );
parameterComponents.add( parameterComponent );
}
validateParameter();
}
private void addToPanel( final ParameterDefinitionEntry entry, final int gridY, final JComponent editor ) {
final JLabel label = new JLabel( computeLabel( entry ) );
final JLabel errorLabel = new JLabel();
errorLabels.put( entry.getName(), errorLabel );
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = gridY;
gbc.gridx = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets( 5, 5, 0, 5 );
carrierPanel.add( label, gbc );
if ( entry.isMandatory() ) {
gbc = new GridBagConstraints();
gbc.gridy = gridY;
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets( 5, 0, 0, 0 );
final JLabel mandatoryLabel = new JLabel( "*" );
mandatoryLabel.setToolTipText( messages.getString( "ParameterReportControllerPane.MandatoryParameter" ) );
carrierPanel.add( mandatoryLabel, gbc );
}
gbc = new GridBagConstraints();
gbc.gridy = gridY;
gbc.gridx = 2;
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 1;
if ( editor instanceof ButtonParameterComponent ) {
gbc.fill = GridBagConstraints.HORIZONTAL;
}
gbc.ipadx = 100;
gbc.insets = new Insets( 5, 0, 0, 0 );
carrierPanel.add( editor, gbc );
gbc = new GridBagConstraints();
gbc.gridy = gridY + 1;
gbc.gridx = 1;
gbc.anchor = GridBagConstraints.NORTH;
gbc.weightx = 1;
gbc.gridwidth = 2;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets( 5, 0, 0, 0 );
carrierPanel.add( errorLabel, gbc );
}
String computeLabel( final ParameterDefinitionEntry entry ) {
final String swingLabel =
entry.getParameterAttribute( ParameterAttributeNames.Swing.NAMESPACE, ParameterAttributeNames.Swing.LABEL,
parameterContext );
if ( swingLabel != null ) {
return swingLabel;
}
String coreLabel = entry.getTranslatedParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.LABEL, parameterContext );
return coreLabel == null ? entry.getName() : coreLabel;
}
public ReportParameterValues getReportParameterValues() {
return reportParameterValues;
}
public void addChangeListener( final ChangeListener changeListener ) {
if ( changeListener == null ) {
throw new NullPointerException();
}
changeListeners.add( changeListener );
}
public void removeChangeListener( final ChangeListener changeListener ) {
if ( changeListener == null ) {
throw new NullPointerException();
}
changeListeners.remove( changeListener );
}
protected void addInternalChangeListener( final ChangeListener changeListener ) {
if ( changeListener == null ) {
throw new NullPointerException();
}
internalChangeListeners.add( changeListener );
}
protected void removeInternalChangeListener( final ChangeListener changeListener ) {
if ( changeListener == null ) {
throw new NullPointerException();
}
internalChangeListeners.remove( changeListener );
}
protected void performUpdate() {
isUpdating = true;
try {
report.getParameterValues().clear();
report.getParameterValues().putAll( reportParameterValues );
setReport( report );
final ChangeEvent event = new ChangeEvent( this );
for ( int i = 0; i < changeListeners.size(); i++ ) {
final ChangeListener listener = changeListeners.get( i );
listener.stateChanged( event );
}
} catch ( ReportProcessingException e ) {
ExceptionDialog.showExceptionDialog( this, messages.getString( "ParameterReportControllerPane.Error" ), messages
.getString( "ParameterReportControllerPane.ErrorWhileConfiguringParameterUI", e.getMessage() ), e );
}
isUpdating = false;
}
protected void updateParameterValue( final String name, final Object value, final boolean autoUpdate ) {
reportParameterValues.put( name, value );
parameterContext.update( reportParameterValues );
if ( inUpdate || autoUpdate == false ) {
return;
}
if ( validateParameter() == false ) {
return;
}
if ( autoUpdateCheckbox.isSelected() ) {
performUpdate();
}
}
private boolean validateParameter() {
globalErrorMessage.setText( null );
for ( final Map.Entry<String, JLabel> stringJLabelEntry : errorLabels.entrySet() ) {
final JLabel o = stringJLabelEntry.getValue();
o.setText( null );
}
boolean retval = true;
if ( validator != null ) {
try {
final ValidationResult validationResult =
validator.validate( new ValidationResult(), report.getParameterDefinition(), parameterContext );
final ValidationMessage[] messages = validationResult.getErrors();
globalErrorMessage.setText( formatMessages( messages ) );
final String[] propertyNames = validationResult.getProperties();
for ( int i = 0; i < propertyNames.length; i++ ) {
final String propertyName = propertyNames[i];
final JLabel o = errorLabels.get( propertyName );
final ValidationMessage[] validationMessages = validationResult.getErrors( propertyName );
final String message = formatMessages( validationMessages );
if ( o == null ) {
final String s = globalErrorMessage.getText();
if ( StringUtils.isEmpty( s ) ) {
globalErrorMessage.setText( propertyName + ": " + message );
} else {
globalErrorMessage.setText( s + "\n" + propertyName + ": " + message );
}
} else {
o.setText( message );
}
}
// Set the updated and validated parameter values as new values.
final ReportParameterValues parameterValues = validationResult.getParameterValues();
for ( final String columnName : parameterValues.getColumnNames() ) {
final Object value = parameterValues.get( columnName );
if ( value != null ) {
reportParameterValues.put( columnName, value );
}
}
parameterContext.update( reportParameterValues );
for ( final ParameterComponent component : parameterComponents ) {
// reinit the components ..
component.initialize();
}
} catch ( Exception e ) {
// mark the report as invalid or so ..
ExceptionDialog.showExceptionDialog( this, messages.getString( "ParameterReportControllerPane.Error" ),
messages.getString( "ParameterReportControllerPane.ErrorWhileConfiguringParameterUI", e.getMessage() ), e );
retval = false;
}
}
try {
inUpdate = true;
final ChangeEvent event = new ChangeEvent( this );
for ( int i = 0; i < internalChangeListeners.size(); i++ ) {
final ChangeListener changeListener = internalChangeListeners.get( i );
changeListener.stateChanged( event );
}
} finally {
inUpdate = false;
}
return retval;
}
private String formatMessages( final ValidationMessage[] validationMessages ) {
final StringBuilder message = new StringBuilder( 1000 );
for ( int j = 0; j < validationMessages.length; j++ ) {
if ( j != 0 ) {
message.append( '\n' );
}
final ValidationMessage validationMessage = validationMessages[j];
message.append( validationMessage.getMessage() );
}
return message.toString();
}
public void setErrorMessage( final String error ) {
globalErrorMessage.setText( error );
}
}