001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/quadtree/DBNode.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2007 by:
006     EXSE, 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     Aennchenstr. 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.quadtree;
044    
045    import java.sql.Connection;
046    import java.sql.PreparedStatement;
047    import java.sql.ResultSet;
048    import java.sql.Statement;
049    import java.util.List;
050    
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    import org.deegree.io.DBConnectionPool;
054    import org.deegree.io.JDBCConnection;
055    import org.deegree.model.spatialschema.Envelope;
056    import org.deegree.model.spatialschema.GeometryFactory;
057    
058    /**
059     * Represents a node of a {@link DBQuadtree}. Nodes contain items which have a spatial extent
060     * corresponding to the node's position in the quadtree.
061     * 
062     * 
063     * @version $Revision: 7845 $
064     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
065     * @author last edited by: $Author: apoth $
066     * 
067     * @version 1.0. $Revision: 7845 $, $Date: 2007-07-25 09:45:35 +0200 (Mi, 25 Jul 2007) $
068     * 
069     * @since 2.0
070     */
071    class DBNode implements Node {
072    
073        private static ILogger LOG = LoggerFactory.getLogger( DBNode.class );
074    
075        private String id = null;
076    
077        private int level;
078    
079        private String[] fk_subnode = new String[4];
080    
081        private Envelope envelope = null;
082    
083        private JDBCConnection jdbc = null;
084    
085        private DBQuadtree qt = null;
086    
087        private String indexName = null;
088    
089        /**
090         * 
091         * @param id
092         * @param jdbc
093         */
094        public DBNode( String id, Envelope env, DBQuadtree qt, String indexName, JDBCConnection jdbc, int level )
095                                throws IndexException {
096            this.id = id;
097            this.envelope = env;
098            this.jdbc = jdbc;
099            this.qt = qt;
100            this.level = level;
101            this.indexName = indexName.trim();
102            if ( !load() ) {
103                create();
104            }
105        }
106    
107        /*
108         * (non-Javadoc)
109         * 
110         * @see org.deegree.io.quadtree.Node#getId()
111         */
112        public String getId() {
113            return id;
114        }
115    
116        Envelope getEnvelope() {
117            return envelope;
118        }
119    
120        /*
121         * (non-Javadoc)
122         * 
123         * @see org.deegree.io.quadtree.Node#insert(java.lang.Object,
124         *      org.deegree.model.spatialschema.Envelope)
125         */
126        public void insert( Object item, Envelope itemEnv )
127                                throws IndexException {
128            if ( level != qt.getDepth() ) {
129                if ( !envelope.intersects( itemEnv ) ) {
130                    System.out.println( "node envelope: " + envelope );
131                    System.out.println( "item envelope: " + itemEnv );
132                    throw new IndexException( "item envelope does not intersects node envelope" );
133                }
134                // split the envelope of this node into four equal sized quarters
135                Envelope[] envs = split();
136                boolean inter = false;
137                int k = 0;
138                for ( int i = 0; i < envs.length; i++ ) {
139                    if ( envs[i].intersects( itemEnv ) ) {
140                        k++;
141                        // check which subnodes are intersected by the
142                        // items envelope; just this nodes
143                        // are considered for futher processing
144                        if ( fk_subnode[i] == null || fk_subnode[i].trim().length() == 0 ) {
145                            inter = true;
146                            fk_subnode[i] = id + '_' + i;
147                        }
148                        Node node = qt.getFromCache( fk_subnode[i] );
149                        if ( node == null ) {
150                            node = new DBNode( fk_subnode[i], envs[i], qt, indexName, jdbc, level + 1 );
151                            qt.addToCache( node );
152                        }
153                        node.insert( item, itemEnv );
154                    }
155                }
156                if ( k == 4 ) {
157                    assigneItem( item );
158                }
159                qt.addToCache( this );
160                if ( inter ) {
161                    update();
162                }
163            } else {
164                assigneItem( item );
165            }
166    
167        }
168    
169        /*
170         * (non-Javadoc)
171         * 
172         * @see org.deegree.io.quadtree.Node#query(org.deegree.model.spatialschema.Envelope,
173         *      java.util.List, int)
174         */
175        public List<Object> query( Envelope searchEnv, List<Object> visitor, int level )
176                                throws IndexException {
177            /*
178             * if ( level == qt.getDepth() || (searchEnv.getWidth() > envelope.getWidth() ||
179             * searchEnv.getHeight() > envelope.getHeight()) ) { addAssignedItems( visitor ); } else {
180             */
181            addAssignedItems( visitor );
182            if ( level != qt.getDepth() ) {
183                Envelope[] envs = split();
184                for ( int i = 0; i < envs.length; i++ ) {
185                    if ( fk_subnode[i] != null && envs[i].intersects( searchEnv ) ) {
186                        // check which subnodes are intersected by the
187                        // items envelope; just this nodes
188                        // are considered for futher processing
189                        Node node = new DBNode( fk_subnode[i], envs[i], qt, indexName, jdbc, level + 1 );
190                        node.query( searchEnv, visitor, level + 1 );
191                    }
192                }
193            }
194            return visitor;
195        }
196    
197        /*
198         * (non-Javadoc)
199         * 
200         * @see org.deegree.io.quadtree.Node#deleteItem(java.lang.Object)
201         */
202        public void deleteItem( Object item ) {
203            if ( level == qt.getDepth() ) {
204    
205            } else {
206    
207            }
208        }
209    
210        /*
211         * (non-Javadoc)
212         * 
213         * @see org.deegree.io.quadtree.Node#deleteRange(org.deegree.model.spatialschema.Envelope)
214         */
215        public void deleteRange( Envelope envelope ) {
216            if ( level == qt.getDepth() ) {
217    
218            } else {
219    
220            }
221        }
222    
223        /**
224         * load all parameter from of node from the database returns true is a node with current ID is
225         * already available from the database
226         * 
227         */
228        private boolean load()
229                                throws IndexException {
230            Connection con = null;
231            DBConnectionPool pool = null;
232            boolean available = true;
233            try {
234                pool = DBConnectionPool.getInstance();
235                con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
236    
237                StringBuffer sb = new StringBuffer( 100 );
238                sb.append( "Select * from " ).append( indexName );
239                sb.append( " where ID = '" ).append( id ).append( "'" );
240    
241                Statement stmt = con.createStatement();
242                ResultSet rs = stmt.executeQuery( sb.toString() );
243                if ( rs.next() ) {
244                    double minx = rs.getFloat( "MINX" );
245                    double miny = rs.getFloat( "MINY" );
246                    double maxx = rs.getFloat( "MAXX" );
247                    double maxy = rs.getFloat( "MAXY" );
248                    envelope = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, null );
249                    fk_subnode[0] = rs.getString( "FK_SUBNODE1" );
250                    fk_subnode[1] = rs.getString( "FK_SUBNODE2" );
251                    fk_subnode[2] = rs.getString( "FK_SUBNODE3" );
252                    fk_subnode[3] = rs.getString( "FK_SUBNODE4" );
253                } else {
254                    available = false;
255                }
256                rs.close();
257                stmt.close();
258            } catch ( Exception e ) {
259                LOG.logError( e.getMessage(), e );
260                throw new IndexException( "could not load node definition from database", e );
261            } finally {
262                try {
263                    pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
264                } catch ( Exception e1 ) {
265                    e1.printStackTrace();
266                }
267            }
268            return available;
269        }
270    
271        /**
272         * updates the database representation of the current node
273         * 
274         * @throws IndexException
275         */
276        private void update()
277                                throws IndexException {
278            Connection con = null;
279            DBConnectionPool pool = null;
280            try {
281                pool = DBConnectionPool.getInstance();
282                con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
283    
284                StringBuffer sb = new StringBuffer( 100 );
285                sb.append( "UPDATE " ).append( indexName ).append( " set " );
286                boolean sub = false;
287                for ( int i = 0; i < fk_subnode.length; i++ ) {
288                    if ( fk_subnode[i] != null ) {
289                        sb.append( " FK_SUBNODE" ).append( i + 1 ).append( "='" );
290                        sb.append( fk_subnode[i] ).append( "' ," );
291                        sub = true;
292                    }
293                }
294                if ( sub ) {
295                    // just execute update if at least one sub node != null
296                    sb = new StringBuffer( sb.substring( 0, sb.length() - 1 ) );
297                    sb.append( " where ID = '" ).append( id ).append( "'" );
298                    Statement stmt = con.createStatement();
299                    stmt.execute( sb.toString() );
300                    stmt.close();
301                }
302            } catch ( Exception e ) {
303                LOG.logError( e.getMessage(), e );
304                throw new IndexException( "could not update node definition at database " + "for node: " + id, e );
305            } finally {
306                try {
307                    pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
308                } catch ( Exception e1 ) {
309                    e1.printStackTrace();
310                }
311            }
312        }
313    
314        /**
315         * creates a new node with current ID and envelope
316         * 
317         * @throws IndexException
318         */
319        void create()
320                                throws IndexException {
321            Connection con = null;
322            DBConnectionPool pool = null;
323            try {
324                pool = DBConnectionPool.getInstance();
325                con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
326    
327                StringBuffer sb = new StringBuffer( 100 );
328                sb.append( "INSERT INTO " ).append( indexName );
329                sb.append( " ( ID, MINX, MINY, MAXX , MAXY ) " );
330                sb.append( "VALUES ( ?, ?, ?, ?, ? ) " );
331                PreparedStatement stmt = con.prepareStatement( sb.toString() );
332                stmt.setString( 1, id );
333                stmt.setFloat( 2, (float) envelope.getMin().getX() );
334                stmt.setFloat( 3, (float) envelope.getMin().getY() );
335                stmt.setFloat( 4, (float) envelope.getMax().getX() );
336                stmt.setFloat( 5, (float) envelope.getMax().getY() );
337                stmt.execute();
338                stmt.close();
339            } catch ( Exception e ) {
340                LOG.logError( e.getMessage(), e );
341                throw new IndexException( "could not create node definition at database", e );
342            } finally {
343                try {
344                    pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
345                } catch ( Exception e1 ) {
346                    e1.printStackTrace();
347                }
348            }
349        }
350    
351        /**
352         * assignes an item to a node by creating a new row in the JT_QTNODE_ITEM table
353         * 
354         * @param Item
355         */
356        private void assigneItem( Object item )
357                                throws IndexException {
358            Connection con = null;
359            DBConnectionPool pool = null;
360            try {
361                pool = DBConnectionPool.getInstance();
362                con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
363    
364                StringBuffer sb = new StringBuffer( 100 );
365                sb.append( "INSERT INTO " ).append( indexName.trim() ).append( "_ITEM " );
366                sb.append( "( FK_QTNODE, FK_ITEM ) " ).append( "VALUES ( ?, ? ) " );
367                PreparedStatement stmt = con.prepareStatement( sb.toString() );
368                stmt.setString( 1, id );
369                if ( item instanceof Integer ) {
370                    stmt.setInt( 2, (Integer) item );
371                } else {
372                    stmt.setString( 2, item.toString() );
373                }
374                stmt.execute();
375                stmt.close();
376            } catch ( Exception e ) {
377                LOG.logError( e.getMessage(), e );
378                throw new IndexException( "could not create node definition at database", e );
379            } finally {
380                try {
381                    pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
382                } catch ( Exception e1 ) {
383                    e1.printStackTrace();
384                }
385            }
386        }
387    
388        /**
389         * adds all item(IDs) assigned to this node
390         * 
391         * @param visitor
392         * @return
393         * @throws IndexException
394         */
395        private List addAssignedItems( List<Object> visitor )
396                                throws IndexException {
397    
398            Connection con = null;
399            DBConnectionPool pool = null;
400            try {
401                pool = DBConnectionPool.getInstance();
402                con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
403    
404                StringBuffer sb = new StringBuffer( 100 );
405                sb.append( "SELECT DISTINCT FK_ITEM from " ).append( indexName ).append( "_ITEM" );
406                sb.append( " where " ).append( "FK_QTNODE = '" ).append( id ).append( "'" );
407                Statement stmt = con.createStatement();
408                ResultSet rs = stmt.executeQuery( sb.toString() );
409                while ( rs.next() ) {
410                    Object s = rs.getObject( 1 );
411                    if ( !visitor.contains( s ) ) {
412                        visitor.add( s );
413                    }
414                }
415                stmt.close();
416            } catch ( Exception e ) {
417                LOG.logError( e.getMessage(), e );
418                throw new IndexException( "could not create node definition at database", e );
419            } finally {
420                try {
421                    pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
422                } catch ( Exception e1 ) {
423                    e1.printStackTrace();
424                }
425            }
426    
427            return visitor;
428        }
429    
430        private Envelope[] split() {
431            Envelope[] envs = new Envelope[4];
432            double nW = envelope.getWidth() / 2d;
433            double nH = envelope.getHeight() / 2d;
434    
435            envs[0] = GeometryFactory.createEnvelope( envelope.getMin().getX(), envelope.getMin().getY(),
436                                                      envelope.getMin().getX() + nW, envelope.getMin().getY() + nH, null );
437            envs[1] = GeometryFactory.createEnvelope( envelope.getMin().getX() + nW, envelope.getMin().getY(),
438                                                      envelope.getMin().getX() + ( 2 * nW ), envelope.getMin().getY() + nH,
439                                                      null );
440            envs[2] = GeometryFactory.createEnvelope( envelope.getMin().getX() + nW, envelope.getMin().getY() + nH,
441                                                      envelope.getMin().getX() + ( 2 * nW ), envelope.getMin().getY()
442                                                                                             + ( 2 * nH ), null );
443            envs[3] = GeometryFactory.createEnvelope( envelope.getMin().getX(), envelope.getMin().getY() + nH,
444                                                      envelope.getMin().getX() + nW, envelope.getMin().getY() + ( 2 * nH ),
445                                                      null );
446    
447            return envs;
448        }
449    
450    }