/*! * 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.attributes; 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.ReportDocumentContext; 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.undo.AttributeEditUndoEntry; import org.pentaho.reporting.designer.core.util.undo.CompoundUndoEntry; import org.pentaho.reporting.designer.core.util.undo.UndoEntry; import org.pentaho.reporting.designer.core.util.undo.UndoManager; import org.pentaho.reporting.engine.classic.core.ReportElement; import org.pentaho.reporting.engine.classic.core.metadata.AttributeMetaData; import org.pentaho.reporting.engine.classic.core.metadata.ElementType; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import javax.swing.*; import java.beans.PropertyEditor; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AttributeTableModel extends AbstractAttributeTableModel { private static final Log logger = LogFactory.getLog( AttributeTableModel.class ); private static final ReportElement[] EMPTY = new ReportElement[ 0 ]; private static final Object[] EMPTY_VALUES = new Object[ 0 ]; private static final Object NULL_INDICATOR = new Object(); private static class AttributeDataBackend extends DataBackend { private ReportElement[] elements; private Object[] fullValues; private Object[] propertyEditors; private AttributeDataBackend() { elements = EMPTY; propertyEditors = EMPTY_VALUES; fullValues = EMPTY_VALUES; } private AttributeDataBackend( final AttributeMetaData[] metaData, final GroupingHeader[] groupings, final ReportElement[] elements ) { super( metaData, groupings ); this.elements = elements; this.fullValues = new Object[ metaData.length ]; this.propertyEditors = new Object[ metaData.length ]; } public ReportElement[] getData() { return elements.clone(); } public Object[] getFullValues() { return fullValues; } public Object[] getPropertyEditors() { return propertyEditors; } public void resetCache() { Arrays.fill( fullValues, null ); } } private ExecutorService pool; private static final String[] EMPTY_FIELDS = new String[ 0 ]; public AttributeTableModel() { pool = Executors.newSingleThreadExecutor(); setDataBackend( new AttributeDataBackend() ); } protected AttributeDataBackend getAttributeDataBackend() { return (AttributeDataBackend) getDataBackend(); } public ReportElement[] getData() { return getAttributeDataBackend().getData(); } public void setData( final ReportElement[] elements ) { // thats fast, we only compare the ids .. if ( isSameElements( elements, getData(), null ) ) { SwingUtilities.invokeLater( new SameElementsUpdateDataTask( getDataBackend() ) ); return; } pool.submit( new UpdateDataTask( elements ) ); } protected void refreshData() { final ReportElement[] data = getAttributeDataBackend().getData(); setDataBackend( updateData( data ) ); } protected DataBackend createDataBackend( final GroupingHeader[] headers, final AttributeMetaData[] metaData, final ReportElement[] elements, final ElementType[] elementTypes ) { super.createDataBackend( headers, metaData, elements, elementTypes ); return new AttributeDataBackend( metaData, headers, elements ); } public int getColumnCount() { return 2; } public Object getValueAt( final int rowIndex, final int columnIndex ) { final AttributeMetaData metaData = getMetaData( rowIndex ); if ( metaData == null ) { return getGroupings( rowIndex ); } switch( columnIndex ) { case 0: return new GroupedName( metaData ); case 1: return computeFullValue( metaData, rowIndex ); default: throw new IndexOutOfBoundsException(); } } public boolean isCellEditable( final int rowIndex, final int columnIndex ) { final AttributeMetaData metaData = getMetaData( rowIndex ); if ( metaData == null ) { return false; } switch( columnIndex ) { case 0: return false; case 1: return "ElementType".equals( metaData.getValueRole() ) == false; // NON-NLS default: throw new IndexOutOfBoundsException(); } } public String getValueRole( final int row, final int column ) { if ( column != 1 ) { return null; } final AttributeMetaData metaData = getMetaData( row ); if ( metaData == null ) { return null; } return metaData.getValueRole(); } public String[] getExtraFields( final int row, final int column ) { if ( column == 0 ) { return EMPTY_FIELDS; } final AttributeMetaData metaData = getMetaData( row ); if ( metaData == null ) { return EMPTY_FIELDS; } return metaData.getExtraCalculationFields(); } public void setValueAt( final Object aValue, final int rowIndex, final int columnIndex ) { final AttributeMetaData metaData = getMetaData( rowIndex ); if ( metaData == null ) { return; } switch( columnIndex ) { case 0: return; case 1: { if ( defineFullValue( metaData, aValue ) ) { final AttributeDataBackend db = (AttributeDataBackend) getDataBackend(); db.fullValues[ rowIndex ] = null; fireTableDataChanged(); } break; } default: throw new IndexOutOfBoundsException(); } } private boolean defineFullValue( final AttributeMetaData metaData, final Object value ) { if ( value != null && metaData.getTargetType().isInstance( value ) == false ) { // not the correct type logger.warn( "Invalid type: " + value + " but expected " + metaData.getTargetType() ); // NON-NLS return false; } final ReportDocumentContext reportRenderContext = getReportRenderContext(); if ( reportRenderContext == null ) { throw new IllegalStateException( "No report render context? Thats bad." ); } final UndoManager undo = reportRenderContext.getUndo(); boolean changed = false; final ReportElement[] elements = getAttributeDataBackend().getData(); final ArrayList<UndoEntry> undos = new ArrayList<UndoEntry>(); for ( int i = 0; i < elements.length; i++ ) { final ReportElement element = elements[ i ]; final Object attribute = element.getAttribute( metaData.getNameSpace(), metaData.getName() ); if ( ( ObjectUtilities.equal( attribute, value ) ) == false ) { undos.add( new AttributeEditUndoEntry ( element.getObjectID(), metaData.getNameSpace(), metaData.getName(), attribute, value ) ); element.setAttribute( metaData.getNameSpace(), metaData.getName(), value ); changed = true; } } undo.addChange( Messages.getString( "AttributeTableModel.UndoName" ), new CompoundUndoEntry( (UndoEntry[]) undos.toArray( new UndoEntry[ undos.size() ] ) ) ); return changed; } private Object computeFullValue( final AttributeMetaData metaData, final int row ) { final AttributeDataBackend dataBackend = getAttributeDataBackend(); final Object[] fullValues = dataBackend.getFullValues(); final Object o = fullValues[ row ]; if ( o == NULL_INDICATOR ) { return null; } if ( o != null ) { return o; } Object lastElement = null; final ReportElement[] elements = dataBackend.getData(); if ( elements.length > 0 ) { final ReportElement element = elements[ 0 ]; lastElement = element.getAttribute( metaData.getNameSpace(), metaData.getName() ); } if ( lastElement != null ) { fullValues[ row ] = lastElement; } else { fullValues[ row ] = NULL_INDICATOR; } return lastElement; } public Class getClassForCell( final int rowIndex, final int columnIndex ) { final AttributeMetaData metaData = getMetaData( rowIndex ); if ( metaData == null ) { return GroupingHeader.class; } switch( columnIndex ) { case 0: return GroupedName.class; case 1: return metaData.getTargetType(); default: throw new IndexOutOfBoundsException(); } } public PropertyEditor getEditorForCell( final int rowIndex, final int columnIndex ) { final AttributeMetaData metaData = getMetaData( rowIndex ); if ( metaData == null ) { return null; } switch( columnIndex ) { case 0: return null; case 1: return computeEditor( metaData, rowIndex ); default: throw new IndexOutOfBoundsException(); } } private PropertyEditor computeEditor( final AttributeMetaData metaData, final int row ) { final Object[] propertyEditors = getAttributeDataBackend().getPropertyEditors(); final Object o = propertyEditors[ row ]; if ( o == NULL_INDICATOR ) { return null; } if ( o != null ) { return (PropertyEditor) o; } PropertyEditor propertyEditor = metaData.getEditor(); if ( propertyEditor == null ) { propertyEditor = getDefaultEditor( metaData.getTargetType(), metaData.getValueRole() ); } if ( propertyEditor == null ) { propertyEditors[ row ] = NULL_INDICATOR; } else { propertyEditors[ row ] = propertyEditor; } return propertyEditor; } public String getColumnName( final int column ) { switch( column ) { case 0: return Messages.getString( "AttributeTableModel.NameColumn" ); case 1: return Messages.getString( "AttributeTableModel.ValueColumn" ); default: throw new IllegalArgumentException(); } } }