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.plugin;
033:
034: import com.vividsolutions.jts.geom.Coordinate;
035: import com.vividsolutions.jts.geom.Geometry;
036: import com.vividsolutions.jts.geom.GeometryFactory;
037: import com.vividsolutions.jts.io.ParseException;
038: import com.vividsolutions.jts.io.WKTReader;
039: import com.vividsolutions.jts.util.Assert;
040:
041: import com.vividsolutions.jump.I18N;
042: import com.vividsolutions.jump.feature.AttributeType;
043: import com.vividsolutions.jump.feature.BasicFeature;
044: import com.vividsolutions.jump.feature.Feature;
045: import com.vividsolutions.jump.feature.FeatureDataset;
046: import com.vividsolutions.jump.feature.FeatureSchema;
047: import com.vividsolutions.jump.util.FlexibleDateParser;
048: import com.vividsolutions.jump.util.StringUtil;
049: import com.vividsolutions.jump.workbench.WorkbenchContext;
050: import com.vividsolutions.jump.workbench.model.CategoryEvent;
051: import com.vividsolutions.jump.workbench.model.FeatureEvent;
052: import com.vividsolutions.jump.workbench.model.Layer;
053: import com.vividsolutions.jump.workbench.model.LayerEvent;
054: import com.vividsolutions.jump.workbench.model.LayerEventType;
055: import com.vividsolutions.jump.workbench.model.LayerListener;
056: import com.vividsolutions.jump.workbench.model.LayerManager;
057: import com.vividsolutions.jump.workbench.model.LayerManagerProxy;
058: import com.vividsolutions.jump.workbench.model.Layerable;
059: import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn;
060: import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory;
061: import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck;
062: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
063: import com.vividsolutions.jump.workbench.ui.LayerNamePanel;
064: import com.vividsolutions.jump.workbench.ui.LayerNamePanelListener;
065: import com.vividsolutions.jump.workbench.ui.LayerNamePanelProxy;
066: import com.vividsolutions.jump.workbench.ui.SchemaPanel;
067: import com.vividsolutions.jump.workbench.ui.TreeLayerNamePanel;
068: import com.vividsolutions.jump.workbench.ui.WorkbenchFrame;
069: import com.vividsolutions.jump.workbench.ui.cursortool.editing.EditingPlugIn;
070: import com.vividsolutions.jump.workbench.ui.images.IconLoader;
071: import com.vividsolutions.jump.workbench.ui.renderer.style.ColorThemingStyle;
072:
073: import java.awt.BorderLayout;
074: import java.awt.event.ActionEvent;
075: import java.awt.event.ActionListener;
076:
077: import java.text.DateFormat;
078:
079: import java.util.ArrayList;
080: import java.util.Arrays;
081: import java.util.Collection;
082: import java.util.Date;
083: import java.util.Iterator;
084: import java.util.List;
085:
086: import javax.swing.ImageIcon;
087: import javax.swing.JInternalFrame;
088: import javax.swing.JOptionPane;
089: import javax.swing.event.InternalFrameAdapter;
090: import javax.swing.event.InternalFrameEvent;
091:
092: public class ViewSchemaPlugIn extends AbstractPlugIn {
093: private static final String KEY = ViewSchemaPlugIn.class
094: + " - FRAME";
095: private EditingPlugIn editingPlugIn;
096: private GeometryFactory factory = new GeometryFactory();
097: private WKTReader wktReader = new WKTReader(factory);
098: private FlexibleDateParser dateParser = new FlexibleDateParser();
099: private DateFormat dateFormatter = DateFormat.getDateInstance();
100:
101: public ViewSchemaPlugIn(EditingPlugIn editingPlugIn) {
102: this .editingPlugIn = editingPlugIn;
103: }
104:
105: public String getName() {
106: return I18N.get("ui.plugin.ViewSchemaPlugIn.view-edit-schema");
107: }
108:
109: private void applyChanges(final Layer layer, final SchemaPanel panel)
110: throws Exception {
111: if (!panel.isModified()) {
112: //User just pressed the Apply button even though he made no edits.
113: //Don't truncate the undo history; instead, exit. [Jon Aquino]
114: return;
115: }
116:
117: if (panel.validateInput() != null) {
118: throw new Exception(panel.validateInput());
119: }
120:
121: panel.getModel().removeBlankRows();
122:
123: FeatureSchema newSchema = new FeatureSchema();
124: //-- [sstein 10. Oct 2006] bugfix for colortheming by Ole
125: FeatureSchema oldSchema = layer.getFeatureCollectionWrapper()
126: .getFeatureSchema();
127: String attributeName = null;
128: //-- end
129:
130: for (int i = 0; i < panel.getModel().getRowCount(); i++) {
131: //-- [sstein 10. Oct 2006] bugfix for colortheming by Ole
132: attributeName = panel.getModel().get(i).getName();
133: newSchema.addAttribute(attributeName, panel.getModel().get(
134: i).getType());
135: if (oldSchema.hasAttribute(attributeName)
136: && !newSchema
137: .getAttributeType(attributeName)
138: .equals(
139: oldSchema
140: .getAttributeType(attributeName))) {
141: if (ColorThemingStyle.get(layer) != null) {
142: layer.removeStyle(ColorThemingStyle.get(layer));
143: layer.getBasicStyle().setEnabled(true);
144: layer.fireAppearanceChanged();
145: }
146: }
147: //-- END: added/modyfied by Ole
148: }
149:
150: List originalFeatures = layer.getFeatureCollectionWrapper()
151: .getFeatures();
152: ArrayList tempFeatures = new ArrayList();
153:
154: //Two-phase commit. Phase 1: check that no conversion errors occur. [Jon Aquino]
155: for (Iterator i = layer.getFeatureCollectionWrapper()
156: .iterator(); i.hasNext();) {
157: Feature feature = (Feature) i.next();
158: tempFeatures.add(convert(feature, panel, newSchema));
159: }
160:
161: //Phase 2: commit. [Jon Aquino]
162: for (int i = 0; i < originalFeatures.size(); i++) {
163: Feature originalFeature = (Feature) originalFeatures.get(i);
164: Feature tempFeature = (Feature) tempFeatures.get(i);
165:
166: //Modify existing features rather than creating new features, because
167: //there may be references to the existing features (e.g. Attribute Viewers).
168: //[Jon Aquino]
169: originalFeature.setSchema(tempFeature.getSchema());
170: originalFeature.setAttributes(tempFeature.getAttributes());
171: }
172:
173: //Non-undoable. [Jon Aquino]
174: layer.getLayerManager().getUndoableEditReceiver()
175: .getUndoManager().discardAllEdits();
176: layer.setFeatureCollection(new FeatureDataset(originalFeatures,
177: newSchema));
178: layer.fireLayerChanged(LayerEventType.METADATA_CHANGED);
179: panel.markAsUnmodified();
180: }
181:
182: private Feature convert(Feature oldFeature, SchemaPanel panel,
183: FeatureSchema newSchema) throws ConversionException {
184: Feature newFeature = new BasicFeature(newSchema);
185:
186: for (int i = 0; i < panel.getModel().getRowCount(); i++) {
187: if (panel.getModel().get(i).getOriginalIndex() == -1) {
188: newFeature
189: .setAttribute(
190: i,
191: (panel.getModel().get(i).getType() == AttributeType.GEOMETRY) ? oldFeature
192: .getGeometry()
193: : null);
194: } else {
195: newFeature.setAttribute(i, convert(oldFeature
196: .getAttribute(panel.getModel().get(i)
197: .getOriginalIndex()), oldFeature
198: .getSchema().getAttributeType(
199: panel.getModel().get(i)
200: .getOriginalIndex()),
201: newFeature.getSchema().getAttributeType(i),
202: panel.getModel().get(i).getName(), panel
203: .isForcingInvalidConversionsToNull()));
204: }
205: }
206:
207: return newFeature;
208: }
209:
210: private String limitLength(String s) {
211: //Limit length of values reported in error messages -- WKT is potentially large.
212: //[Jon Aquino]
213: return StringUtil.limitLength(s, 30);
214: }
215:
216: private Object convert(Object oldValue, AttributeType oldType,
217: AttributeType newType, String name,
218: boolean forcingInvalidConversionsToNull)
219: throws ConversionException {
220: try {
221: if (oldValue == null) {
222: return (newType == AttributeType.GEOMETRY) ? factory
223: .createPoint((Coordinate) null) : null;
224: }
225:
226: if (oldType == AttributeType.STRING) {
227: String oldString = (String) oldValue;
228:
229: if (newType == AttributeType.STRING) {
230: return oldString;
231: }
232:
233: if (newType == AttributeType.INTEGER) {
234: try {
235: return new Integer(oldString);
236: } catch (NumberFormatException e) {
237: throw new ConversionException(
238: I18N
239: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-integer")
240: + " \""
241: + limitLength(oldValue
242: .toString())
243: + "\" ("
244: + name + ")");
245: }
246: }
247:
248: if (newType == AttributeType.DOUBLE) {
249: try {
250: return new Double(oldString);
251: } catch (NumberFormatException e) {
252: throw new ConversionException(
253: I18N
254: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-double")
255: + " \""
256: + limitLength(oldValue
257: .toString())
258: + "\" ("
259: + name + ")");
260: }
261: }
262:
263: if (newType == AttributeType.GEOMETRY) {
264: try {
265: return wktReader.read(oldString);
266: } catch (ParseException e) {
267: throw new ConversionException(
268: I18N
269: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-geometry")
270: + " \""
271: + limitLength(oldValue
272: .toString())
273: + "\" ("
274: + name + ")");
275: }
276: }
277:
278: if (newType == AttributeType.DATE) {
279: try {
280: return dateParser.parse(oldString, false);
281: } catch (java.text.ParseException e) {
282: throw new ConversionException(
283: I18N
284: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-date")
285: + " \""
286: + limitLength(oldValue
287: .toString())
288: + "\" ("
289: + name + ")");
290: }
291: }
292: }
293:
294: if (oldType == AttributeType.INTEGER) {
295: int oldInt = ((Integer) oldValue).intValue();
296:
297: if (newType == AttributeType.STRING) {
298: return "" + oldInt;
299: }
300:
301: if (newType == AttributeType.INTEGER) {
302: return oldValue;
303: }
304:
305: if (newType == AttributeType.DOUBLE) {
306: return new Double(oldInt);
307: }
308:
309: if (newType == AttributeType.GEOMETRY) {
310: throw new ConversionException(
311: I18N
312: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-geometry")
313: + " \""
314: + limitLength(oldValue.toString())
315: + "\" (" + name + ")");
316: }
317:
318: if (newType == AttributeType.DATE) {
319: try {
320: return dateParser.parse("" + oldInt, false);
321: } catch (java.text.ParseException e) {
322: throw new ConversionException(
323: I18N
324: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-date")
325: + " \""
326: + limitLength(oldValue
327: .toString())
328: + "\" ("
329: + name + ")");
330: }
331: }
332: }
333:
334: if (oldType == AttributeType.DOUBLE) {
335: double oldDouble = ((Double) oldValue).doubleValue();
336:
337: if (newType == AttributeType.STRING) {
338: return "" + oldDouble;
339: }
340:
341: if (newType == AttributeType.INTEGER) {
342: return new Integer((int) oldDouble);
343: }
344:
345: if (newType == AttributeType.DOUBLE) {
346: return oldValue;
347: }
348:
349: if (newType == AttributeType.GEOMETRY) {
350: throw new ConversionException(
351: I18N
352: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-geometry")
353: + " \""
354: + limitLength(oldValue.toString())
355: + "\" (" + name + ")");
356: }
357:
358: if (newType == AttributeType.DATE) {
359: throw new ConversionException(
360: I18N
361: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-date")
362: + " \""
363: + limitLength(oldValue.toString())
364: + "\" (" + name + ")");
365: }
366: }
367:
368: if (oldType == AttributeType.GEOMETRY) {
369: Geometry oldGeometry = (Geometry) oldValue;
370:
371: if (newType == AttributeType.STRING) {
372: return oldGeometry.toString();
373: }
374:
375: if (newType == AttributeType.INTEGER) {
376: throw new ConversionException(
377: I18N
378: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-integer")
379: + " \""
380: + limitLength(oldValue.toString())
381: + "\" (" + name + ")");
382: }
383:
384: if (newType == AttributeType.DOUBLE) {
385: throw new ConversionException(
386: I18N
387: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-double")
388: + " \""
389: + limitLength(oldValue.toString())
390: + "\" (" + name + ")");
391: }
392:
393: if (newType == AttributeType.GEOMETRY) {
394: return oldGeometry;
395: }
396:
397: if (newType == AttributeType.DATE) {
398: throw new ConversionException(
399: I18N
400: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-date")
401: + " \""
402: + limitLength(oldValue.toString())
403: + "\" (" + name + ")");
404: }
405: }
406:
407: if (oldType == AttributeType.DATE) {
408: Date oldDate = (Date) oldValue;
409:
410: if (newType == AttributeType.STRING) {
411: return dateFormatter.format(oldDate);
412: }
413:
414: if (newType == AttributeType.INTEGER) {
415: return new Integer((int) oldDate.getTime());
416: }
417:
418: if (newType == AttributeType.DOUBLE) {
419: return new Double(oldDate.getTime());
420: }
421:
422: if (newType == AttributeType.GEOMETRY) {
423: throw new ConversionException(
424: I18N
425: .get("ui.plugin.ViewSchemaPlugIn.cannot-convert-to-geometry")
426: + " \""
427: + limitLength(oldValue.toString())
428: + "\" (" + name + ")");
429: }
430:
431: if (newType == AttributeType.DATE) {
432: return oldValue;
433: }
434: }
435:
436: Assert.shouldNeverReachHere(newType.toString());
437:
438: return null;
439: } catch (ConversionException e) {
440: if (forcingInvalidConversionsToNull) {
441: return (newType == AttributeType.GEOMETRY) ? factory
442: .createPoint((Coordinate) null) : null;
443: }
444:
445: throw e;
446: }
447: }
448:
449: private void commitEditsInProgress(final SchemaPanel panel) {
450: //Skip if nothing is being edited, otherwise may get false positive. [Jon Aquino]
451: if (panel.getTable().getEditingRow() != -1) {
452: //If user is in the middle of editing a field name, call stopCellEditing
453: //so that new field name is committed (if valid) or an error is recorded
454: //(if invalid). [Jon Aquino]
455: panel.getTable().getCellEditor(
456: panel.getTable().getEditingRow(),
457: panel.getTable().getEditingColumn())
458: .stopCellEditing();
459: }
460: }
461:
462: public boolean execute(PlugInContext context) throws Exception {
463: reportNothingToUndoYet(context);
464:
465: //Can't simply use Blackboard#get(key, default) because default requires that
466: //we create a new EditSchemaFrame, and we don't want to do this unless we
467: //have to because the EditSchemaFrame constructor modifies the blackboard.
468: //Result: Executing this plug-in twice creates two frames, even if we don't close
469: //the first. [Jon Aquino]
470: if (frame(context) == null) {
471: context
472: .getSelectedLayer(0)
473: .getBlackboard()
474: .put(
475: KEY,
476: new EditSchemaFrame(context
477: .getWorkbenchFrame(), context
478: .getSelectedLayer(0), editingPlugIn));
479: }
480:
481: frame(context).surface();
482:
483: return true;
484: }
485:
486: private EditSchemaFrame frame(PlugInContext context) {
487: return (EditSchemaFrame) context.getSelectedLayer(0)
488: .getBlackboard().get(KEY);
489: }
490:
491: public static MultiEnableCheck createEnableCheck(
492: final WorkbenchContext workbenchContext) {
493: EnableCheckFactory checkFactory = new EnableCheckFactory(
494: workbenchContext);
495:
496: return new MultiEnableCheck()
497: .add(
498: checkFactory
499: .createWindowWithLayerNamePanelMustBeActiveCheck())
500: .add(
501: checkFactory
502: .createExactlyNLayersMustBeSelectedCheck(1));
503: }
504:
505: private static class ConversionException extends Exception {
506: public ConversionException(String message) {
507: super (message);
508: }
509: }
510:
511: public static final ImageIcon ICON = IconLoader.icon("Object.gif");
512:
513: private class EditSchemaFrame extends JInternalFrame implements
514: LayerNamePanelProxy, LayerNamePanel, LayerManagerProxy {
515: private LayerManager layerManager;
516: private Layer layer;
517: private WorkbenchFrame workbenchFrame;
518:
519: public EditSchemaFrame(final WorkbenchFrame workbenchFrame,
520: final Layer layer, EditingPlugIn editingPlugIn) {
521: this .layer = layer;
522: this .workbenchFrame = workbenchFrame;
523: layer.getBlackboard().put(KEY, this );
524:
525: this .layerManager = layer.getLayerManager();
526: addInternalFrameListener(new InternalFrameAdapter() {
527: public void internalFrameClosed(InternalFrameEvent e) {
528: layer.getBlackboard().put(KEY, null);
529: }
530: });
531:
532: final SchemaPanel panel = new SchemaPanel(layer,
533: editingPlugIn, workbenchFrame.getContext());
534: setResizable(true);
535: setClosable(true);
536: setMaximizable(true);
537: setIconifiable(true);
538: getContentPane().setLayout(new BorderLayout());
539: getContentPane().add(panel, BorderLayout.CENTER);
540: setSize(500, 300);
541: updateTitle(layer);
542: layer.getLayerManager().addLayerListener(
543: new LayerListener() {
544: public void categoryChanged(CategoryEvent e) {
545: }
546:
547: public void featuresChanged(FeatureEvent e) {
548: }
549:
550: public void layerChanged(LayerEvent e) {
551: updateTitle(layer);
552: }
553: });
554: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
555: panel.add(new ActionListener() {
556: public void actionPerformed(ActionEvent e) {
557: try {
558: commitEditsInProgress(panel);
559: applyChanges(layer, panel);
560: } catch (Exception x) {
561: workbenchFrame.handleThrowable(x);
562: }
563: }
564: });
565: addInternalFrameListener(new InternalFrameAdapter() {
566: public void internalFrameClosing(InternalFrameEvent e) {
567: commitEditsInProgress(panel);
568:
569: if (!layer.isEditable() || !panel.isModified()) {
570: dispose();
571:
572: return;
573: }
574:
575: switch (JOptionPane
576: .showConfirmDialog(
577: EditSchemaFrame.this ,
578: I18N
579: .get("ui.plugin.ViewSchemaPlugIn.apply-changes-to-schema"),
580: "JUMP",
581: JOptionPane.YES_NO_CANCEL_OPTION,
582: JOptionPane.WARNING_MESSAGE)) {
583: case JOptionPane.YES_OPTION:
584:
585: try {
586: applyChanges(layer, panel);
587: } catch (Exception x) {
588: workbenchFrame.handleThrowable(x);
589:
590: return;
591: }
592:
593: dispose();
594:
595: return;
596:
597: case JOptionPane.NO_OPTION:
598: dispose();
599:
600: return;
601:
602: case JOptionPane.CANCEL_OPTION:
603: return;
604:
605: default:
606: Assert.shouldNeverReachHere();
607: }
608: }
609: });
610: }
611:
612: private void updateTitle(Layer layer) {
613: setTitle((layer.isEditable() ? I18N
614: .get("ui.plugin.ViewSchemaPlugIn.edit") : I18N
615: .get("ui.plugin.ViewSchemaPlugIn.view"))
616: + " "
617: + I18N.get("ui.plugin.ViewSchemaPlugIn.schema")
618: + ": " + layer.getName());
619: }
620:
621: public LayerManager getLayerManager() {
622: return layerManager;
623: }
624:
625: public Layer chooseEditableLayer() {
626: return TreeLayerNamePanel.chooseEditableLayer(this );
627: }
628:
629: public void surface() {
630: if (!workbenchFrame.hasInternalFrame(this )) {
631: workbenchFrame.addInternalFrame(this , false, true);
632: }
633:
634: workbenchFrame.activateFrame(this );
635: moveToFront();
636: }
637:
638: public LayerNamePanel getLayerNamePanel() {
639: return this ;
640: }
641:
642: public Collection getSelectedCategories() {
643: return new ArrayList();
644: }
645:
646: public Layer[] getSelectedLayers() {
647: return new Layer[] { layer };
648: }
649:
650: public Collection selectedNodes(Class c) {
651: if (!Layerable.class.isAssignableFrom(c)) {
652: return new ArrayList();
653: }
654:
655: return Arrays.asList(getSelectedLayers());
656: }
657:
658: public void addListener(LayerNamePanelListener listener) {
659: }
660:
661: public void removeListener(LayerNamePanelListener listener) {
662: }
663: }
664: }
|