/*! * 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.crosstab; import org.pentaho.reporting.designer.core.Messages; import org.pentaho.reporting.designer.core.ReportDesignerContext; import org.pentaho.reporting.designer.core.editor.ReportDataChangeListener; import org.pentaho.reporting.designer.core.editor.ReportDocumentContext; import org.pentaho.reporting.designer.core.util.IconLoader; import org.pentaho.reporting.engine.classic.core.CrosstabGroup; import org.pentaho.reporting.engine.classic.core.MetaAttributeNames; import org.pentaho.reporting.engine.classic.core.elementfactory.CrosstabBuilder; import org.pentaho.reporting.engine.classic.core.elementfactory.CrosstabDetail; import org.pentaho.reporting.engine.classic.core.elementfactory.CrosstabDimension; import org.pentaho.reporting.engine.classic.core.wizard.ContextAwareDataSchemaModel; import org.pentaho.reporting.engine.classic.core.wizard.DataAttributeContext; import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes; import org.pentaho.reporting.libraries.designtime.swing.BorderlessButton; import org.pentaho.reporting.libraries.designtime.swing.CommonDialog; import org.pentaho.reporting.libraries.designtime.swing.LibSwingUtil; import org.pentaho.reporting.libraries.designtime.swing.bulk.BulkDataProvider; import org.pentaho.reporting.libraries.designtime.swing.bulk.DefaultBulkListModel; import org.pentaho.reporting.libraries.designtime.swing.bulk.RemoveBulkAction; import org.pentaho.reporting.libraries.designtime.swing.bulk.SortBulkDownAction; import org.pentaho.reporting.libraries.designtime.swing.bulk.SortBulkUpAction; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import java.awt.*; import java.awt.event.ActionEvent; import java.util.ArrayList; public class CreateCrosstabDialog extends CommonDialog implements ReportDataChangeListener { private class AddListSelectionAction extends AbstractAction implements ListSelectionListener { private ListSelectionModel selectionModel; private DefaultListModel data; private ListModel fields; /** * Defines an <code>Action</code> object with a default description string and default icon. */ private AddListSelectionAction( final JList availableFields, final DefaultBulkListModel data ) { this.selectionModel = availableFields.getSelectionModel(); this.fields = availableFields.getModel(); this.data = data; putValue( Action.SMALL_ICON, IconLoader.getInstance().getFowardArrowIcon() ); putValue( Action.SHORT_DESCRIPTION, Messages.getString( "CreateCrosstabDialog.AddColumn" ) ); selectionModel.addListSelectionListener( this ); setEnabled( selectionModel.isSelectionEmpty() == false ); } public void valueChanged( final ListSelectionEvent e ) { setEnabled( selectionModel.isSelectionEmpty() == false ); } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { for ( int i = 0; i < fields.getSize(); i++ ) { if ( selectionModel.isSelectedIndex( i ) ) { data.addElement( fields.getElementAt( i ) ); } } } } private class AddDimensionAction extends AbstractAction implements ListSelectionListener { private ListSelectionModel selectionModel; private CrosstabDimensionTableModel data; private ListModel fields; /** * Defines an <code>Action</code> object with a default description string and default icon. */ private AddDimensionAction( final JList availableFields, final CrosstabDimensionTableModel data ) { this.selectionModel = availableFields.getSelectionModel(); this.fields = availableFields.getModel(); this.data = data; putValue( Action.SMALL_ICON, IconLoader.getInstance().getFowardArrowIcon() ); putValue( Action.SHORT_DESCRIPTION, Messages.getString( "CreateCrosstabDialog.AddColumn" ) ); selectionModel.addListSelectionListener( this ); setEnabled( selectionModel.isSelectionEmpty() == false ); } public void valueChanged( final ListSelectionEvent e ) { setEnabled( selectionModel.isSelectionEmpty() == false ); } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { for ( int i = 0; i < fields.getSize(); i++ ) { if ( selectionModel.isSelectedIndex( i ) ) { final String item = (String) fields.getElementAt( i ); data.add( new CrosstabDimension( item ) ); } } } } private class AddDetailsAction extends AbstractAction implements ListSelectionListener { private ListSelectionModel selectionModel; private CrosstabDetailTableModel data; private ListModel fields; /** * Defines an <code>Action</code> object with a default description string and default icon. */ private AddDetailsAction( final JList availableFields, final CrosstabDetailTableModel data ) { this.selectionModel = availableFields.getSelectionModel(); this.fields = availableFields.getModel(); this.data = data; putValue( Action.SMALL_ICON, IconLoader.getInstance().getFowardArrowIcon() ); putValue( Action.SHORT_DESCRIPTION, Messages.getString( "CreateCrosstabDialog.AddColumn" ) ); selectionModel.addListSelectionListener( this ); setEnabled( selectionModel.isSelectionEmpty() == false ); } public void valueChanged( final ListSelectionEvent e ) { setEnabled( selectionModel.isSelectionEmpty() == false ); } /** * Invoked when an action occurs. */ public void actionPerformed( final ActionEvent e ) { for ( int i = 0; i < fields.getSize(); i++ ) { if ( selectionModel.isSelectedIndex( i ) ) { final String item = (String) fields.getElementAt( i ); data.add( new CrosstabDetail( item ) ); } } } } private class MonitorMandatoryDimensionsHandler implements TableModelListener { public void tableChanged( final TableModelEvent e ) { validateInputs( false ); } } private DraggableJList otherFields; private DraggableCrosstabDimensionTable rowFields; private DraggableCrosstabDimensionTable columnFields; private JTable detailFields; private FieldListCellRenderer fieldListCellRenderer; private DraggableJList availableFields; private DefaultBulkListModel availableFieldsModel; private DefaultBulkListModel otherFieldsModel; private CrosstabDimensionTableModel rowsFieldsModel; private CrosstabDimensionTableModel columnsFieldsModel; private CrosstabDetailTableModel detailFieldsModel; private CrosstabOptionsPane optionsPane; public CreateCrosstabDialog() { init(); } public CreateCrosstabDialog( final Frame owner ) throws HeadlessException { super( owner ); init(); } public CreateCrosstabDialog( final Dialog owner ) throws HeadlessException { super( owner ); init(); } protected void init() { setTitle( Messages.getString( "CreateCrosstabDialog.Title" ) ); setModal( true ); optionsPane = new CrosstabOptionsPane(); fieldListCellRenderer = new FieldListCellRenderer(); availableFieldsModel = new DefaultBulkListModel(); availableFields = new DraggableJList( availableFieldsModel ); availableFields.setTransferHandler( new CrosstabDialogTransferHandler( availableFields, true ) ); availableFields.setCellRenderer( fieldListCellRenderer ); otherFieldsModel = new DefaultBulkListModel(); otherFields = new DraggableJList( otherFieldsModel ); otherFields.setVisibleRowCount( 3 ); otherFields.setCellRenderer( fieldListCellRenderer ); rowsFieldsModel = new CrosstabDimensionTableModel(); rowsFieldsModel.addTableModelListener( new MonitorMandatoryDimensionsHandler() ); rowFields = new DraggableCrosstabDimensionTable( rowsFieldsModel ); columnsFieldsModel = new CrosstabDimensionTableModel(); columnsFieldsModel.addTableModelListener( new MonitorMandatoryDimensionsHandler() ); columnFields = new DraggableCrosstabDimensionTable( columnsFieldsModel ); detailFieldsModel = new CrosstabDetailTableModel(); detailFields = new DraggableCrosstabDetailTable( detailFieldsModel ); super.init(); } protected void performInitialResize() { super.performInitialResize(); if ( getHeight() > 800 ) { setBounds( getX(), getY(), getWidth(), 800 ); } LibSwingUtil.centerDialogInParent( this ); } protected String getDialogId() { return "ReportDesigner.Core.CreateCrosstab"; // NON-NLS } protected Component createContentPane() { final JTabbedPane pane = new JTabbedPane(); pane.addTab( Messages.getString( "CreateCrosstabDialog.Fields" ), createSelectionPane() ); pane.addTab( Messages.getString( "CreateCrosstabDialog.Options" ), optionsPane ); final JPanel contentPane = new JPanel(); contentPane.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) ); contentPane.setLayout( new BorderLayout() ); contentPane.add( createTitlePanel(), BorderLayout.NORTH ); contentPane.add( pane, BorderLayout.CENTER ); return contentPane; } private JPanel createTitlePanel() { final JPanel titlePanel = new JPanel( new FlowLayout( FlowLayout.LEFT, 5, 5 ) ); titlePanel.setBorder( BorderFactory.createEmptyBorder( 0, 0, 5, 0 ) ); titlePanel.add( new JLabel( Messages.getString( "CreateCrosstabDialog.TitleLabel" ) ) ); return titlePanel; } private JComponent createSelectionPane() { final JPanel sidePane = new JPanel(); sidePane.setLayout( new BorderLayout() ); final JLabel tablesColumnsLabel = new JLabel( Messages.getString( "CreateCrosstabDialog.AvailableFields" ) ); sidePane.add( tablesColumnsLabel, BorderLayout.NORTH ); final JScrollPane comp = new JScrollPane ( availableFields, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); sidePane.add( comp, BorderLayout.CENTER ); final JPanel tablesPane = new JPanel(); tablesPane.setLayout( new GridBagLayout() ); addList( tablesPane, 0, otherFields, "CreateCrosstabDialog.OtherFields" ); addTable( tablesPane, 1, rowFields, "CreateCrosstabDialog.RowFields", new AddDimensionAction( availableFields, rowsFieldsModel ) ); addTable( tablesPane, 2, columnFields, "CreateCrosstabDialog.ColumnsFields", new AddDimensionAction( availableFields, columnsFieldsModel ) ); addTable( tablesPane, 3, detailFields, "CreateCrosstabDialog.Details", new AddDetailsAction( availableFields, detailFieldsModel ) ); final JSplitPane splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, sidePane, tablesPane ); splitPane.setBorder( null ); return splitPane; } private void addTable( final JComponent tablesPane, final int index, final JTable list, final String labelText, final Action addAction ) { final BulkDataProvider bulkListModel = (BulkDataProvider) list.getModel(); final JButton otherAdd = new BorderlessButton( addAction ); final JLabel otherLabel = new JLabel( Messages.getString( labelText ) ); final ListSelectionModel otherSelectionModel = list.getSelectionModel(); final JButton otherSortUp = new BorderlessButton( new SortBulkUpAction( bulkListModel, otherSelectionModel ) ); final JButton otherSortDown = new BorderlessButton( new SortBulkDownAction( bulkListModel, otherSelectionModel ) ); final JButton otherRemove = new BorderlessButton( new RemoveBulkAction( bulkListModel, otherSelectionModel ) ); GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.WEST; gbc.gridx = 2; gbc.gridy = index * 2; gbc.weightx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherLabel, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = index * 2; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherSortUp, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = index * 2; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherSortDown, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 5; gbc.gridy = index * 2; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherRemove, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 1 + index * 2; gbc.weighty = 1; gbc.fill = GridBagConstraints.BOTH; gbc.gridwidth = 4; gbc.insets = new Insets( 0, 5, 5, 0 ); tablesPane.add( new JScrollPane ( list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ), gbc ); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1 + index * 2; gbc.insets = new Insets( 5, 5, 5, 0 ); tablesPane.add( otherAdd, gbc ); } private void addList( final JComponent tablesPane, final int index, final JList list, final String labelText ) { final DefaultBulkListModel bulkListModel = (DefaultBulkListModel) list.getModel(); final JButton otherAdd = new BorderlessButton( new AddListSelectionAction( availableFields, bulkListModel ) ); final JLabel otherLabel = new JLabel( Messages.getString( labelText ) ); final ListSelectionModel otherSelectionModel = list.getSelectionModel(); final JButton otherSortUp = new BorderlessButton( new SortBulkUpAction( bulkListModel, otherSelectionModel ) ); final JButton otherSortDown = new BorderlessButton( new SortBulkDownAction( bulkListModel, otherSelectionModel ) ); final JButton otherRemove = new BorderlessButton( new RemoveBulkAction( bulkListModel, otherSelectionModel ) ); GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.WEST; gbc.gridx = 2; gbc.gridy = index * 2; gbc.weightx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherLabel, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = index * 2; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherSortUp, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = index * 2; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherSortDown, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 5; gbc.gridy = index * 2; gbc.insets = new Insets( 5, 5, 5, 5 ); tablesPane.add( otherRemove, gbc ); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 1 + index * 2; gbc.weighty = 1; gbc.fill = GridBagConstraints.BOTH; gbc.gridwidth = 4; gbc.insets = new Insets( 0, 5, 5, 0 ); tablesPane.add( new JScrollPane ( list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ), gbc ); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1 + index * 2; gbc.insets = new Insets( 5, 5, 5, 0 ); tablesPane.add( otherAdd, gbc ); } protected DefaultBulkListModel getAvailableFieldsModel() { return availableFieldsModel; } public void dataModelChanged( final ReportDocumentContext context ) { final ContextAwareDataSchemaModel dataSchemaModel = context.getReportDataSchemaModel(); final String[] columnNames = filterDatabaseColumn( dataSchemaModel ); final DefaultBulkListModel availableFieldsModel = getAvailableFieldsModel(); availableFieldsModel.setBulkData( columnNames ); this.fieldListCellRenderer.setModel( context.getReportDataSchemaModel() ); } private String[] filterDatabaseColumn( final ContextAwareDataSchemaModel dataSchemaModel ) { final ArrayList<String> fields = new ArrayList<String>(); final String[] columnNames = dataSchemaModel.getColumnNames(); final DataAttributeContext dac = dataSchemaModel.getDataAttributeContext(); for ( final String columnName : columnNames ) { final DataAttributes attributes = dataSchemaModel.getDataSchema().getAttributes( columnName ); final Object sourceAttribute = attributes.getMetaAttribute ( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.SOURCE, String.class, dac ); if ( MetaAttributeNames.Core.SOURCE_VALUE_ENVIRONMENT.equals( sourceAttribute ) || MetaAttributeNames.Core.SOURCE_VALUE_PARAMETER.equals( sourceAttribute ) ) { continue; } fields.add( columnName ); } return fields.toArray( new String[ fields.size() ] ); } public CrosstabGroup createCrosstab( final ReportDesignerContext designerContext, final CrosstabGroup editedGroup ) { if ( designerContext == null ) { throw new NullPointerException(); } final ReportDocumentContext reportRenderContext = designerContext.getActiveContext(); if ( reportRenderContext == null ) { throw new IllegalArgumentException(); } this.optionsPane.setReportDesignerContext( designerContext ); try { final CrosstabBuilder originBuilder; if ( editedGroup != null ) { originBuilder = CrosstabEditSupport.populateBuilder( editedGroup, reportRenderContext.getReportDataSchemaModel() ); populateDialogFromBuilder( originBuilder ); optionsPane.setValuesFromGroup( editedGroup ); } else { originBuilder = new CrosstabBuilder( reportRenderContext.getReportDataSchemaModel() ); } reportRenderContext.addReportDataChangeListener( this ); dataModelChanged( reportRenderContext ); validateInputs( false ); if ( performEdit() == false ) { return null; } if ( columnsFieldsModel.size() < 1 ) { return null; } if ( rowsFieldsModel.size() < 1 ) { return null; } final CrosstabBuilder builder = originBuilder.clearDimensions(); configureBuilderFromOptions( builder ); for ( int i = 0; i < detailFieldsModel.size(); i += 1 ) { final CrosstabDetail crosstabDetail = detailFieldsModel.get( i ); builder.addDetails( crosstabDetail.getField(), crosstabDetail.getAggregation() ); } for ( int col = 0; col < columnsFieldsModel.size(); col += 1 ) { final CrosstabDimension column = columnsFieldsModel.get( col ); builder.addColumnDimension( column ); } for ( int row = 0; row < rowsFieldsModel.size(); row += 1 ) { final CrosstabDimension rowDimension = rowsFieldsModel.get( row ); builder.addRowDimension( rowDimension ); } for ( int other = 0; other < otherFieldsModel.size(); other += 1 ) { final String column = (String) otherFieldsModel.get( other ); builder.addOtherDimension( column ); } final CrosstabGroup crosstabGroup = builder.create(); optionsPane.setValuesOnGroup( crosstabGroup ); return crosstabGroup; } finally { reportRenderContext.removeReportDataChangeListener( this ); this.fieldListCellRenderer.setModel( null ); this.optionsPane.setReportDesignerContext( null ); } } protected boolean validateInputs( final boolean onConfirm ) { boolean retval = true; if ( columnsFieldsModel.size() < 1 ) { retval = false; } if ( rowsFieldsModel.size() < 1 ) { retval = false; } getConfirmAction().setEnabled( retval ); return retval; } private void configureBuilderFromOptions( final CrosstabBuilder builder ) { builder.setGroupNamePrefix( "Group " ); // NON-NLS optionsPane.configureCrosstabBuilder( builder ); } private void populateDialogFromBuilder( final CrosstabBuilder builder ) { optionsPane.configureFromCrosstabBuilder( builder ); for ( final String other : builder.getOthers() ) { //noinspection unchecked otherFieldsModel.addElement( other ); } for ( final CrosstabDimension d : builder.getRows() ) { rowsFieldsModel.add( d ); } for ( final CrosstabDimension d : builder.getColumns() ) { columnsFieldsModel.add( d ); } for ( final CrosstabDetail detail : builder.getDetails() ) { detailFieldsModel.add( detail ); } } }