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 }