/*
*------------------------------------------------------------------------------
* Copyright (C) 2014-2015 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.metadata.editor.maptable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import omero.model.NamedValue;
import org.openmicroscopy.shoola.util.ui.table.Reorderable;
import omero.gateway.model.MapAnnotationData;
/**
* {@link TableModel} used for the {@link MapTable}
*
* @author Dominik Lindner <a
* href="mailto:d.lindner@dundee.ac.uk">d.lindner@dundee.ac.uk</a>
*/
public class MapTableModel extends DefaultTableModel implements Reorderable {
/** Reference to the table */
private MapTable table;
/** The underlying {@link MapAnnotationData} to represent */
private MapAnnotationData map;
/** The current data displayed */
private List<NamedValue> data;
/** A copy of the original data */
private List<NamedValue> originalData;
/**
* Creates a new instance
*
* @param table
*/
public MapTableModel(MapTable table) {
this.table = table;
}
/**
* Get the entry of a certain row
*
* @param index The row to check.
* @return See above.
*/
public NamedValue getRow(int index) {
if (index >= 0 && index < data.size()) {
return data.get(index);
}
return null;
}
@Override
public int getRowCount() {
if (table == null)
return 0;
return data == null ? 0 : data.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return "Key";
case 1:
return "Value";
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return getValueAt(0, columnIndex).getClass();
}
@Override
public Object getValueAt(int row, int column) {
if (row < data.size()) {
NamedValue d = data.get(row);
switch (column) {
case 0:
return d.name;
case 1:
return d.value;
}
}
return null;
}
@Override
public void setValueAt(Object aValue, int row, int column) {
String value = (String) aValue;
if (row < data.size()) {
NamedValue d = data.get(row);
switch (column) {
case 0:
d.name = value;
break;
case 1:
d.value = value;
// automatically add a new row at the end of the table
if (row == data.size() - 1 && !MapUtils.isEmpty(d))
addEntry("", "");
}
}
fireTableCellUpdated(row, column);
}
/**
* Adds a new entry to the end
*
* @param name
* The name, see {@link NamedValue#name}
* @param value
* The value, see {@link NamedValue#value}
*/
void addEntry(String name, String value) {
addEntries(Arrays.asList(new NamedValue(name, value)), -1);
}
/**
* Inserts a list of entries at a certain position
*
* @param entries
* The entries to insert
* @param index
* The position where to insert them
*/
public void addEntries(List<NamedValue> entries, int index) {
if (index < 0 || index > data.size())
index = data.size();
data.addAll(index, entries);
syncBackToMap();
}
/**
* Delete an entry at a certain position
*
* @param index
* The position of the entry to delete
*/
public void deleteEntry(int index) {
if (index >= 0 && index < data.size()) {
data.remove(index);
syncBackToMap();
}
}
/**
* Delete entries at certain positions
*
* @param indices
* The positions of the entries to delete
*/
public void deleteEntries(int[] indices) {
List<NamedValue> toRemove = new ArrayList<NamedValue>();
for (int index : indices)
toRemove.add(getRow(index));
data.removeAll(toRemove);
syncBackToMap();
}
/**
* Set the {@link MapAnnotationData} to represent
*
* @param map
* The {@link MapAnnotationData}
*/
@SuppressWarnings("unchecked")
public void setData(MapAnnotationData map) {
this.map = map;
this.data = (List<NamedValue>) map.getContent();
if (this.data == null) {
this.data = new ArrayList<NamedValue>();
this.originalData = new ArrayList<NamedValue>();
} else {
this.originalData = new ArrayList<NamedValue>(this.data.size());
for (NamedValue tmp : data) {
this.originalData.add(new NamedValue(tmp.name, tmp.value));
}
}
// Add a dummy row to an editable, but empty table
if (data.size() == 0 && table.canEdit()) {
addEntry(MapUtils.DUMMY_KEY, MapUtils.DUMMY_VALUE);
}
}
/**
* Returns the data, but with empty rows removed
*
* @return See above
*/
private List<NamedValue> getTrimmedData() {
List<NamedValue> result = new ArrayList<NamedValue>();
for (NamedValue nv : data) {
if (!MapUtils.isEmpty(nv))
result.add(nv);
}
return result;
}
/**
* Writes the data back to the MapAnnotation and revalidates the table
*/
private void syncBackToMap() {
map.setContent(getTrimmedData());
table.revalidate();
fireTableDataChanged();
}
/**
* Check if the {@link MapTableModel} has been modified
*
* @return <code>true</code> if it has been modified, <code>false</code>
* otherwise
*/
public boolean isDirty() {
List<NamedValue> data = getTrimmedData();
if (data.size() != originalData.size())
return true;
for (int i = 0; i < data.size(); i++) {
NamedValue nv1 = data.get(i);
NamedValue nv2 = originalData.get(i);
if (!nv1.name.equals(nv2.name) || !nv1.value.equals(nv2.value))
return true;
}
return false;
}
/**
* Checks if the table contains any {@link NamedValue}s
*
* @return <code>true</code> if it doesn't, <code>false</code> if it does.
*/
public boolean isEmpty() {
return data.isEmpty();
}
/**
* Get the {@link MapAnnotationData} represented by this
* {@link MapTableModel}
*
* @return See above.
*/
public MapAnnotationData getMap() {
if (isDirty()) {
map.setContent(getTrimmedData());
}
return map;
}
@Override
public boolean isCellEditable(int row, int column) {
if (column == 2)
return false;
return table.canEdit();
}
@Override
public int reorder(int fromIndices[], int toIndex) {
List<NamedValue> values = new ArrayList<NamedValue>();
int offset = 0;
for (int fromIndex : fromIndices) {
if (toIndex < 0)
toIndex = 0;
if (toIndex > data.size())
toIndex = data.size();
if (fromIndex >= 0 && fromIndex < data.size()) {
values.add(data.get(fromIndex));
if (fromIndex <= toIndex)
offset--;
}
}
int newIndex = toIndex + offset;
if (!values.isEmpty()) {
data.removeAll(values);
data.addAll(newIndex, values);
}
syncBackToMap();
return newIndex;
}
}