001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/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 (Do, 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 }