/*
* 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.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.apache.sis.referencing.CRS;
import org.apache.sis.util.ArgumentChecks;
import org.geotoolkit.index.tree.Node;
import org.geotoolkit.index.tree.basic.SplitCase;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.FactoryException;
/**
* {@link TreeAccess} implementation.<br/>
* Store all {@link Node} architecture use by {@link Tree} on disk drive.
*
* @author Rémi Maréchal (Geomatys).
*/
public class TreeAccessFile extends ChannelTreeAccess {
/**
* Position in the tree file where CRS description should begin.
*/
private static final int CRS_POSITION = 34;
/**
* Number of Integer per Node.<br/><br/>
* parent ID<br/>
* sibling ID<br/>
* child ID<br/>
* children number.
*
* @see HilbertTreeAccessFile#INT_NUMBER
*/
private static final int INT_NUMBER = 4;
/**
* Build a {@link Tree} from an already filled file at {@link Path} location.<br/><br/>
*
* @param input {@code File} which already contains {@link Node} architecture.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber tree version.
* @param byteBufferLength length in Byte unit of the buffer which read and write on hard disk.
* @throws IOException if problem during read or write Node.
* @throws ClassNotFoundException if there is a problem during {@link CoordinateReferenceSystem} invert serialization.
*/
public TreeAccessFile(final Path input, final int magicNumber, final double versionNumber, final int byteBufferLength) throws IOException, ClassNotFoundException {
this(input, magicNumber, versionNumber, byteBufferLength, INT_NUMBER);
}
/**
* Build a {@link Tree} from an already filled file at {@link Path} location.<br><br>
*
* Note : The default length value of ByteBuffer which read and write on hard disk, is 4096 Bytes.
*
* @param input {@code File} which already contains {@link Node} architecture.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber tree version.
* @throws IOException if problem during read or write Node.
* @throws ClassNotFoundException if there is a problem during {@link CoordinateReferenceSystem} invert serialization.
*/
public TreeAccessFile(final Path input, final int magicNumber, final double versionNumber) throws IOException, ClassNotFoundException{
this(input, magicNumber, versionNumber, DEFAULT_BUFFER_LENGTH, INT_NUMBER);
}
/**
* Build a {@link TreeAccess} from an already filled file at {@link Path} location.
*
* @param input {@code Path} which already contains {@link Node} architecture.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber tree version.
* @param byteBufferLength length in Byte unit of the buffer which read and write on hard disk.
* @param integerNumberPerNode integer number per Node which will be red/written during Node reading/writing process.
* @throws IOException if problem during read or write Node.
* @throws ClassNotFoundException if there is a problem during {@link CoordinateReferenceSystem} invert serialization.
*/
protected TreeAccessFile(final Path input, final int magicNumber, final double versionNumber ,
final int byteBufferLength, final int integerNumberPerNode) throws IOException, ClassNotFoundException {
super(Files.newByteChannel(input, StandardOpenOption.CREATE, StandardOpenOption.READ,
StandardOpenOption.WRITE),
magicNumber, versionNumber, byteBufferLength, integerNumberPerNode);
}
/**
* Build and insert {@link Node} architecture in a file at {@link Path} location.<br/>
* If file is not empty, data within it will be overwrite.<br/>
* If file does not exist a file will be create.<br/><br/>
*
* Constructor only use by {@link BasicRTree} implementation.
*
* @param outPut {@code File} where {@link Node} architecture which will be write.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber version number.
* @param maxElements element number per cell.
* @param splitMade define tree node split made.
* @param crs
* @param byteBufferLength length in Byte unit of the buffer which read and write on hard disk.
* @throws IOException if problem during read or write Node.
*/
public TreeAccessFile(final Path outPut, final int magicNumber, final double versionNumber, final int maxElements,
final SplitCase splitMade, final CoordinateReferenceSystem crs, final int byteBufferLength) throws IOException {
this(outPut, magicNumber, versionNumber, maxElements, 0, splitMade, crs, byteBufferLength, INT_NUMBER);
}
/**
* Build and insert {@link Node} architecture in a file at {@link Path} location.<br/>
* If file is not empty, data within it will be overwrite.<br/>
* If file does not exist a file will be create.<br/><br/>
*
* Constructor only use by {@link BasicRTree} implementation.
*
* @param outPut {@code File} where {@link Node} architecture which will be write.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber version number.
* @param maxElements element number per cell.
* @param splitMade define how to split a {@link Node}, only use by {@link BasicRTree}, may be {@code null} for other tree.
* @param crs
* @throws IOException if problem during read or write Node.
*/
public TreeAccessFile(final Path outPut, final int magicNumber, final double versionNumber, final int maxElements,
final SplitCase splitMade, final CoordinateReferenceSystem crs) throws IOException {
this(outPut, magicNumber, versionNumber, maxElements, 0, splitMade, crs, DEFAULT_BUFFER_LENGTH, INT_NUMBER);
}
/**
* Build and insert {@link Node} architecture in a file at {@link Path} location.<br/>
* If file is not empty, data within it will be overwrite.<br/>
* If file does not exist a file will be create.
*
* @param outPut {@code File} where {@link Node} architecture which will be write.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber version number.
* @param maxElements element number per cell.
* @param crs
* @param byteBufferLength length in Byte unit of the buffer which read and write on hard disk.
* @throws IOException if problem during read or write Node.
*/
public TreeAccessFile(final Path outPut, final int magicNumber, final double versionNumber,
final int maxElements, final CoordinateReferenceSystem crs, final int byteBufferLength) throws IOException {
this(outPut, magicNumber, versionNumber, maxElements, 0, null, crs, byteBufferLength, INT_NUMBER);
}
/**
* Build and insert {@link Node} architecture in a file at {@link Path} location.<br/>
* If file is not empty, data within it will be overwrite.<br/>
* If file does not exist a file will be create.
*
* @param outPut {@code File} where {@link Node} architecture which will be write.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber version number.
* @param maxElements element number per cell.
* @param crs
* @throws IOException if problem during read or write Node.
*/
public TreeAccessFile(final Path outPut, final int magicNumber, final double versionNumber,
final int maxElements, final CoordinateReferenceSystem crs) throws IOException {
this(outPut, magicNumber, versionNumber, maxElements, 0, null, crs, DEFAULT_BUFFER_LENGTH, INT_NUMBER);
}
/**
* Build and insert {@link Node} architecture in a file at {@link Path} location.<br/>
* If file is not empty, data within it will be overwrite.<br/>
* If file does not exist a file will be create.
*
* @param outPut {@code File} where {@link Node} architecture which will be write.
* @param magicNumber {@code Integer} single {@link Tree} code.
* @param versionNumber version number.
* @param maxElements element number per cell.
* @param hilbertOrder
* @param crs
* @param splitMade define how to split a {@link Node}, only use by {@link BasicRTree}, may be {@code null} for other tree.
* @param byteBufferLength length in Byte unit of the buffer which read and write on hard disk.
* @param integerNumberPerNode integer number per Node which will be red/written during Node reading/writing process.
* @throws IOException if problem during read or write Node.
*/
protected TreeAccessFile(final Path outPut, final int magicNumber, final double versionNumber,
final int maxElements, final int hilbertOrder, final SplitCase splitMade,
final CoordinateReferenceSystem crs, final int byteBufferLength, final int integerNumberPerNode)
throws IOException {
super(Files.newByteChannel(outPut, StandardOpenOption.CREATE, StandardOpenOption.READ,
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING), magicNumber, versionNumber, maxElements, hilbertOrder, splitMade, crs, byteBufferLength, integerNumberPerNode);
}
/**
* Retrieve the CRS of the input tree.
* @param treeFile The file containing the tree.
* @return The {@link org.opengis.referencing.crs.CoordinateReferenceSystem} in which teh tree is expressed.
* @throws java.lang.IllegalArgumentException if input file is null.
* @throws java.io.IOException if input file does not exists, or is a directory, or a problem happens at reading, or if problem during CRS WKT serialization.
*/
public static CoordinateReferenceSystem getTreeCRS(final File treeFile) throws IOException {
ArgumentChecks.ensureNonNull("Input tree file", treeFile);
if (!treeFile.isFile()) {
throw new IOException("Input file is not a regular file : "+treeFile);
}
final RandomAccessFile raf = new RandomAccessFile(treeFile, "r");
raf.seek(34);
final int byteTabLength = raf.readInt();
final byte[] crsByteArray = new byte[byteTabLength];
raf.read(crsByteArray, 0, byteTabLength);
final String wktCrs = new String(crsByteArray);
try {
return CRS.fromWKT(wktCrs);
} catch (FactoryException ex) {
throw new IOException(ex);
}
}
//
// /**
// * Retrieve the CRS of the input tree.
// * @param treeFile The file containing the tree.
// * @return The {@link org.opengis.referencing.crs.CoordinateReferenceSystem} in which teh tree is expressed.
// * @throws java.lang.IllegalArgumentException if input file is null.
// * @throws java.io.IOException if input file does not exists, or is a directory, or a problem happens at reading.
// * @throws java.lang.ClassNotFoundException If the read object is corrupted,
// */
// public static CoordinateReferenceSystem getTreeCRS(final File treeFile) throws IOException, ClassNotFoundException {
// ArgumentChecks.ensureNonNull("Input tree file", treeFile);
// if (!treeFile.isFile()) {
// throw new IOException("Input file is not a regular file : "+treeFile);
// }
// final RandomAccessFile raf = new RandomAccessFile(treeFile, "r");
// final FileChannel fChannel = raf.getChannel();
// final ByteBuffer buff = ByteBuffer.allocate(38);
// fChannel.read(buff);
// assert fChannel.position() == 38;
// buff.flip();
// buff.position(4);
// final ByteOrder bO = (buff.get() == 1) ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
// buff.order(bO);
// buff.position(34);
// final int byteTabLength = buff.getInt();
// final byte[] crsByteArray = new byte[byteTabLength];
// raf.seek(38);
// raf.read(crsByteArray, 0, byteTabLength);
//
// try (final ObjectInputStream crsInputS =
// new ObjectInputStream(new ByteArrayInputStream(crsByteArray))) {
//
// return (CoordinateReferenceSystem) crsInputS.readObject();
// }
// }
}