001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.workbench.ui;
034:
035: import java.util.ArrayList;
036: import java.util.Arrays;
037: import java.util.Collection;
038: import java.util.Collections;
039: import java.util.Comparator;
040: import java.util.Date;
041: import java.util.Iterator;
042: import java.util.List;
043:
044: import javax.swing.JTable;
045: import javax.swing.event.TableModelEvent;
046:
047: import com.vividsolutions.jts.geom.*;
048: import com.vividsolutions.jts.util.Assert;
049: import com.vividsolutions.jump.I18N;
050: import com.vividsolutions.jump.feature.AttributeType;
051: import com.vividsolutions.jump.feature.Feature;
052: import com.vividsolutions.jump.feature.FeatureSchema;
053: import com.vividsolutions.jump.workbench.model.CategoryEvent;
054: import com.vividsolutions.jump.workbench.model.FeatureEvent;
055: import com.vividsolutions.jump.workbench.model.FeatureEventType;
056: import com.vividsolutions.jump.workbench.model.Layer;
057: import com.vividsolutions.jump.workbench.model.LayerEvent;
058: import com.vividsolutions.jump.workbench.model.LayerEventType;
059: import com.vividsolutions.jump.workbench.model.LayerListener;
060: import com.vividsolutions.jump.workbench.model.UndoableCommand;
061:
062: public class LayerTableModel extends ColumnBasedTableModel {
063: private Layer layer;
064: private ArrayList features = new ArrayList();
065: private String sortedColumnName = null;
066: private boolean sortAscending = false;
067:
068: private abstract class MyColumn extends Column {
069: public MyColumn(String name, Class dataClass) {
070: super (name, dataClass);
071: }
072:
073: public Object getValueAt(int rowIndex) {
074: return getValue(getFeature(rowIndex));
075: }
076:
077: public void setValueAt(Object value, int rowIndex) {
078: setValue(value, getFeature(rowIndex));
079: }
080:
081: protected abstract Object getValue(Feature feature);
082:
083: protected abstract void setValue(Object value, Feature feature);
084: }
085:
086: private Column fidColumn = new MyColumn("FID", Integer.class) {
087: protected Object getValue(Feature feature) {
088: return new Integer(feature.getID());
089: }
090:
091: protected void setValue(Object value, Feature feature) {
092: Assert.shouldNeverReachHere();
093: }
094: };
095:
096: private Column geomButtonColumn = new MyColumn(" ", null) {//button column [Jon Aquino]
097: protected Object getValue(Feature feature) {
098: return feature;
099: }
100:
101: protected void setValue(Object value, Feature feature) {
102: Assert.shouldNeverReachHere();
103: }
104: };
105:
106: private FeatureSchema schema;
107:
108: public LayerTableModel(final Layer layer) {
109: this .layer = layer;
110:
111: layer.getLayerManager().addLayerListener(layerListener);
112: initColumns(layer);
113: }
114:
115: private LayerListener layerListener = new LayerListener() {
116: public void categoryChanged(CategoryEvent e) {
117: }
118:
119: public void featuresChanged(FeatureEvent e) {
120: if (e.getLayer() != getLayer()) {
121: return;
122: }
123: if (e.getType() == FeatureEventType.DELETED) {
124: removeAll(e.getFeatures());
125: }
126: if (e.getType() == FeatureEventType.ATTRIBUTES_MODIFIED) {
127: for (Iterator i = e.getFeatures().iterator(); i
128: .hasNext();) {
129: Feature feature = (Feature) i.next();
130: int row = getFeatures().indexOf(feature);
131: if (row != -1) {
132: fireTableChanged(new TableModelEvent(
133: LayerTableModel.this , row, row));
134: }
135: }
136: }
137: }
138:
139: public void layerChanged(LayerEvent e) {
140: if (e.getLayerable() != getLayer()) {
141: return;
142: }
143:
144: if (e.getType() == LayerEventType.METADATA_CHANGED) {
145: //User may have changed the schema. [Jon Aquino]
146: if (!schema.equals(layer.getFeatureCollectionWrapper()
147: .getFeatureSchema(), true)) {
148: initColumns(layer);
149: fireTableChanged(new TableModelEvent(
150: LayerTableModel.this ,
151: TableModelEvent.HEADER_ROW));
152: }
153: }
154:
155: }
156: };
157:
158: private void initColumns(final Layer layer) {
159: schema = layer.getFeatureCollectionWrapper().getFeatureSchema();
160: ArrayList columns = new ArrayList();
161: columns.add(geomButtonColumn);
162: columns.add(fidColumn);
163:
164: for (int i = 0; i < schema.getAttributeCount(); i++) {
165: if (schema.getAttributeType(i) == AttributeType.GEOMETRY) {
166: continue;
167: }
168:
169: final int j = i;
170: columns.add(new MyColumn(schema.getAttributeName(i), schema
171: .getAttributeType(i).toJavaClass()) {
172: protected Object getValue(Feature feature) {
173: // MD - trapping bad index value here, since at this point it's too late to do anything about it
174: Object value = null;
175: try {
176: value = feature.getAttribute(j);
177: } catch (ArrayIndexOutOfBoundsException ex) {
178: ex.printStackTrace();
179: }
180: return value;
181: }
182:
183: protected void setValue(Object value,
184: final Feature feature) {
185: final Feature oldAttributes = (Feature) feature
186: .clone();
187: final Feature newAttributes = (Feature) feature
188: .clone();
189: newAttributes.setAttribute(j, value);
190: layer.getLayerManager().getUndoableEditReceiver()
191: .startReceiving();
192: try {
193: UndoableCommand command = new UndoableCommand(
194: I18N
195: .get("ui.plugin.LayerTableModel.edit")
196: + " "
197: + schema.getAttributeName(j)) {
198: public void execute() {
199: setAttributesOf(feature, newAttributes);
200: }
201:
202: public void unexecute() {
203: setAttributesOf(feature, oldAttributes);
204: }
205: };
206: command.execute();
207: layer.getLayerManager()
208: .getUndoableEditReceiver().receive(
209: command.toUndoableEdit());
210: } finally {
211: layer.getLayerManager()
212: .getUndoableEditReceiver()
213: .stopReceiving();
214: }
215: }
216: });
217: }
218: setColumns(columns);
219: }
220:
221: private void setAttributesOf(Feature feature, Feature attributes) {
222: // [UT] 25.08.2005 the old clone is available here but not used! so use it!
223: Feature oldClone = (Feature) feature.clone();
224: for (int i = 0; i < feature.getSchema().getAttributeCount(); i++) {
225: feature.setAttribute(i, attributes.getAttribute(i));
226: }
227: // remove this to include method with reference to old feature
228: /*layer.getLayerManager().fireFeaturesChanged(
229: Arrays.asList(new Feature[] { feature }),
230: FeatureEventType.ATTRIBUTES_MODIFIED,
231: layer);*/
232:
233: layer.getLayerManager().fireFeaturesAttChanged(
234: Arrays.asList(new Feature[] { feature }),
235: FeatureEventType.ATTRIBUTES_MODIFIED, layer,
236: Arrays.asList(new Feature[] { oldClone }));
237: }
238:
239: public Layer getLayer() {
240: return layer;
241: }
242:
243: public Feature getFeature(int row) {
244: return (Feature) features.get(row);
245: }
246:
247: public int getRowCount() {
248: return features.size();
249: }
250:
251: public boolean isCellEditable(int rowIndex, int columnIndex) {
252: if (!layer.isEditable()) {
253: return false;
254: }
255:
256: if (getColumn(columnIndex) == fidColumn) {
257: return false;
258: }
259:
260: if (getColumn(columnIndex) == geomButtonColumn) {
261: return false;
262: }
263:
264: return true;
265: }
266:
267: public void clear() {
268: features.clear();
269: fireTableChanged(new TableModelEvent(this ));
270: }
271:
272: public void removeAll(Collection featuresToRemove) {
273: for (Iterator i = featuresToRemove.iterator(); i.hasNext();) {
274: Feature feature = (Feature) i.next();
275: int row = features.indexOf(feature);
276: if (row == -1) {
277: //A LayerTableModel might not have all the features in a layer
278: //i.e. a FeatureInfo window, as opposed to a complete Attributes window. [Jon Aquino]
279: continue;
280: }
281: features.remove(row);
282: fireTableChanged(new TableModelEvent(this , row, row,
283: TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));
284: }
285: }
286:
287: public void addAll(Collection newFeatures) {
288: int originalFeaturesSize = features.size();
289: features.addAll(newFeatures);
290:
291: if (sortedColumnName != null) {
292: sort(sortedColumnName, sortAscending);
293: }
294:
295: fireTableChanged(new TableModelEvent(this ,
296: originalFeaturesSize, features.size() - 1,
297: TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
298: }
299:
300: /**
301: * Facilitate garbage collection by releasing references.
302: */
303: public void dispose() {
304: layer.getLayerManager().removeLayerListener(layerListener);
305: features.clear();
306: }
307:
308: public List getFeatures() {
309: return Collections.unmodifiableList(features);
310: }
311:
312: /**
313: * @return null if the table has not yet been sorted
314: */
315: public String getSortedColumnName() {
316: return sortedColumnName;
317: }
318:
319: public boolean isSortAscending() {
320: return sortAscending;
321: }
322:
323: public void sort(String columnName) {
324: sort(columnName,
325: columnName.equals(sortedColumnName) ? (!sortAscending)
326: : true);
327: }
328:
329: public void sort(final String columnName, final boolean ascending) {
330: this .sortAscending = ascending;
331: this .sortedColumnName = columnName;
332:
333: final int column = indexOfColumn(columnName);
334: Collections.sort(features, new Comparator() {
335: public int compare(Object o1, Object o2) {
336: return ascendingCompare(o1, o2)
337: * (ascending ? 1 : (-1));
338: }
339:
340: private int ascendingCompare(Object o1, Object o2) {
341: Feature f1 = (Feature) o1;
342: Feature f2 = (Feature) o2;
343:
344: Object v1 = ((MyColumn) getColumn(column)).getValue(f1);
345: Object v2 = ((MyColumn) getColumn(column)).getValue(f2);
346: return compareValue(v1, v2);
347: }
348: });
349: }
350:
351: private static int compareValue(Object o1, Object o2) {
352: if (o1 == null)
353: return -1;
354: if (o2 == null)
355: return 1;
356:
357: if (o1 instanceof Boolean) {
358: return compareBoolean((Boolean) o1, (Boolean) o2);
359: } else if (o1 instanceof Geometry) {
360: return 0; // for now - change to compare type
361: } else if (o1 instanceof Comparable) {
362: Comparable attribute1 = (Comparable) o1;
363: Comparable attribute2 = (Comparable) o2;
364: return attribute1.compareTo(attribute2);
365: }
366: return 0;
367: }
368:
369: private static int compareBoolean(Boolean b1, Boolean b2) {
370: boolean bool1 = b1.booleanValue();
371: boolean bool2 = b2.booleanValue();
372: if (bool1 == bool2)
373: return 0;
374: return bool1 ? 1 : -1;
375: }
376:
377: public String getType(int column) {
378: return null;
379: }
380:
381: public static void main(String[] args) {
382: System.out.println(new JTable().getDefaultEditor(Date.class));
383: }
384:
385: }
|