/*!
* 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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.designer.core.util.FastPropertyEditorManager;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
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.engine.classic.core.metadata.StyleMetaData;
import org.pentaho.reporting.engine.classic.core.style.ResolverStyleSheet;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.beans.PropertyEditor;
public abstract class AbstractStyleTableModel<T extends StyleDataBackend>
extends AbstractTableModel implements ElementMetaDataTableModel, GroupingModel {
protected class SameElementsUpdateDataTask implements Runnable {
private T dataBackend;
private boolean synchronous;
protected SameElementsUpdateDataTask( final T elements,
final boolean synchronous ) {
this.dataBackend = elements;
this.synchronous = synchronous;
}
public void run() {
dataBackend.resetCache();
try {
if ( synchronous || SwingUtilities.isEventDispatchThread() ) {
setDataBackend( dataBackend );
fireTableDataChanged();
} else {
SwingUtilities.invokeAndWait( new NotifyChangeTask( dataBackend ) );
}
} catch ( Exception e ) {
UncaughtExceptionsModel.getInstance().addException( e );
}
}
}
protected class NotifyChangeTask implements Runnable {
private T dataBackend;
protected NotifyChangeTask( final T dataBackend ) {
this.dataBackend = dataBackend;
}
public void run() {
setDataBackend( dataBackend );
fireTableDataChanged();
}
}
private static final Log logger = LogFactory.getLog( AbstractStyleTableModel.class );
private static final String[] EXTRA_FIELDS = new String[ 0 ];
private TableStyle tableStyle;
private T dataBackend;
private boolean synchronous;
public AbstractStyleTableModel() {
tableStyle = TableStyle.GROUPED;
}
public boolean isSynchronous() {
return synchronous;
}
public void setSynchronous( final boolean synchronous ) {
this.synchronous = synchronous;
}
protected synchronized T getDataBackend() {
return dataBackend;
}
protected synchronized void setDataBackend( final T dataBackend ) {
this.dataBackend = dataBackend;
}
public int getRowCount() {
return dataBackend.getRowCount();
}
protected StyleMetaData getMetaData( final int row ) {
return getDataBackend().getMetaData( row );
}
protected GroupingHeader getGroupings( final int row ) {
return getDataBackend().getGroupings( row );
}
public TableStyle getTableStyle() {
return tableStyle;
}
public void setTableStyle( final TableStyle tableStyle ) {
if ( tableStyle == null ) {
throw new NullPointerException();
}
this.tableStyle = tableStyle;
}
/**
* Uses the name of the old groupings to set the collapse status of the new groupings so that when a user makes a
* selection not all of the groups return to the expanded state. In essence makes group collapses "sticky" where the
* group heading hasn't changed.
*
* @param groupings
* @param oldGroupings
*/
protected GroupingHeader[] reconcileState( final GroupingHeader[] groupings,
final GroupingHeader[] oldGroupings ) {
if ( oldGroupings == null ) {
return groupings;
}
for ( int i = 0; i < groupings.length; i++ ) {
final GroupingHeader header = groupings[ i ];
if ( header == null ) {
continue;
}
final GroupingHeader oldHeader = findFirstOccurrenceOfHeaderTitle( oldGroupings, header.getHeaderText() );
if ( oldHeader != null ) {
header.setCollapsed( oldHeader.isCollapsed() );
}
}
return groupings;
}
private GroupingHeader findFirstOccurrenceOfHeaderTitle( final GroupingHeader[] headerArray,
final String headerTitle ) {
for ( final GroupingHeader header : headerArray ) {
if ( header == null ) {
continue;
}
if ( ObjectUtilities.equal( header.getHeaderText(), headerTitle ) ) {
return header;
}
}
return null;
}
public int getColumnCount() {
return 3;
}
public String getColumnName( final int column ) {
switch( column ) {
case 0:
return Messages.getString( "StyleTableModel.NameColumn" );
case 1:
return Messages.getString( "StyleTableModel.InheritColumn" );
case 2:
return Messages.getString( "StyleTableModel.ValueColumn" );
default:
throw new IllegalArgumentException();
}
}
public Object getValueAt( final int rowIndex, final int columnIndex ) {
final StyleMetaData metaData = getMetaData( rowIndex );
if ( metaData == null ) {
return getGroupings( rowIndex );
}
switch( columnIndex ) {
case 0:
return new GroupedName( metaData );
case 1:
return computeInheritValue( metaData, rowIndex );
case 2:
return computeFullValue( metaData, rowIndex );
default:
throw new IndexOutOfBoundsException();
}
}
public boolean isCellEditable( final int rowIndex, final int columnIndex ) {
final StyleMetaData metaData = getMetaData( rowIndex );
if ( metaData == null ) {
return false;
}
switch( columnIndex ) {
case 0:
return false;
case 1:
case 2:
return true;
default:
throw new IndexOutOfBoundsException();
}
}
public void setValueAt( final Object aValue, final int rowIndex, final int columnIndex ) {
final StyleMetaData metaData = getMetaData( rowIndex );
if ( metaData == null ) {
return;
}
switch( columnIndex ) {
case 0:
return;
case 1: {
if ( Boolean.TRUE.equals( aValue ) ) {
if ( defineFullValue( metaData, null ) ) {
getDataBackend().clearCache( rowIndex );
fireTableDataChanged();
}
}
break;
}
case 2: {
if ( defineFullValue( metaData, aValue ) ) {
getDataBackend().clearCache( rowIndex );
fireTableDataChanged();
}
break;
}
default:
throw new IndexOutOfBoundsException();
}
}
protected abstract Object computeInheritValue( final StyleMetaData metaData,
final int rowIndex );
protected abstract boolean defineFullValue( final StyleMetaData metaData, final Object value );
protected Object computeFullValue( final StyleMetaData metaData,
final int row ) {
final StyleDataBackend dataBackend1 = getDataBackend();
final Object[] fullValues = dataBackend1.getFullValues();
final Object o = fullValues[ row ];
if ( o == StyleDataBackend.NULL_INDICATOR ) {
return null;
}
if ( o != null ) {
return o;
}
final ResolverStyleSheet styleSheet = dataBackend1.getResolvedStyle();
final Object lastElement = styleSheet.getStyleProperty( metaData.getStyleKey() );
if ( lastElement != null ) {
fullValues[ row ] = lastElement;
} else {
fullValues[ row ] = StyleDataBackend.NULL_INDICATOR;
}
return lastElement;
}
public Class getClassForCell( final int rowIndex, final int columnIndex ) {
final StyleMetaData metaData = getMetaData( rowIndex );
if ( metaData == null ) {
return GroupingHeader.class;
}
switch( columnIndex ) {
case 0:
return GroupedName.class;
case 1:
return Boolean.class;
case 2:
return metaData.getTargetType();
default:
throw new IndexOutOfBoundsException();
}
}
public PropertyEditor getEditorForCell( final int rowIndex, final int columnIndex ) {
final StyleMetaData metaData = getMetaData( rowIndex );
if ( metaData == null ) {
return null;
}
switch( columnIndex ) {
case 0:
return null;
case 1:
return null;
case 2:
return computeEditor( metaData, rowIndex );
default:
throw new IndexOutOfBoundsException();
}
}
protected PropertyEditor computeEditor( final StyleMetaData metaData,
final int row ) {
final Object[] propertyEditors = getDataBackend().getPropertyEditors();
final Object o = propertyEditors[ row ];
if ( o == StyleDataBackend.NULL_INDICATOR ) {
return null;
}
if ( o != null ) {
return (PropertyEditor) o;
}
PropertyEditor propertyEditor = metaData.getEditor();
if ( propertyEditor == null ) {
propertyEditor = getDefaultEditor( metaData.getTargetType() );
}
if ( propertyEditor == null ) {
propertyEditors[ row ] = StyleDataBackend.NULL_INDICATOR;
} else {
propertyEditors[ row ] = propertyEditor;
}
return propertyEditor;
}
protected PropertyEditor getDefaultEditor( final Class type ) {
if ( String.class.equals( type ) ) {
return null;
}
return FastPropertyEditorManager.findEditor( type );
}
public String getValueRole( final int row, final int column ) {
return AttributeMetaData.VALUEROLE_VALUE;
}
public String[] getExtraFields( final int row, final int column ) {
return EXTRA_FIELDS;
}
public GroupingHeader getGroupHeader( final int index ) {
return getGroupings( index );
}
public boolean isHeaderRow( final int index ) {
return dataBackend.getMetaData( index ) == null;
}
}