001 //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/sql/transaction/delete/FeatureGraph.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.io.datastore.sql.transaction.delete;
037
038 import java.util.ArrayList;
039 import java.util.HashMap;
040 import java.util.HashSet;
041 import java.util.List;
042 import java.util.Map;
043 import java.util.Set;
044
045 import org.deegree.framework.log.ILogger;
046 import org.deegree.framework.log.LoggerFactory;
047 import org.deegree.i18n.Messages;
048 import org.deegree.io.datastore.DatastoreException;
049 import org.deegree.io.datastore.FeatureId;
050 import org.deegree.io.datastore.schema.MappedFeaturePropertyType;
051 import org.deegree.io.datastore.sql.AbstractRequestHandler;
052 import org.deegree.io.datastore.sql.LockHandler;
053 import org.deegree.ogcwebservices.wfs.operation.transaction.Delete;
054
055 /**
056 * Used to determine the structure of features and subfeatures that have to be deleted by a {@link Delete} operation.
057 * <p>
058 * When features are selected for deletion, all of their descendant subfeatures will be deleted as well, except for
059 * those features that have superfeatures which are not descendants of the features to be deleted.
060 *
061 * @see DeleteHandler
062 * @see FeatureNode
063 *
064 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
065 * @author last edited by: $Author: mschneider $
066 *
067 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Thu, 18 Jun 2009) $
068 */
069 public class FeatureGraph {
070
071 private static final ILogger LOG = LoggerFactory.getLogger( FeatureGraph.class );
072
073 private AbstractRequestHandler handler;
074
075 private Set<FeatureId> rootFeatures = new HashSet<FeatureId>();
076
077 private Map<FeatureId, FeatureNode> fidToNode = new HashMap<FeatureId, FeatureNode>();
078
079 /**
080 * Creates a new <code>FeatureGraph</code> instance for the given root {@link FeatureId}s and the specified
081 * {@link DeleteHandler}.
082 *
083 * @param rootFids
084 * ids of the feature instances to be deleted
085 * @param handler
086 * associated <code>DeleteHandler</code>
087 * @throws DatastoreException
088 */
089 FeatureGraph( List<FeatureId> rootFids, DeleteHandler handler ) throws DatastoreException {
090 this.handler = handler;
091 for ( FeatureId fid : rootFids ) {
092 this.rootFeatures.add( fid );
093 addNode( fid );
094 }
095 markUndeletableFeatures();
096 }
097
098 /**
099 * Creates a new <code>FeatureGraph</code> instance for the given root {@link FeatureId}s and the specified
100 * {@link LockHandler}.
101 *
102 * @param rootFids
103 * ids of the feature instances to be locked
104 * @param handler
105 * associated <code>LockHandler</code>
106 * @throws DatastoreException
107 */
108 public FeatureGraph( Set<FeatureId> rootFids, LockHandler handler ) throws DatastoreException {
109 this.handler = handler;
110 for ( FeatureId fid : rootFids ) {
111 this.rootFeatures.add( fid );
112 addNode( fid );
113 }
114 }
115
116 /**
117 * Returns the {@link FeatureId}s of all features contained in the graph.
118 *
119 * @return the <code>FeatureId</code>s of all features contained in the graph
120 */
121 public Set<FeatureId> getAllFids() {
122 return this.fidToNode.keySet();
123 }
124
125 /**
126 * Returns the {@link FeatureNode} that represents the feature with the given {@link FeatureId}.
127 *
128 * @param fid
129 * id of the feature to look up
130 * @return the corresponding <code>FeatureNode</code> if it exists in the graph, null otherwise
131 */
132 FeatureNode getNode( FeatureId fid ) {
133 return fidToNode.get( fid );
134 }
135
136 /**
137 * Returns the {@link FeatureNode}s that represent all root features that have been targeted for deletion.
138 *
139 * @return <code>FeatureNode</code> representing all root features
140 */
141 List<FeatureNode> getRootNodes() {
142 List<FeatureNode> rootNodes = new ArrayList<FeatureNode>( this.rootFeatures.size() );
143 for ( FeatureId fid : rootFeatures ) {
144 rootNodes.add( getNode( fid ) );
145 }
146 return rootNodes;
147 }
148
149 /**
150 * Adds the specified feature (and it's descendants) (as {@link FeatureNode}s).
151 *
152 * @param fid
153 * the id of the feature to be added
154 * @throws DatastoreException
155 */
156 private void addNode( FeatureId fid )
157 throws DatastoreException {
158
159 if ( this.fidToNode.get( fid ) == null ) {
160 // skip determination of super features if feature is not deletable anyway
161 Set<FeatureId> superFeatures = null;
162 if ( fid.getFeatureType().isDeletable() ) {
163 superFeatures = handler.determineSuperFeatures( fid );
164 } else {
165 LOG.logDebug( "Skipping super feature lookup for feature: " + fid
166 + " -- feature type is not deletable anyway." );
167 superFeatures = new HashSet<FeatureId>();
168 superFeatures.add (fid);
169 }
170
171 Map<MappedFeaturePropertyType, List<FeatureId>> subFeatures = handler.determineSubFeatures( fid );
172 FeatureNode node = new FeatureNode( this, fid, subFeatures, superFeatures );
173 this.fidToNode.put( fid, node );
174
175 for ( FeatureId subFid : node.getSubFeatureIds() ) {
176 addNode( subFid );
177 }
178 }
179 }
180
181 /**
182 * Marks all {@link FeatureNode}s in the graph that cannot be deleted, because they are descendants (subfeatures)
183 * of other undeletable {@link FeatureNode}s.
184 */
185 private void markUndeletableFeatures() {
186
187 LOG.logDebug( "Determining undeletable features." );
188
189 int lastVetoCount = -1;
190 int vetoCount = 0;
191
192 while ( vetoCount != lastVetoCount ) {
193 LOG.logDebug( "vetoCount: " + vetoCount );
194 LOG.logDebug( "lastVetoCount: " + lastVetoCount );
195 lastVetoCount = vetoCount;
196 vetoCount = 0;
197 for ( FeatureNode node : this.fidToNode.values() ) {
198 if ( node.isDeletable() ) {
199 boolean deletable = true;
200 if ( !node.getFid().getFeatureType().isDeletable() ) {
201 String msg = Messages.getMessage( "DATASTORE_FEATURE_NOT_DELETABLE", node.getFid().toString() );
202 LOG.logInfo( msg );
203 deletable = false;
204 } else {
205 for ( FeatureId superFid : node.getSuperFeatureIds() ) {
206 FeatureNode superNode = getNode( superFid );
207 if ( superNode == null || !superNode.isDeletable() ) {
208 deletable = false;
209 break;
210 }
211 }
212 }
213 if ( !deletable ) {
214 node.markAsUndeletable();
215 vetoCount++;
216 }
217 } else {
218 vetoCount++;
219 }
220 }
221 }
222 LOG.logDebug( "Delete vetos: " + lastVetoCount );
223 }
224
225 @Override
226 public String toString() {
227 StringBuffer sb = new StringBuffer( "FeatureGraph:\n" );
228 for ( FeatureId rootFid : this.rootFeatures ) {
229 sb.append( this.fidToNode.get( rootFid ).toString( "", new HashSet<FeatureNode>() ) );
230 }
231 return sb.toString();
232 }
233 }