/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2012, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.internal.tree; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.geotoolkit.index.tree.AbstractTree; import org.geotoolkit.index.tree.Node; import org.geotoolkit.index.tree.StoreIndexException; import org.geotoolkit.index.tree.basic.SplitCase; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * Mechanic to store Tree architecture.<br/><br/> * It exist two differents implementation.<br/> * One which write each Tree {@link Node} and other Tree informations in a file at place specified by user.<br/> * The other, store in computer memory each Node. * * @see TreeAccessFile * @see TreeAccessMemory * @author Remi Marechal (Geomatys). */ public abstract class TreeAccess { /** * Default buffer length value use to read and write on hard disk. * * @see TreeAccessFile */ protected static final int DEFAULT_BUFFER_LENGTH = 4096; /** * CoordinateReferenceSystem attribut use by Tree. */ protected CoordinateReferenceSystem crs; /** * Maximum element number accepted by each Tree leaf. */ protected int maxElement; /** * The most higher Node in Tree.(also called Tree trunk). */ protected Node root; /** * Identifier of each node which will be store in TreeAccess implementation.<br/> * Default value is 1, value reserved for root Node. */ protected int nodeId = 1; /** * Table which contain all search result Node identifier. */ protected int[] tabSearch; /** * Search table attributs. */ protected int currentLength; protected int currentPosition; /** * boundary of search region. */ protected double[] regionSearch; /** * Store treeIdentifier when user call close method from tree. * * @see AbstractTree#close() */ protected int treeIdentifier; /** * Store eltNumber when user call close method from tree.<br/> * eltNumber represent number of data inserted in Tree. * * @see AbstractTree#close() */ protected int eltNumber; /** * Double table fill with {@link Double#NaN} value.<br/> * Element use if boundary of Node as a null value like remove last element in tree.<br/> * Moreover in Hilbert RTree they exist Node which call cell and within each Hilbert leaf.<br/> * This cells may have no boundary its an expected comportement. * * @see TreeAccessFile#writeNode(org.geotoolkit.index.tree.Node) */ protected double[] nanBound; /** * Store maximum hilbert value reach by each {@link HilbertNode}. * * @see HilbertNode. */ protected int hilbertOrder; /** * Store identifier of all removed Node to re-use them. */ protected List<Integer> recycleID = new LinkedList<Integer>(); /** * Split Made, only use by {@link BasicRTree} implementation.<br/> * see {@link SplitCase}. */ protected SplitCase splitMade; /** * Create a TreeAccess. */ protected TreeAccess() { treeIdentifier = 1; } /** * Find all value stored in Tree which intersect region search. * * @param nodeID Node identifier where search begin. Generaly begin at node identifier. * @param regionSearch boundary of search region. * @return integer table which contain all value stored in Tree which intersect region search. * @throws IOException if read or write Exception in {@link TreeAccessFile} implementation. */ public synchronized int[] search(int nodeID, double[] regionSearch) throws IOException { currentLength = 100; tabSearch = new int[currentLength]; currentPosition = 0; this.regionSearch = regionSearch; internalSearch(nodeID); return Arrays.copyOf(tabSearch, currentPosition); } /** * Search method adapted for implementation. * * @param nodeID current Node identifier search * @throws IOException if read or write Exception in {@link TreeAccessFile} implementation. */ public abstract void internalSearch(int nodeID) throws IOException; /** * Read Node at specified Node identifier. * * @param indexNode node identifier. * @return Node at specified Node identifier. * @throws IOException if read Exception in {@link TreeAccessFile} implementation. */ public abstract Node readNode(final int indexNode) throws IOException; /** * Write Node. * * @param candidate Node which will be written. * @throws IOException if write Exception in {@link TreeAccessFile} implementation. */ public abstract void writeNode(final Node candidate) throws IOException; /** * Remove specified Node. * * @param candidate Node which will be remove. */ public abstract void removeNode(final Node candidate); /** * Return CoordinateReferenceSystem use by Tree. * * @return CoordinateReferenceSystem use by Tree. */ public CoordinateReferenceSystem getCRS() { return crs; } /** * Return authorized element number stored in each Node. * * @return authorized element number stored in each Node. */ public int getMaxElementPerCells() { return maxElement; } /** * Return root Node. * * @return root Node. * @throws StoreIndexException if problem during tree root node reading. */ public Node getRoot() throws StoreIndexException { return root; } /** * Affect a new tree Identifier value. * Generaly use when caller use close method. * * @param treeIdentifier * @see AbstractTree#close() */ public synchronized void setTreeIdentifier(final int treeIdentifier) { this.treeIdentifier = treeIdentifier; } /** * Return current Tree Identifier value. * * @return current Tree Identifier value. */ public int getTreeIdentifier() { return treeIdentifier; } /** * Affect new element number value. * * @param eltNumber new element number value. * @see AbstractTree#close() */ public synchronized void setEltNumber(int eltNumber) { this.eltNumber = eltNumber; } /** * Return number of element stored in Tree. * * @return number of element stored in Tree. */ public int getEltNumber() { return eltNumber; } /** * Only use in {@link HilbertRTree} Tree implementation.<br/> * * Return maximum leaf Hilbert order. * * @return Leaf Hilbert Order. * @see #hilbertOrder */ public int getHilbertOrder() { return hilbertOrder; } /** * Only use by {@link BasicRTree} Tree implementation.<br/> * Return split Node made. * * @return split Node made. * @see #splitMade. */ public SplitCase getSplitMade() { return splitMade; } /** * Put TreeAccess just like after creating. * * @throws IOException if problem during write current {@link ByteBuffer} in {@link TreeAccessFile} implementation. * @see TreeAccessFile#rewind() * @see TreeAccessMemory#rewind() */ public synchronized void rewind() throws IOException { nodeId = 1; recycleID.clear(); } /** * Close all stream and write current {@link ByteBuffer} use by {@link TreeAccessFile} implementation.<br/> * In {@link TreeAccessMemory} implementation method is empty. * * @throws IOException if problem during write current {@link ByteBuffer} or * close stream use in {@link TreeAccessFile} implementation. */ public abstract void close() throws IOException; /** * Flush all stream and write current {@link ByteBuffer} use by {@link TreeAccessFile} implementation.<br/> * In {@link TreeAccessMemory} implementation method is empty. * * @throws IOException if problem during write current {@link ByteBuffer} or * flush stream use in {@link TreeAccessFile} implementation. */ public abstract void flush() throws IOException; /** * Return true if {@link TreeAccess} has already been closed else false. * * @return true if {@link TreeAccess} has already been closed else false. */ public abstract boolean isClose(); /** * Create a {@link Node} adapted to {@link AbstractBasicRTree} and {@link AbstractStarRTree} Implementations. * * @param boundary Node boundary. * @param properties Byte which contain Node properties if a Node is Leaf or not. * @param parentId parent identifier Node of current Node. * @param siblingId sibling or neighbour Node. * @param childId child Node identifier or stored value if Node is a leaf. * @return Node adapted from Tree implementation. * @throws StoreIndexException if write problem during HilbertNode creation. * @see HilbertTreeAccessFile#createNode(double[], byte, int, int, int) * @see HilbertNode#HilbertNode(org.geotoolkit.index.tree.access.TreeAccess, int, double[], byte, int, int, int) */ public synchronized Node createNode(double[] boundary, byte properties, int parentId, int siblingId, int childId) { final int currentID = (recycleID.isEmpty()) ? nodeId++ : recycleID.remove(0); return new Node(this, currentID, boundary, properties, parentId, siblingId, childId); } }