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    }