/*!
* 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 java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.SwingUtilities;
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;
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();
}
}
}