//$HeadURL$
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2008 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.igeo.modules;
import static java.awt.datatransfer.DataFlavor.stringFlavor;
import static java.util.Collections.singletonList;
import static java.util.UUID.randomUUID;
import static org.deegree.framework.log.LoggerFactory.getLogger;
import static org.deegree.framework.xml.XMLFragment.DEFAULT_URL;
import static org.deegree.igeo.i18n.Messages.get;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import org.deegree.datatypes.Types;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.util.Pair;
import org.deegree.framework.util.StringTools;
import org.deegree.framework.xml.XMLException;
import org.deegree.framework.xml.XMLParsingException;
import org.deegree.igeo.commands.UnselectFeaturesCommand;
import org.deegree.igeo.commands.digitize.InsertFeatureCommand;
import org.deegree.igeo.config.LayerType.MetadataURL;
import org.deegree.igeo.dataadapter.DataAccessAdapter;
import org.deegree.igeo.dataadapter.DataAccessFactory;
import org.deegree.igeo.dataadapter.FeatureAdapter;
import org.deegree.igeo.i18n.Messages;
import org.deegree.igeo.mapmodel.Datasource;
import org.deegree.igeo.mapmodel.Layer;
import org.deegree.igeo.mapmodel.MapModel;
import org.deegree.igeo.modules.ActionDescription.ACTIONTYPE;
import org.deegree.igeo.modules.DefaultMapModule.SelectedFeaturesVisitor;
import org.deegree.igeo.settings.ClipboardOptions;
import org.deegree.igeo.views.DialogFactory;
import org.deegree.model.Identifier;
import org.deegree.model.feature.Feature;
import org.deegree.model.feature.FeatureCollection;
import org.deegree.model.feature.FeatureException;
import org.deegree.model.feature.FeatureFactory;
import org.deegree.model.feature.FeatureProperty;
import org.deegree.model.feature.GMLFeatureAdapter;
import org.deegree.model.feature.GMLFeatureCollectionDocument;
import org.deegree.model.feature.schema.FeatureType;
import org.deegree.model.feature.schema.PropertyType;
import org.deegree.model.spatialschema.Geometry;
import org.deegree.model.spatialschema.GeometryException;
import org.deegree.model.spatialschema.WKTAdapter;
import org.xml.sax.SAXException;
/**
* Module for handling copy and paste of features from one layer to another
*
* @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
* @param <T>
*/
public class CopyPasteModule<T> extends DefaultModule<T> {
private static final ILogger LOG = getLogger( CopyPasteModule.class );
static {
ActionDescription ad1 = new ActionDescription( "clearClipboard", "removes all content from clip board", null,
"clear clip board", ACTIONTYPE.PushButton, null, null );
ActionDescription ad2 = new ActionDescription( "copy",
"copies selected features as GML3 objects into clip board",
null, "copies selected features", ACTIONTYPE.PushButton, null,
null );
ActionDescription ad3 = new ActionDescription( "copyAsWKT", "copies selected features as WKT into clip board",
null, "copies selected features as WKT", ACTIONTYPE.PushButton,
null, null );
ActionDescription ad4 = new ActionDescription( "paste", "pastes features from clip board into selected layer",
null, "pastes features from clip board", ACTIONTYPE.PushButton,
null, null );
moduleCapabilities = new ModuleCapabilities( ad1, ad2, ad3, ad4 );
}
/**
*
*/
public void selectedFeaturesToLayer() {
String mmId = getInitParameter( "assignedMapModel" );
MapModel mm = appContainer.getMapModel( new Identifier( mmId ) );
SelectedFeaturesVisitor visitor = new SelectedFeaturesVisitor( -1 );
try {
mm.walkLayerTree( visitor );
if ( visitor.col.size() > 0 ) {
FeatureCollection col = FeatureFactory.createFeatureCollection( "UUID_" + randomUUID().toString(),
visitor.col.size() );
col.setFeatureType( visitor.col.getFeature( 0 ).getFeatureType() );
for ( int i = 0; i < visitor.col.size(); ++i ) {
col.add( visitor.col.getFeature( i ).cloneDeep() );
}
String name = DialogFactory.openInputDialog( appContainer.getViewPlatform(), getViewForm(), get( "$MD10558" ),
get( "$MD10559" ) );
if ( name == null ) {
return;
}
Datasource ds = DataAccessFactory.createDatasource( name, col );
Layer layer = new Layer( mm, new Identifier( name ), name, name, singletonList( ds ),
Collections.<MetadataURL> emptyList() );
appContainer.getCommandProcessor().executeSychronously( new UnselectFeaturesCommand( mm, false ), true );
Layer selLayer = mm.getLayersSelectedForAction( MapModel.SELECTION_ACTION ).get( 0 );
mm.insert( layer, selLayer.getParent(), selLayer, false );
layer.fireRepaintEvent();
}
} catch ( Exception e ) {
LOG.logError( "Unknown error", e );
DialogFactory.openErrorDialog( appContainer.getViewPlatform(), null, Messages.get( "$MD11213" ),
Messages.get( "$MD11214" ), e );
}
}
/**
*
*/
public void copyAsWKT() {
if ( "application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) {
int max = appContainer.getSettings().getClipboardOptions().getMaxObjects();
MapModel mm = appContainer.getMapModel( null );
SelectedFeaturesVisitor visitor = new SelectedFeaturesVisitor( max );
try {
mm.walkLayerTree( visitor );
} catch ( Exception e ) {
LOG.logError( "Unknown error", e );
return;
}
try {
StringBuffer wkt = new StringBuffer();
for ( int i = 0; i < visitor.col.size(); ++i ) {
Geometry geom = visitor.col.getFeature( i ).getDefaultGeometryPropertyValue();
wkt.append( WKTAdapter.export( geom ) );
if ( i != visitor.col.size() - 1 ) {
wkt.append( ";\n" );
}
}
// use both clip boards for text?
Clipboard clip = Toolkit.getDefaultToolkit().getSystemSelection();
if ( clip != null ) {
clip.setContents( new StringSelection( wkt.toString() ), null );
}
clip = Toolkit.getDefaultToolkit().getSystemClipboard();
clip.setContents( new StringSelection( wkt.toString() ), null );
} catch ( GeometryException e ) {
LOG.logError( "Unknown error", e );
}
}
}
/**
*
*/
public void paste() {
if ( "application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) {
MapModel mm = appContainer.getMapModel( null );
Toolkit tk = Toolkit.getDefaultToolkit();
if ( !addFeaturesFromClipboard( tk.getSystemClipboard(), mm ) ) {
addFeaturesFromClipboard( tk.getSystemSelection(), mm );
}
}
}
/**
* removes content from clipboard
*/
public void clearClipboard() {
if ( "application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) {
Toolkit tk = Toolkit.getDefaultToolkit();
tk.getSystemClipboard().setContents( new StringSelection( "" ), null );
}
}
private boolean addCompleteFeaturesFromClipboard( String trans, MapModel mm ) {
GMLFeatureCollectionDocument doc = new GMLFeatureCollectionDocument();
try {
doc.load( new StringReader( trans ), DEFAULT_URL );
FeatureCollection col = doc.parse();
doc = null; // could be freed now in case of memory problems
boolean asked = false;
boolean yes = false;
for ( Layer l : mm.getLayersSelectedForAction( MapModel.SELECTION_ACTION ) ) {
for ( int i = 0; i < col.size(); ++i ) {
Feature f = col.getFeature( i );
Pair<FeatureType, InsertFeatureCommand> pair = insertFeature( l, f );
if ( pair.first != null && pair.first.equals( f.getFeatureType() ) ) { // this is implemented on a
// name comparison basis
// only, but I say it's close enough...
try {
appContainer.getCommandProcessor().executeSychronously( pair.second, true );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
DialogFactory.openErrorDialog( appContainer.getViewPlatform(), null,
Messages.get( "$MD11215" ), Messages.get( "$MD11216" ), e );
}
} else {
if ( ( asked && yes )
|| ( !asked && DialogFactory.openConfirmDialogYESNO( appContainer.getViewPlatform(), getViewForm(),
get( "$MD10555" ), get( "$DI10019" ) ) ) ) {
asked = true;
yes = true;
InsertFeatureCommand newFeature = insertFeature( l, f.getDefaultGeometryPropertyValue() );
try {
appContainer.getCommandProcessor().executeSychronously( newFeature, true );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
DialogFactory.openErrorDialog( appContainer.getViewPlatform(), null,
Messages.get( "$MD11217" ), Messages.get( "$MD11218" ),
e );
}
} else {
asked = true;
}
}
}
}
} catch ( SAXException e ) {
// no GML either. message?
LOG.logWarning( "no GML either.", e );
} catch ( IOException e ) {
// should not happen, a string is a string
LOG.logWarning( "should not happen, a string is a string", e );
} catch ( XMLParsingException e ) {
// no GML either. message?
LOG.logWarning( "no GML either.", e );
}
return false;
// I'll leave this here for later use. Maybe someone'd like a better copy/paste mechanism one day.
// // let's try it with the featuresFlavor
// boolean inserted = false;
//
// try {
// Collection<?> col = (Collection<?>) trans.getTransferData( FeaturesSelection.featuresFlavor );
// for ( Object o : col ) {
// if ( o instanceof Feature ) {
// for ( Layer l : mm.getLayersSelectedForAction( MapModel.SELECTION_ACTION ) ) {
// appContainer.getCommandProcessor().addCommand( insertFeature( l, (Feature) o ) );
// inserted = true;
// }
// }
// }
//
// return inserted;
// } catch ( UnsupportedFlavorException e ) {
// LOG.logDebug( "Stack trace: ", e );
// // this time it's final
// } catch ( IOException e ) {
// LOG.logDebug( "Stack trace: ", e );
// // probably ignore, or message for no data
// // unsure when this will actually happen
// }
//
// return false;
}
private boolean addFeaturesFromClipboard( Clipboard clip, MapModel mm ) {
if ( clip == null ) {
return false;
}
Transferable trans = clip.getContents( null );
String str = null;
try {
str = (String) trans.getTransferData( stringFlavor );
LOG.logDebug( "String from Clipboard: ", str );
if ( str == null ) {
// return addCompleteFeaturesFromClipboard( trans, mm );
return false;
}
String[] s = StringTools.toArray( str, ";", false );
for ( String string : s ) {
if ( string != null && string.length() > 5 ) {
Geometry geom = WKTAdapter.wrap( string, mm.getCoordinateSystem() );
for ( Layer l : mm.getLayersSelectedForAction( MapModel.SELECTION_ACTION ) ) {
try {
appContainer.getCommandProcessor().executeSychronously( insertFeature( l, geom ), true );
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
DialogFactory.openErrorDialog( appContainer.getViewPlatform(), null,
Messages.get( "$MD11219" ), Messages.get( "$MD11220a", l ),
e );
}
}
}
}
return true;
} catch ( UnsupportedFlavorException e ) {
LOG.logDebug( "Stack trace: ", e );
// return addCompleteFeaturesFromClipboard( trans, mm ); // comment this in again if the method can cope
// with different flavors
} catch ( IOException e ) {
LOG.logDebug( "Stack trace: ", e );
// probably ignore, or message for no data
// unsure when this will actually happen
} catch ( GeometryException e ) {
LOG.logDebug( "Stack trace: ", e );
return addCompleteFeaturesFromClipboard( str, mm );
}
return false;
}
private static InsertFeatureCommand insertFeature( Layer layer, Geometry geometry ) {
for ( DataAccessAdapter adapter : layer.getDataAccess() ) {
if ( adapter instanceof FeatureAdapter ) {
FeatureAdapter fa = (FeatureAdapter) adapter;
FeatureType ft = fa.getSchema();
LinkedList<FeatureProperty> props = new LinkedList<FeatureProperty>();
PropertyType[] pts = ft.getProperties();
for ( PropertyType pt : pts ) {
if ( pt.getType() == Types.GEOMETRY ) {
props.add( FeatureFactory.createFeatureProperty( pt.getName(), geometry ) );
} else {
for ( int i = 0; i < pt.getMinOccurs(); ++i ) {
switch ( pt.getType() ) {
case Types.VARCHAR:
props.add( FeatureFactory.createFeatureProperty( pt.getName(), "string" ) );
case Types.INTEGER:
props.add( FeatureFactory.createFeatureProperty( pt.getName(), 0 ) );
case Types.DATE:
props.add( FeatureFactory.createFeatureProperty( pt.getName(), new Date() ) );
}
// TODO add more types with default values, or find a better solution to create a blank new
// feature
}
}
}
return new InsertFeatureCommand( adapter, FeatureFactory.createFeature( randomUUID().toString(), ft,
props ) );
}
}
return null;
}
private static Pair<FeatureType, InsertFeatureCommand> insertFeature( Layer layer, Feature feature ) {
for ( DataAccessAdapter adapter : layer.getDataAccess() ) {
if ( adapter instanceof FeatureAdapter ) {
return new Pair<FeatureType, InsertFeatureCommand>( ( (FeatureAdapter) adapter ).getSchema(),
new InsertFeatureCommand( adapter, feature ) );
}
}
return null;
}
/**
*
*/
public void copy() {
if ( "application".equalsIgnoreCase( appContainer.getViewPlatform() ) ) {
ClipboardOptions opts = appContainer.getSettings().getClipboardOptions();
String format = opts.getFormat();
String mmId = getInitParameter( "assignedMapModel" );
MapModel mm = appContainer.getMapModel( new Identifier( mmId ) );
SelectedFeaturesVisitor visitor = new SelectedFeaturesVisitor( opts.getMaxObjects() );
try {
mm.walkLayerTree( visitor );
} catch ( Exception e ) {
LOG.logError( "Unknown error", e );
return;
}
if ( format.equalsIgnoreCase( "text/xml; subtype=gml/3.1.1" ) ) {
// LinkedList<Feature> features = new LinkedList<Feature>();
// for ( int i = 0; i < visitor.col.size(); ++i ) {
// features.add( visitor.col.getFeature( i ) );
// }
// format or no format?
String doc;
try {
doc = new GMLFeatureAdapter().export( visitor.col ).getAsPrettyString();
// use both clip boards for text?
Clipboard clip = Toolkit.getDefaultToolkit().getSystemSelection();
if ( clip != null ) {
clip.setContents( new StringSelection( doc ), null );
}
clip = Toolkit.getDefaultToolkit().getSystemClipboard();
clip.setContents( new StringSelection( doc ), null );
} catch ( XMLException e ) {
LOG.logError( "Unknown error", e );
} catch ( IOException e ) {
LOG.logError( "Unknown error", e );
} catch ( FeatureException e ) {
LOG.logError( "Unknown error", e );
} catch ( SAXException e ) {
LOG.logError( "Unknown error", e );
}
}
}
}
}