/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.workbench.ui; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.swing.JTable; import javax.swing.event.TableModelEvent; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.util.Assert; import com.vividsolutions.jump.I18N; import com.vividsolutions.jump.feature.AttributeType; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureSchema; import com.vividsolutions.jump.workbench.model.CategoryEvent; import com.vividsolutions.jump.workbench.model.FeatureEvent; import com.vividsolutions.jump.workbench.model.FeatureEventType; import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.model.LayerEvent; import com.vividsolutions.jump.workbench.model.LayerEventType; import com.vividsolutions.jump.workbench.model.LayerListener; import com.vividsolutions.jump.workbench.model.UndoableCommand; public class LayerTableModel extends ColumnBasedTableModel { private Layer layer; private ArrayList features = new ArrayList(); private String sortedColumnName = null; private boolean sortAscending = false; private abstract class MyColumn extends Column { public MyColumn(String name, Class dataClass) { super(name, dataClass); } public Object getValueAt(int rowIndex) { return getValue(getFeature(rowIndex)); } public void setValueAt(Object value, int rowIndex) { setValue(value, getFeature(rowIndex)); } protected abstract Object getValue(Feature feature); protected abstract void setValue(Object value, Feature feature); } private Column fidColumn = new MyColumn("FID", Integer.class) { protected Object getValue(Feature feature) { return new Integer(feature.getID()); } protected void setValue(Object value, Feature feature) { Assert.shouldNeverReachHere(); } }; private Column geomButtonColumn = new MyColumn(" ", null) {//button column [Jon Aquino] protected Object getValue(Feature feature) { return feature; } protected void setValue(Object value, Feature feature) { Assert.shouldNeverReachHere(); } }; private FeatureSchema schema; public LayerTableModel(final Layer layer) { this.layer = layer; layer.getLayerManager().addLayerListener(layerListener); initColumns(layer); } private LayerListener layerListener = new LayerListener() { public void categoryChanged(CategoryEvent e) {} public void featuresChanged(FeatureEvent e) { if (e.getLayer() != getLayer()) { return; } if (e.getType() == FeatureEventType.DELETED) { removeAll(e.getFeatures()); } if (e.getType() == FeatureEventType.ATTRIBUTES_MODIFIED) { for (Iterator i = e.getFeatures().iterator(); i.hasNext();) { Feature feature = (Feature) i.next(); int row = getFeatures().indexOf(feature); if (row != -1) { fireTableChanged(new TableModelEvent(LayerTableModel.this, row, row)); } } } } public void layerChanged(LayerEvent e) { if (e.getLayerable() != getLayer()) { return; } if (e.getType() == LayerEventType.METADATA_CHANGED) { //User may have changed the schema. [Jon Aquino] if (!schema.equals(layer.getFeatureCollectionWrapper().getFeatureSchema(), true)) { initColumns(layer); fireTableChanged( new TableModelEvent(LayerTableModel.this, TableModelEvent.HEADER_ROW)); } } } }; private void initColumns(final Layer layer) { schema = layer.getFeatureCollectionWrapper().getFeatureSchema(); ArrayList columns = new ArrayList(); columns.add(geomButtonColumn); columns.add(fidColumn); for (int i = 0; i < schema.getAttributeCount(); i++) { if (schema.getAttributeType(i) == AttributeType.GEOMETRY) { continue; } final int j = i; columns .add(new MyColumn(schema.getAttributeName(i), schema.getAttributeType(i).toJavaClass()) { protected Object getValue(Feature feature) { // MD - trapping bad index value here, since at this point it's too late to do anything about it Object value = null; try { value = feature.getAttribute(j); } catch (ArrayIndexOutOfBoundsException ex) { ex.printStackTrace(); } return value; } protected void setValue(Object value, final Feature feature) { final Feature oldAttributes = (Feature) feature.clone(); final Feature newAttributes = (Feature) feature.clone(); newAttributes.setAttribute(j, value); layer.getLayerManager().getUndoableEditReceiver().startReceiving(); try { UndoableCommand command = new UndoableCommand(I18N.get("ui.plugin.LayerTableModel.edit")+" " + schema.getAttributeName(j)) { public void execute() { setAttributesOf(feature, newAttributes); } public void unexecute() { setAttributesOf(feature, oldAttributes); } }; command.execute(); layer.getLayerManager().getUndoableEditReceiver().receive( command.toUndoableEdit()); } finally { layer.getLayerManager().getUndoableEditReceiver().stopReceiving(); } } }); } setColumns(columns); } private void setAttributesOf(Feature feature, Feature attributes) { // [UT] 25.08.2005 the old clone is available here but not used! so use it! Feature oldClone = (Feature) feature.clone(); for (int i = 0; i < feature.getSchema().getAttributeCount(); i++) { feature.setAttribute(i, attributes.getAttribute(i)); } // remove this to include method with reference to old feature /*layer.getLayerManager().fireFeaturesChanged( Arrays.asList(new Feature[] { feature }), FeatureEventType.ATTRIBUTES_MODIFIED, layer);*/ layer.getLayerManager().fireFeaturesAttChanged( Arrays.asList(new Feature[] { feature }), FeatureEventType.ATTRIBUTES_MODIFIED, layer, Arrays.asList(new Feature[] { oldClone })); } public Layer getLayer() { return layer; } public Feature getFeature(int row) { return (Feature) features.get(row); } public int getRowCount() { return features.size(); } public boolean isCellEditable(int rowIndex, int columnIndex) { if (!layer.isEditable()) { return false; } if (getColumn(columnIndex) == fidColumn) { return false; } if (getColumn(columnIndex) == geomButtonColumn) { return false; } FeatureSchema schema = layer.getFeatureCollectionWrapper().getFeatureSchema(); if (schema.isAttributeReadOnly(schema.getAttributeIndex(getColumn( columnIndex).getName()))) return false; return true; } public void clear() { features.clear(); fireTableChanged(new TableModelEvent(this)); } //public void removeAll(Collection featuresToRemove) { // //if (featuresToRemove.size() > 100) { // // removeAllFast(featuresToRemove); // // return; // //} // for (Iterator i = featuresToRemove.iterator(); i.hasNext();) { // Feature feature = (Feature) i.next(); // int row = features.indexOf(feature); // if (row == -1) { // //A LayerTableModel might not have all the features in a layer // //i.e. a FeatureInfo window, as opposed to a complete Attributes window. [Jon Aquino] // continue; // } // features.remove(row); // fireTableChanged( // new TableModelEvent( // this, // row, // row, // TableModelEvent.ALL_COLUMNS, // TableModelEvent.DELETE)); // } //} private void removeAll(Collection featuresToRemove) { List<Integer> idsToRemove = new ArrayList<Integer>(); for (Iterator it = featuresToRemove.iterator() ; it.hasNext() ; ) { idsToRemove.add(((Feature)it.next()).getID()); } Collections.sort(idsToRemove); ArrayList newFeatures = new ArrayList(); for (Iterator it = features.iterator() ; it.hasNext() ; ) { Feature f = (Feature)it.next(); if (Collections.binarySearch(idsToRemove, f.getID()) < 0) { newFeatures.add(f); } } features = newFeatures; fireTableChanged(new TableModelEvent(this)); } public void addAll(Collection newFeatures) { int originalFeaturesSize = features.size(); Collection newFeaturesOnly = new ArrayList(newFeatures); newFeaturesOnly.removeAll(features); features.addAll(newFeaturesOnly); if (sortedColumnName != null) { sort(sortedColumnName, sortAscending); } fireTableChanged( new TableModelEvent( this, originalFeaturesSize, features.size() - 1, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT)); } /** * Facilitate garbage collection by releasing references. */ public void dispose() { layer.getLayerManager().removeLayerListener(layerListener); features.clear(); } public List getFeatures() { return Collections.unmodifiableList(features); } /** * @return null if the table has not yet been sorted */ public String getSortedColumnName() { return sortedColumnName; } public boolean isSortAscending() { return sortAscending; } public void sort(String columnName) { sort(columnName, columnName.equals(sortedColumnName) ? (!sortAscending) : true); } public void sort(final String columnName, final boolean ascending) { this.sortAscending = ascending; this.sortedColumnName = columnName; final int column = indexOfColumn(columnName); Collections.sort(features, new Comparator() { public int compare(Object o1, Object o2) { return ascendingCompare(o1, o2) * (ascending ? 1 : (-1)); } private int ascendingCompare(Object o1, Object o2) { Feature f1 = (Feature) o1; Feature f2 = (Feature) o2; Object v1 = ((MyColumn) getColumn(column)).getValue(f1); Object v2 = ((MyColumn) getColumn(column)).getValue(f2); return compareValue(v1, v2); } }); } private static int compareValue(Object o1, Object o2) { if (o1 == null) return -1; if (o2 == null) return 1; if (o1 instanceof Boolean) { return compareBoolean((Boolean) o1, (Boolean) o2); } else if (o1 instanceof Geometry) { return 0; // for now - change to compare type } else if (o1 instanceof Comparable) { Comparable attribute1 = (Comparable) o1; Comparable attribute2 = (Comparable) o2; return attribute1.compareTo(attribute2); } return 0; } private static int compareBoolean(Boolean b1, Boolean b2) { boolean bool1 = b1.booleanValue(); boolean bool2 = b2.booleanValue(); if (bool1 == bool2) return 0; return bool1 ? 1 : -1; } public String getType(int column) { return null; } public static void main(String[] args) { System.out.println(new JTable().getDefaultEditor(Date.class)); } }