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: package com.vividsolutions.jump.workbench.ui.renderer.style;
033:
034: import java.util.ArrayList;
035: import java.util.Collections;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Map;
039: import java.util.TreeMap;
040: import java.util.TreeSet;
041:
042: import javax.swing.event.TableModelEvent;
043: import javax.swing.table.TableModel;
044:
045: import com.vividsolutions.jump.I18N;
046: import com.vividsolutions.jump.feature.FeatureSchema;
047: import com.vividsolutions.jump.util.Block;
048: import com.vividsolutions.jump.util.CollectionUtil;
049: import com.vividsolutions.jump.util.ImmutableFirstElementList;
050: import com.vividsolutions.jump.workbench.ui.ColumnBasedTableModel;
051:
052: public class ColorThemingTableModel extends ColumnBasedTableModel {
053:
054: public ColorThemingTableModel(BasicStyle defaultStyle,
055: String attributeName, Map attributeValueToBasicStyleMap,
056: Map attributeValueToLabelMap, FeatureSchema schema) {
057: //Value doesn't matter. [Jon Aquino]
058: attributeMappings = new ImmutableFirstElementList(
059: new AttributeMapping(null, defaultStyle, null));
060: this .attributeName = attributeName;
061: setMaps(attributeValueToBasicStyleMap, attributeValueToLabelMap);
062: setColumns(createColumns(schema));
063: }
064:
065: public static final int COLOR_COLUMN = 0;
066: public static final int ATTRIBUTE_COLUMN = 1;
067: public static final int LABEL_COLUMN = 2;
068:
069: public void setMaps(Map attributeValueToBasicStyleMap,
070: Map attributeValueToLabelMap) {
071: attributeMappings.clear();
072: for (Iterator i = attributeValueToBasicStyleMap.keySet()
073: .iterator(); i.hasNext();) {
074: Object attributeValue = i.next();
075: attributeMappings.add(new AttributeMapping(attributeValue,
076: (BasicStyle) attributeValueToBasicStyleMap
077: .get(attributeValue),
078: (String) attributeValueToLabelMap
079: .get(attributeValue)));
080: }
081: fireTableChanged(new TableModelEvent(this ));
082: }
083:
084: protected static class AttributeMapping implements Comparable {
085: private String label;
086:
087: public AttributeMapping(Object attributeValue,
088: BasicStyle basicStyle, String label) {
089: this .attributeValue = attributeValue;
090: this .basicStyle = basicStyle;
091: this .label = label;
092: }
093:
094: private Object attributeValue;
095: private BasicStyle basicStyle;
096:
097: public Object getAttributeValue() {
098: return attributeValue;
099: }
100:
101: public BasicStyle getBasicStyle() {
102: return basicStyle;
103: }
104:
105: public int compareTo(Object o) {
106: AttributeMapping other = (AttributeMapping) o;
107: if (attributeValue == null) {
108: return -1;
109: }
110: if (other.attributeValue == null) {
111: return 1;
112: }
113: return ((Comparable) attributeValue)
114: .compareTo((Comparable) other.attributeValue);
115: }
116:
117: public void setAttributeValue(Object object) {
118: attributeValue = object;
119: }
120:
121: public void setBasicStyle(BasicStyle style) {
122: basicStyle = style;
123: }
124:
125: protected String getLabel() {
126: return label;
127: }
128:
129: protected void setLabel(String label) {
130: this .label = label;
131: }
132: }
133:
134: public void clear() {
135: attributeMappings.clear();
136: fireTableChanged(new TableModelEvent(this ));
137: }
138:
139: public boolean containsNullAttributeValues() {
140: for (Iterator i = nonDefaultAttributeMappings().iterator(); i
141: .hasNext();) {
142: AttributeMapping attributeMapping = (AttributeMapping) i
143: .next();
144: if (attributeMapping.getAttributeValue() == null) {
145: return true;
146: }
147: }
148: return false;
149: }
150:
151: protected AttributeMapping attributeMapping(int i) {
152: return (AttributeMapping) attributeMappings.get(i);
153: }
154:
155: public BasicStyle getDefaultStyle() {
156: return attributeMapping(0).getBasicStyle();
157: }
158:
159: public Object findDuplicateAttributeValue() {
160: TreeSet set = new TreeSet();
161: for (Iterator i = nonDefaultAttributeMappings().iterator(); i
162: .hasNext();) {
163: AttributeMapping attributeMapping = (AttributeMapping) i
164: .next();
165: if (attributeMapping.getAttributeValue() == null) {
166: //Check nulls elsewhere. TreeSet won't accept nulls. [Jon Aquino]
167: continue;
168: }
169: if (set.contains(attributeMapping.getAttributeValue())) {
170: return attributeMapping.getAttributeValue();
171: }
172: set.add(attributeMapping.getAttributeValue());
173: }
174: return null;
175: }
176:
177: //Can't use TreeMap because attributes may not be unique (this is
178: //an invalid state of course and we won't let the user hit OK until he
179: //resolves it). Can't use HashMap because Geometry doesn't implement
180: //#hash. [Jon Aquino]
181: protected List attributeMappings;
182:
183: protected List createColumns(final FeatureSchema schema) {
184: ArrayList columns = new ArrayList();
185: columns
186: .add(new Column(
187: I18N
188: .get("ui.renderer.style.ColorThemingTableModel.attribute-value"),
189: BasicStyle.class) {
190: public Object getValueAt(int rowIndex) {
191: return attributeMapping(rowIndex)
192: .getBasicStyle();
193: }
194:
195: public void setValueAt(Object value, int rowIndex) {
196: attributeMapping(rowIndex).setBasicStyle(
197: (BasicStyle) value);
198: fireTableChanged(new TableModelEvent(
199: ColorThemingTableModel.this , rowIndex));
200: }
201: });
202: columns
203: .add(new Column(
204: I18N
205: .get("ui.renderer.style.ColorThemingTableModel.attribute-value"),
206: null) {
207: public Class getDataClass() {
208: //attributeName will be null if layer has no non-spatial attributes
209: //[Jon Aquino]
210: //attributeName will not be present in the schema if the user
211: //has deleted this attribute. [Jon Aquino]
212: return attributeName == null
213: || !schema.hasAttribute(attributeName) ? Object.class
214: : schema
215: .getAttributeType(attributeName)
216: .toJavaClass();
217: }
218:
219: public Object getValueAt(int rowIndex) {
220: return attributeMapping(rowIndex)
221: .getAttributeValue();
222: }
223:
224: public void setValueAt(Object value, int rowIndex) {
225: attributeMapping(rowIndex).setAttributeValue(
226: value);
227: //The validators need to know that the table has changed. [Jon Aquino]
228: fireTableChanged(new AttributeValueTableModelEvent(
229: ColorThemingTableModel.this , rowIndex));
230: }
231: });
232: columns.add(new Column("Label", String.class) {
233: public Object getValueAt(int rowIndex) {
234: return attributeMapping(rowIndex).getLabel();
235: }
236:
237: public void setValueAt(Object value, int rowIndex) {
238: attributeMapping(rowIndex).setLabel((String) value);
239: fireTableChanged(new AttributeValueTableModelEvent(
240: ColorThemingTableModel.this , rowIndex));
241: }
242: });
243: return columns;
244: }
245:
246: public static class AttributeValueTableModelEvent extends
247: TableModelEvent {
248: public AttributeValueTableModelEvent(TableModel source, int row) {
249: super (source, row);
250: }
251:
252: }
253:
254: public void apply(ColorScheme colorScheme,
255: boolean skipDefaultAttributeMapping) {
256: //Leave the first element out of the sort, because it's the "(All other values)"
257: //element. [Jon Aquino]
258: for (Iterator i = (skipDefaultAttributeMapping ? nonDefaultAttributeMappings()
259: : attributeMappings).iterator(); i.hasNext();) {
260: AttributeMapping attributeMapping = (AttributeMapping) i
261: .next();
262: attributeMapping.setBasicStyle(new BasicStyle(colorScheme
263: .next()));
264: }
265: fireTableChanged(new TableModelEvent(this ));
266: }
267:
268: public int getRowCount() {
269: return attributeMappings.size();
270: }
271:
272: public void setAttributeName(String attributeName) {
273: this .attributeName = attributeName;
274: }
275:
276: public Map getAttributeValueToBasicStyleMap() {
277: return attributeValueToObjectMap(new Block() {
278: public Object yield(Object attributeMapping) {
279: return ((AttributeMapping) attributeMapping)
280: .getBasicStyle();
281: }
282: });
283: }
284:
285: public Map getAttributeValueToLabelMap() {
286: return attributeValueToObjectMap(new Block() {
287: public Object yield(Object attributeMapping) {
288: return ((AttributeMapping) attributeMapping).getLabel();
289: }
290: });
291: }
292:
293: private Map attributeValueToObjectMap(Block getter) {
294: TreeMap attributeValueToObjectMap = new TreeMap();
295: //Skip the first element, which is the default style. [Jon Aquino]
296: for (Iterator i = nonDefaultAttributeMappings().iterator(); i
297: .hasNext();) {
298: AttributeMapping attributeMapping = (AttributeMapping) i
299: .next();
300: attributeValueToObjectMap.put(attributeMapping
301: .getAttributeValue(), getter
302: .yield(attributeMapping));
303: }
304: return attributeValueToObjectMap;
305: }
306:
307: private boolean lastSortAscending = true;
308:
309: public boolean wasLastSortAscending() {
310: return lastSortAscending;
311: }
312:
313: public void sort() {
314: sort(!lastSortAscending);
315: }
316:
317: public void sort(boolean ascending) {
318: //Leave the first element out of the sort, because it's the "(All other values)"
319: //element. [Jon Aquino]
320: if (ascending) {
321: Collections.sort(nonDefaultAttributeMappings());
322: } else {
323: Collections.sort(nonDefaultAttributeMappings(), Collections
324: .reverseOrder());
325: }
326: lastSortAscending = ascending;
327: fireTableChanged(new TableModelEvent(this ));
328: }
329:
330: public void removeAttributeValues(int[] rows) {
331: for (Iterator i = CollectionUtil.reverseSortedSet(rows)
332: .iterator(); i.hasNext();) {
333: Integer row = (Integer) i.next();
334: attributeMappings.remove(row.intValue());
335: fireTableChanged(new TableModelEvent(this , row.intValue(),
336: row.intValue(), TableModelEvent.ALL_COLUMNS,
337: TableModelEvent.DELETE));
338: }
339: }
340:
341: /**
342: * @return row
343: */
344: public int insertAttributeValue(int row, ColorScheme colorScheme) {
345: attributeMappings.add(row, new AttributeMapping(null,
346: new BasicStyle(colorScheme.next()), ""));
347: fireTableChanged(new TableModelEvent(this , row, row,
348: TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
349: return row;
350: }
351:
352: protected String attributeName;
353:
354: public boolean isCellEditable(int rowIndex, int columnIndex) {
355: //Any cell except the one that says "(All other values)" [Jon Aquino]
356: if (rowIndex == 0 && columnIndex == ATTRIBUTE_COLUMN) {
357: return false;
358: }
359: if (rowIndex == 0 && columnIndex == LABEL_COLUMN) {
360: return false;
361: }
362: return true;
363: }
364:
365: protected List nonDefaultAttributeMappings() {
366: return attributeMappings.subList(1, attributeMappings.size());
367: }
368:
369: }
|