/* * 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.plugin.clipboard; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.ImageIcon; import javax.swing.JComponent; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jump.coordsys.Reprojector; import com.vividsolutions.jump.feature.AttributeType; import com.vividsolutions.jump.feature.BasicFeature; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureSchema; import com.vividsolutions.jump.io.WKTReader; import com.vividsolutions.jump.util.StringUtil; import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.model.UndoableCommand; import com.vividsolutions.jump.workbench.plugin.AbstractPlugIn; import com.vividsolutions.jump.workbench.plugin.EnableCheck; import com.vividsolutions.jump.workbench.plugin.EnableCheckFactory; import com.vividsolutions.jump.workbench.plugin.MultiEnableCheck; import com.vividsolutions.jump.workbench.plugin.PlugInContext; import com.vividsolutions.jump.workbench.ui.GUIUtil; import com.vividsolutions.jump.workbench.ui.images.famfam.IconLoaderFamFam; /** * Lets user paste items from the clipboard. */ public class PasteItemsPlugIn extends AbstractPlugIn { private WKTReader reader = new WKTReader(); private static final String DECIMAL_PATTERN = "\\d+(?:\\.\\d+)?"; private static final String WHITESPACE_OR_COMMA = "(?:\\s+|(?:\\s*,\\s*))"; private static final Pattern pointCoordsPattern = Pattern.compile("\\s*\\(?\\s*(" + DECIMAL_PATTERN + ")" + WHITESPACE_OR_COMMA + "(" + DECIMAL_PATTERN + ")(?:" + WHITESPACE_OR_COMMA + "(" + DECIMAL_PATTERN + "))?\\s*\\)?\\s*"); // Note: Need to copy the data twice: once when the user hits Copy, so she is // free to modify the original afterwards, and again when the user hits Paste, // so she is free to modify the first copy then hit Paste again. [Jon Aquino] public PasteItemsPlugIn() { } public static final ImageIcon ICON = IconLoaderFamFam.icon("paste_plain.png"); public String getNameWithMnemonic() { return StringUtil.replace(getName(), "P", "&P", false); } public boolean execute(final PlugInContext context) throws Exception { reportNothingToUndoYet(context); Collection features; Transferable transferable = GUIUtil.getContents(Toolkit.getDefaultToolkit() .getSystemClipboard()); if (transferable.isDataFlavorSupported(CollectionOfFeaturesTransferable.COLLECTION_OF_FEATURES_FLAVOR)) { features = (Collection)GUIUtil.getContents( Toolkit.getDefaultToolkit().getSystemClipboard()).getTransferData( CollectionOfFeaturesTransferable.COLLECTION_OF_FEATURES_FLAVOR); } else { // Allow the user to paste features using WKT. [Jon Aquino] String value = (String)transferable.getTransferData(DataFlavor.stringFlavor); features = processCoordinates(value); if (features.isEmpty()) { features = reader.read(new StringReader(value)).getFeatures(); } } final Layer layer = context.getSelectedLayer(0); final Collection featureCopies = conform(features, layer.getFeatureCollectionWrapper().getFeatureSchema()); execute(new UndoableCommand(getName()) { public void execute() { layer.getFeatureCollectionWrapper().addAll(featureCopies); } public void unexecute() { layer.getFeatureCollectionWrapper().removeAll(featureCopies); } }, context); return true; } private Collection<Feature> processCoordinates(String value) { Matcher matcher = pointCoordsPattern.matcher(value); Collection<Feature> features = new ArrayList<Feature>(); if (matcher.find() && matcher.start() == 0) { do { double x = Double.parseDouble(matcher.group(1)); double y = Double.parseDouble(matcher.group(2)); Coordinate coordinate = new Coordinate(x, y); String zString = matcher.group(3); if (zString != null) { coordinate.z = Double.parseDouble(zString); } FeatureSchema featureSchema = new FeatureSchema(); featureSchema.addAttribute("Geometry", AttributeType.GEOMETRY); Feature feature = new BasicFeature(featureSchema); Point point = new GeometryFactory().createPoint(coordinate); feature.setGeometry(point); features.add(feature); } while (matcher.find()); } return features; } public static Collection conform(Collection features, FeatureSchema targetFeatureSchema) { final ArrayList featureCopies = new ArrayList(); for (Iterator i = features.iterator(); i.hasNext();) { Feature feature = (Feature)i.next(); featureCopies.add(conform(feature, targetFeatureSchema)); } return featureCopies; } private static Feature conform(Feature original, FeatureSchema targetFeatureSchema) { // Transfer as many attributes as possible, matching on name. [Jon Aquino] Feature copy = new BasicFeature(targetFeatureSchema); copy.setGeometry((Geometry)original.getGeometry().clone()); for (int i = 0; i < original.getSchema().getAttributeCount(); i++) { if (i == original.getSchema().getGeometryIndex()) { continue; } String attributeName = original.getSchema().getAttributeName(i); if (!copy.getSchema().hasAttribute(attributeName)) { continue; } if (copy.getSchema().getAttributeType(attributeName) != original.getSchema() .getAttributeType(attributeName)) { continue; } copy.setAttribute(attributeName, original.getAttribute(attributeName)); } Reprojector.instance().reproject(copy.getGeometry(), original.getSchema().getCoordinateSystem(), copy.getSchema().getCoordinateSystem()); return copy; } public static MultiEnableCheck createEnableCheck( final WorkbenchContext workbenchContext) { EnableCheckFactory checkFactory = new EnableCheckFactory(workbenchContext); return new MultiEnableCheck().add( checkFactory.createWindowWithLayerNamePanelMustBeActiveCheck()).add( checkFactory.createExactlyNLayersMustBeSelectedCheck(1)).add( checkFactory.createSelectedLayersMustBeEditableCheck()).add( new EnableCheck() { public String check(JComponent component) { Transferable transferable = GUIUtil.getContents(Toolkit.getDefaultToolkit() .getSystemClipboard()); if (transferable == null) { return "Clipboard must not be empty"; } if (transferable.isDataFlavorSupported(CollectionOfFeaturesTransferable.COLLECTION_OF_FEATURES_FLAVOR)) { return null; } try { if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { String value = (String)transferable.getTransferData(DataFlavor.stringFlavor); if (isWKT(value) || isCoordinates(value)) { return null; } } } catch (Exception e) { workbenchContext.getErrorHandler().handleThrowable(e); } return "Clipboard must contain geometries or Well-Known Text"; } private boolean isCoordinates(String value) { return pointCoordsPattern.matcher(value).find(); } private boolean isWKT(String s) { try { new WKTReader().read(new StringReader(s)); return true; } catch (Exception e) { return false; } } }); } }