/*!
* 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.drilldown;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.designer.core.Messages;
import org.pentaho.reporting.designer.core.editor.drilldown.model.DrillDownParameter;
import org.pentaho.reporting.designer.core.util.table.ElementMetaDataTableModel;
import org.pentaho.reporting.designer.core.util.table.GroupedName;
import org.pentaho.reporting.designer.core.util.table.GroupingHeader;
import org.pentaho.reporting.designer.core.util.table.GroupingModel;
import org.pentaho.reporting.designer.core.util.table.TableStyle;
import org.pentaho.reporting.engine.classic.core.metadata.AttributeMetaData;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import javax.swing.table.AbstractTableModel;
import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
public class DrillDownParameterTableModel extends AbstractTableModel
implements ElementMetaDataTableModel, GroupingModel {
private static class PlainParameterComparator implements Comparator {
public int compare( final Object o1, final Object o2 ) {
final DrillDownParameter parameter1 = (DrillDownParameter) o1;
final DrillDownParameter parameter2 = (DrillDownParameter) o2;
if ( parameter1 == null && parameter2 == null ) {
return 0;
}
if ( parameter1 == null ) {
return -1;
}
if ( parameter2 == null ) {
return 1;
}
if ( parameter1.getPosition() < parameter2.getPosition() ) {
return -1;
}
if ( parameter1.getPosition() > parameter2.getPosition() ) {
return 1;
}
return parameter1.getName().compareTo( parameter2.getName() );
}
}
private static class GroupedParameterComparator implements Comparator {
public int compare( final Object o1, final Object o2 ) {
final DrillDownParameter parameter1 = (DrillDownParameter) o1;
final DrillDownParameter parameter2 = (DrillDownParameter) o2;
if ( parameter1 == null && parameter2 == null ) {
return 0;
}
if ( parameter1 == null ) {
return -1;
}
if ( parameter2 == null ) {
return 1;
}
final DrillDownParameter.Type type1 = parameter1.getType();
final DrillDownParameter.Type type2 = parameter2.getType();
final int compareType = type1.compareTo( type2 );
if ( compareType != 0 ) {
return compareType;
}
if ( parameter1.getPosition() < parameter2.getPosition() ) {
return -1;
}
if ( parameter1.getPosition() > parameter2.getPosition() ) {
return 1;
}
return parameter1.getName().compareTo( parameter2.getName() );
}
}
private static final Log logger = LogFactory.getLog( DrillDownParameterTableModel.class );
private static final DrillDownParameter[] EMPTY_ELEMENTS = new DrillDownParameter[ 0 ];
private static final GroupingHeader[] EMPTY_GROUPINGS = new GroupingHeader[ 0 ];
private HashSet filteredParameterNames;
private String[] filteredParameterNamesArray;
private GroupingHeader[] groupings;
private TableStyle tableStyle;
private DrillDownParameter[] elements;
private DrillDownParameter[] groupedElements;
private String[] extraFields;
/**
* Constructs a default <code>DefaultTableModel</code> which is a table of zero columns and zero rows.
*/
public DrillDownParameterTableModel() {
this.filteredParameterNamesArray = new String[ 0 ];
this.filteredParameterNames = new HashSet();
this.tableStyle = TableStyle.GROUPED;
this.elements = EMPTY_ELEMENTS;
this.groupings = EMPTY_GROUPINGS;
this.groupedElements = EMPTY_ELEMENTS;
this.extraFields = new String[ 0 ];
}
public String[] getExtraFields() {
return extraFields.clone();
}
public void setExtraFields( final String[] extraFields ) {
this.extraFields = extraFields.clone();
}
/**
* Returns the number of rows in the model. A <code>JTable</code> uses this method to determine how many rows it
* should display. This method should be quick, as it is called frequently during rendering.
*
* @return the number of rows in the model
* @see #getColumnCount
*/
public int getRowCount() {
return groupedElements.length;
}
/**
* Returns the number of columns in the model. A <code>JTable</code> uses this method to determine how many columns it
* should create and display by default.
*
* @return the number of columns in the model
* @see #getRowCount
*/
public int getColumnCount() {
return 2;
}
/**
* Returns a default name for the column using spreadsheet conventions: A, B, C, ... Z, AA, AB, etc. If
* <code>column</code> cannot be found, returns an empty string.
*
* @param column the column being queried
* @return a string containing the default name of <code>column</code>
*/
public String getColumnName( final int column ) {
if ( column == 0 ) {
return Messages.getString( "DrillDownParameterTable.ParameterName" );
}
return Messages.getString( "DrillDownParameterTable.ParameterValue" );
}
public TableStyle getTableStyle() {
return tableStyle;
}
public void setTableStyle( final TableStyle tableStyle ) {
if ( tableStyle == null ) {
throw new NullPointerException();
}
this.tableStyle = tableStyle;
updateData( getData() );
}
private DrillDownParameter[] filter( final DrillDownParameter[] elements ) {
final ArrayList<DrillDownParameter> retval = new ArrayList<DrillDownParameter>( elements.length );
for ( int i = 0; i < elements.length; i++ ) {
final DrillDownParameter element = elements[ i ];
if ( filteredParameterNames.contains( element.getName() ) ) {
continue;
}
retval.add( element );
}
return retval.toArray( new DrillDownParameter[ retval.size() ] );
}
protected void updateData( final DrillDownParameter[] elements ) {
this.elements = elements.clone();
final DrillDownParameter[] metaData = filter( elements );
if ( tableStyle == TableStyle.ASCENDING ) {
Arrays.sort( metaData, new PlainParameterComparator() );
this.groupings = new GroupingHeader[ metaData.length ];
this.groupedElements = metaData;
} else if ( tableStyle == TableStyle.DESCENDING ) {
Arrays.sort( metaData, Collections.reverseOrder( new PlainParameterComparator() ) );
this.groupings = new GroupingHeader[ metaData.length ];
this.groupedElements = metaData;
} else {
Arrays.sort( metaData, new GroupedParameterComparator() );
int groupCount = 0;
if ( metaData.length > 0 ) {
DrillDownParameter.Type oldValue = null;
for ( int i = 0; i < metaData.length; i++ ) {
if ( groupCount == 0 ) {
groupCount = 1;
final DrillDownParameter firstdata = metaData[ i ];
oldValue = firstdata.getType();
continue;
}
final DrillDownParameter data = metaData[ i ];
final DrillDownParameter.Type grouping = data.getType();
if ( ( ObjectUtilities.equal( oldValue, grouping ) ) == false ) {
oldValue = grouping;
groupCount += 1;
}
}
}
final DrillDownParameter[] groupedMetaData = new DrillDownParameter[ metaData.length + groupCount ];
this.groupings = new GroupingHeader[ groupedMetaData.length ];
int targetIdx = 0;
GroupingHeader group = null;
for ( int sourceIdx = 0; sourceIdx < metaData.length; sourceIdx++ ) {
final DrillDownParameter data = metaData[ sourceIdx ];
if ( sourceIdx == 0 ) {
group = new GroupingHeader( data.getType().toString() );
groupings[ targetIdx ] = group;
targetIdx += 1;
} else {
final String newgroup = data.getType().toString();
if ( ( ObjectUtilities.equal( newgroup, group.getHeaderText() ) ) == false ) {
group = new GroupingHeader( newgroup );
groupings[ targetIdx ] = group;
targetIdx += 1;
}
}
groupings[ targetIdx ] = group;
groupedMetaData[ targetIdx ] = data;
targetIdx += 1;
}
this.groupedElements = groupedMetaData;
}
fireTableDataChanged();
}
/**
* Returns the value for the cell at <code>columnIndex</code> and <code>rowIndex</code>.
*
* @param rowIndex the row whose value is to be queried
* @param columnIndex the column whose value is to be queried
* @return the value Object at the specified cell
*/
public Object getValueAt( final int rowIndex, final int columnIndex ) {
final DrillDownParameter metaData = groupedElements[ rowIndex ];
if ( metaData == null ) {
return groupings[ rowIndex ];
}
switch( columnIndex ) {
case 0:
return new GroupedName( metaData, metaData.getName(), metaData.getType().toString() );
case 1:
return metaData.getFormulaFragment();
default:
throw new IndexOutOfBoundsException();
}
}
/**
* Returns false. This is the default implementation for all cells.
*
* @param rowIndex the row being queried
* @param columnIndex the column being queried
* @return false
*/
public boolean isCellEditable( final int rowIndex, final int columnIndex ) {
final DrillDownParameter metaData = groupedElements[ rowIndex ];
if ( metaData == null ) {
return false;
}
switch( columnIndex ) {
case 0:
return metaData.getType() == DrillDownParameter.Type.MANUAL;
case 1:
return true;
default:
throw new IndexOutOfBoundsException();
}
}
public void setValueAt( final Object aValue, final int rowIndex, final int columnIndex ) {
final DrillDownParameter metaData = groupedElements[ rowIndex ];
if ( metaData == null ) {
return;
}
switch( columnIndex ) {
case 0:
if ( aValue instanceof GroupedName ) {
final GroupedName name = (GroupedName) aValue;
metaData.setName( name.getName() );
fireTableDataChanged();
}
return;
case 1: {
if ( aValue == null ) {
metaData.setFormulaFragment( null );
} else {
metaData.setFormulaFragment( String.valueOf( aValue ) );
}
fireTableDataChanged();
break;
}
default:
throw new IndexOutOfBoundsException();
}
}
public Class getClassForCell( final int row, final int column ) {
final DrillDownParameter metaData = groupedElements[ row ];
if ( metaData == null ) {
return GroupingHeader.class;
}
if ( column == 0 ) {
return GroupedName.class;
}
return String.class;
}
public PropertyEditor getEditorForCell( final int row, final int column ) {
return null;
}
public String getValueRole( final int row, final int column ) {
if ( column == 0 ) {
return AttributeMetaData.VALUEROLE_VALUE; // NON-NLS
}
return AttributeMetaData.VALUEROLE_FORMULA;// NON-NLS
}
public String[] getExtraFields( final int row, final int column ) {
return extraFields;
}
public GroupingHeader getGroupHeader( final int index ) {
return groupings[ index ];
}
public boolean isHeaderRow( final int index ) {
return groupedElements[ index ] == null;
}
public String[] getFilteredParameterNames() {
return filteredParameterNamesArray.clone();
}
public void setFilteredParameterNames( final String[] names ) {
this.filteredParameterNamesArray = names.clone();
this.filteredParameterNames.clear();
this.filteredParameterNames.addAll( Arrays.asList( names ) );
updateData( elements );
}
public void setData( final DrillDownParameter[] parameter ) {
updateData( parameter );
}
public DrillDownParameter[] getData() {
return elements.clone();
}
public DrillDownParameter[] getGroupedData() {
return groupedElements.clone();
}
public DrillDownParameter.Type getParameterType( final int row ) {
final DrillDownParameter downParameter = groupedElements[ row ];
if ( downParameter != null ) {
return downParameter.getType();
}
return null;
}
public boolean isPreferred( final int row ) {
final DrillDownParameter downParameter = groupedElements[ row ];
if ( downParameter != null ) {
return downParameter.isPreferred();
}
return true;
}
}