//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/ogcwebservices/wfs/operation/transaction/TransactionDocument.java,v 1.13 2006/11/27 12:22:05 poth Exp $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 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
Aennchenstraße 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.ogcwebservices.wfs.operation.transaction;
import java.net.URI;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.deegree.datatypes.QualifiedName;
import org.deegree.framework.xml.XMLParsingException;
import org.deegree.framework.xml.XMLTools;
import org.deegree.i18n.Messages;
import org.deegree.model.feature.Feature;
import org.deegree.model.feature.FeatureCollection;
import org.deegree.model.feature.FeatureFactory;
import org.deegree.model.feature.GMLFeatureCollectionDocument;
import org.deegree.model.feature.GMLFeatureDocument;
import org.deegree.model.filterencoding.AbstractFilter;
import org.deegree.model.filterencoding.Filter;
import org.deegree.ogcbase.CommonNamespaces;
import org.deegree.ogcbase.PropertyPath;
import org.deegree.ogcwebservices.InvalidParameterValueException;
import org.deegree.ogcwebservices.wfs.operation.AbstractWFSRequestDocument;
import org.deegree.ogcwebservices.wfs.operation.transaction.Insert.ID_GEN;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
/**
* Parser for "wfs:Transaction" requests and contained elements.
*
* @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
* @author last edited by: $Author: poth $
*
* @version $Revision: 1.13 $, $Date: 2006/11/27 12:22:05 $
*/
public class TransactionDocument extends AbstractWFSRequestDocument {
private static final long serialVersionUID = -394478447170286393L;
/**
* Parses the underlying document into a <code>Transaction</code> request object.
*
* @param id
* @return corresponding <code>Transaction</code> object
* @throws XMLParsingException
* @throws InvalidParameterValueException
*/
public Transaction parse( String id )
throws XMLParsingException, InvalidParameterValueException {
checkServiceAttribute();
String version = checkVersionAttribute();
Element root = this.getRootElement();
String lockId = XMLTools.getNodeAsString( root, "wfs:LockId/text()", nsContext, null );
boolean releaseAllFeatures = parseReleaseActionParameter();
List<TransactionOperation> operations = new ArrayList<TransactionOperation>();
List list = XMLTools.getNodes( root, "*", nsContext );
for ( int i = 0; i < list.size(); i++ ) {
Element element = (Element) list.get( i );
TransactionOperation operation = parseOperation( element );
operations.add( operation );
}
// vendorspecific attributes; required by deegree rights management
Map<String, String> vendorSpecificParams = parseDRMParams( root );
return new Transaction( id, version, vendorSpecificParams, lockId, operations,
releaseAllFeatures, this );
}
/**
* Parses the optional "releaseAction" attribute of the root element.
*
* @return true, if releaseAction equals "ALL" (or is left out), false if it equals "SOME"
* @throws InvalidParameterValueException if parameter
* @throws XMLParsingException
*/
private boolean parseReleaseActionParameter()
throws InvalidParameterValueException, XMLParsingException {
String releaseAction = XMLTools.getNodeAsString( getRootElement(), "@releaseAction",
nsContext, "ALL" );
boolean releaseAllFeatures = true;
if ( releaseAction != null ) {
if ( "SOME".equals( releaseAction ) ) {
releaseAllFeatures = false;
} else if ( "ALL".equals( releaseAction ) ) {
releaseAllFeatures = true;
} else {
throw new InvalidParameterValueException( "releaseAction", releaseAction );
}
}
return releaseAllFeatures;
}
/**
* Parses the given element as a <code>TransactionOperation</code>.
* <p>
* The given element must be one of the following:
* <ul>
* <li>wfs:Insert</li>
* <li>wfs:Update</li>
* <li>wfs:Delete</li>
* <li>wfs:Native</li>
* </ul>
*
* @param element operation element
* @return corresponding <code>TransactionOperation</code> object
* @throws XMLParsingException
*/
private TransactionOperation parseOperation( Element element )
throws XMLParsingException {
TransactionOperation operation = null;
if ( !element.getNamespaceURI().equals( CommonNamespaces.WFSNS.toString() ) ) {
String msg = Messages.getMessage( "WFS_INVALID_OPERATION", element.getNodeName() );
throw new XMLParsingException( msg );
}
if ( element.getLocalName().equals( "Insert" ) ) {
operation = parseInsert( element );
} else if ( element.getLocalName().equals( "Update" ) ) {
operation = parseUpdate( element );
} else if ( element.getLocalName().equals( "Delete" ) ) {
operation = parseDelete( element );
} else if ( element.getLocalName().equals( "Native" ) ) {
operation = parseNative( element );
} else {
String msg = Messages.getMessage( "WFS_INVALID_OPERATION", element.getNodeName() );
throw new XMLParsingException( msg );
}
return operation;
}
/**
* Parses the given element as a "wfs:Insert" operation.
*
* @param element "wfs:Insert" operation
* @return corresponding Insert object
* @throws XMLParsingException
*/
private Insert parseInsert( Element element )
throws XMLParsingException {
FeatureCollection fc = null;
ID_GEN mode = parseIdGen( element );
String handle = XMLTools.getNodeAsString( element, "@handle", nsContext, null );
URI srsName = XMLTools.getNodeAsURI( element, "@srsName", nsContext, null );
List childElementList = XMLTools.getRequiredNodes( element, "*", nsContext );
// either one _gml:FeatureCollection element or any number of _gml:Feature elements
boolean isFeatureCollection = isFeatureCollection( (Element) childElementList.get( 0 ) );
if ( isFeatureCollection ) {
LOG.logDebug( "Insert (FeatureCollection)" );
GMLFeatureCollectionDocument doc = new GMLFeatureCollectionDocument( false );
doc.setRootElement( (Element) childElementList.get( 0 ) );
doc.setSystemId( this.getSystemId() );
fc = doc.parse();
} else {
LOG.logDebug( "Insert (Features)" );
Feature[] features = new Feature[childElementList.size()];
for ( int i = 0; i < childElementList.size(); i++ ) {
try {
GMLFeatureDocument doc = new GMLFeatureDocument( false );
doc.setRootElement( (Element) childElementList.get( i ) );
doc.setSystemId( this.getSystemId() );
features[i] = doc.parseFeature();
} catch ( Exception e ) {
throw new XMLParsingException( e.getMessage(), e );
}
}
fc = FeatureFactory.createFeatureCollection( null, features );
}
Insert insert = new Insert( handle, mode, srsName, fc );
return insert;
}
/**
* Checks whether the given element is a (concrete) gml:_FeatureCollection element.
* <p>
* NOTE: This check is far from perfect. Instead of determining the type of the element by
* inspecting the schema, the decision is made by checking for child elements with name
* "gml:featureMember".
*
* @param element
* potential gml:_FeatureCollection element
* @return true, if the given element appears to be a gml:_FeatureCollection element, false
* otherwise
* @throws XMLParsingException
*/
private boolean isFeatureCollection( Element element )
throws XMLParsingException {
boolean containsFeatureCollection = false;
List nodeList = XMLTools.getNodes( element, "gml:featureMember", nsContext );
if ( nodeList.size() > 0 ) {
containsFeatureCollection = true;
}
return containsFeatureCollection;
}
/**
* Parses the optional "idGen" attribute of the given "wfs:Insert" element.
*
* @param element "wfs:Insert" element
* @return "idGen" attribute code
* @throws XMLParsingException
*/
private ID_GEN parseIdGen( Element element )
throws XMLParsingException {
ID_GEN mode;
String idGen = XMLTools.getNodeAsString( element, "@idgen", nsContext,
Insert.ID_GEN_GENERATE_NEW_STRING );
if ( Insert.ID_GEN_GENERATE_NEW_STRING.equals( idGen ) ) {
mode = Insert.ID_GEN.GENERATE_NEW;
} else if ( Insert.ID_GEN_USE_EXISTING_STRING.equals( idGen ) ) {
mode = Insert.ID_GEN.USE_EXISTING;
} else if ( Insert.ID_GEN_REPLACE_DUPLICATE_STRING.equals( idGen ) ) {
mode = Insert.ID_GEN.REPLACE_DUPLICATE;
} else {
String msg = Messages.getMessage( "WFS_INVALID_IDGEN_VALUE", idGen,
Insert.ID_GEN_GENERATE_NEW_STRING,
Insert.ID_GEN_REPLACE_DUPLICATE_STRING,
Insert.ID_GEN_USE_EXISTING_STRING );
throw new XMLParsingException( msg );
}
return mode;
}
/**
* Parses the given element as a "wfs:Update" operation.
*
* @param element "wfs:Update" operation
* @return corresponding Update object
* @throws XMLParsingException
*/
private Update parseUpdate( Element element )
throws XMLParsingException {
Update update = null;
String handle = XMLTools.getNodeAsString( element, "@handle", nsContext, null );
QualifiedName typeName = XMLTools.getRequiredNodeAsQualifiedName( element, "@typeName",
nsContext );
Element filterElement = (Element) XMLTools.getNode( element, "ogc:Filter", nsContext );
Filter filter = null;
if ( filterElement != null ) {
filter = AbstractFilter.buildFromDOM( filterElement );
}
List properties = XMLTools.getNodes( element, "wfs:Property", nsContext );
if ( properties.size() > 0 ) {
// "standard" update (specifies properties + their replacement values)
LOG.logDebug( "Update (replacement Properties)" );
Map<PropertyPath, Node> replacementProperties = new LinkedHashMap<PropertyPath, Node>();
for ( int i = 0; i < properties.size(); i++ ) {
Node propertyNode = (Node) properties.get( i );
Text propertyNameNode = (Text) XMLTools.getRequiredNode( propertyNode,
"wfs:Name/text()",
nsContext );
PropertyPath propertyName = parsePropertyPath( propertyNameNode );
// TODO improve this
Node valueNode = XMLTools.getNode( propertyNode, "wfs:Value/*", nsContext );
if ( valueNode == null ) {
valueNode = XMLTools.getNode( propertyNode, "wfs:Value/text()", nsContext );
}
if ( replacementProperties.get( propertyName ) != null ) {
String msg = Messages.getMessage( "WFS_UPDATE_DUPLICATE_PROPERTY", handle,
propertyName );
throw new XMLParsingException( msg );
}
replacementProperties.put( propertyName, valueNode );
}
update = new Update( handle, typeName, replacementProperties, filter );
} else {
// deegree specific update (specifies a single replacement feature)
LOG.logDebug( "Update (replacement Feature)" );
Feature replacementFeature = null;
List childElementList = XMLTools.getRequiredNodes( element, "*", nsContext );
if ( ( filter == null && childElementList.size() != 1 )
|| ( filter != null && childElementList.size() != 2 ) ) {
String msg = Messages.getMessage( "WFS_UPDATE_FEATURE_REPLACE", handle );
throw new XMLParsingException( msg );
}
try {
GMLFeatureDocument doc = new GMLFeatureDocument( false );
doc.setRootElement( (Element) childElementList.get( 0 ) );
doc.setSystemId( this.getSystemId() );
replacementFeature = doc.parseFeature();
} catch ( Exception e ) {
String msg = Messages.getMessage( "WFS_UPDATE_FEATURE_REPLACE", handle );
throw new XMLParsingException( msg );
}
update = new Update( handle, typeName, replacementFeature, filter );
}
return update;
}
/**
* Parses the given element as a "wfs:Delete" operation.
*
* @param element "wfs:Delete" operation
* @return corresponding Delete object
*/
private Delete parseDelete( Element element )
throws XMLParsingException {
String handle = XMLTools.getNodeAsString( element, "@handle", nsContext, null );
QualifiedName typeName = XMLTools.getRequiredNodeAsQualifiedName( element, "@typeName",
nsContext );
Element filterElement = (Element) XMLTools.getNode( element, "ogc:Filter", nsContext );
Filter filter = null;
if ( filterElement != null ) {
filter = AbstractFilter.buildFromDOM( filterElement );
}
return new Delete( handle, typeName, filter );
}
/**
* Parses the given element as a "wfs:Native" operation.
*
* @param element "wfs:Native" operation
* @return corresponding Native object
*/
private Native parseNative( Element element )
throws XMLParsingException {
String handle = XMLTools.getNodeAsString( element, "@handle", nsContext, null );
String vendorID = XMLTools.getRequiredNodeAsString( element, "@vendorId", nsContext );
boolean safeToIgnore = XMLTools.getRequiredNodeAsBoolean( element, "@safeToIgnore",
nsContext );
return new Native( handle, element, vendorID, safeToIgnore );
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: TransactionDocument.java,v $
Revision 1.13 2006/11/27 12:22:05 poth
support for vendorspecific parameters: user, password and sessionid added
Revision 1.12 2006/11/16 08:53:21 mschneider
Merged messages from org.deegree.ogcwebservices.wfs and its subpackages.
Revision 1.11 2006/11/07 11:09:36 mschneider
Added exceptions in case anything other than the 1.1.0 format is requested.
Revision 1.10 2006/10/05 16:58:55 poth
required changes to support none-GML formated inserts and updates
Revision 1.9 2006/09/14 00:01:20 mschneider
Little corrections + javadoc fixes.
Revision 1.8 2006/08/31 15:02:27 mschneider
Deactivated guessing of simple types.
Revision 1.7 2006/06/15 18:30:48 poth
*** empty log message ***
Revision 1.6 2006/06/07 17:19:40 mschneider
Delegated version parsing to AbstractWFSRequestDocument.parseVersion(). Renamed parseTransaction() to parse().
Revision 1.5 2006/06/06 17:09:02 mschneider
Moved checkServiceParameter() to super class.
Revision 1.4 2006/05/24 15:26:27 mschneider
Use LinkedHashMap to keep insertion order of the property names to be replaced.
Revision 1.3 2006/05/23 16:11:18 mschneider
More work on update.
Revision 1.2 2006/05/17 18:30:01 mschneider
Fixed error in parsing of "wfs:Update" elements.
Revision 1.1 2006/05/16 16:25:30 mschneider
Moved transaction related classes from org.deegree.ogcwebservices.wfs.operation to org.deegree.ogcwebservices.wfs.operation.transaction.
********************************************************************** */