/*!
* 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 java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
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 "Name";
}
return "Value";
}
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;
}
}