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