/*
* eXist Open Source Native XML Database
* Copyright (C) 2007 The eXist Project
* http://exist-db.org
*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* This program 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id$
*
* @author Pierrick Brihaye <pierrick.brihaye@free.fr>
*/
package org.exist.indexing.spatial;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.apache.log4j.Logger;
import org.exist.collections.Collection;
import org.exist.dom.DocumentImpl;
import org.exist.dom.ExtArrayNodeSet;
import org.exist.dom.NodeProxy;
import org.exist.dom.NodeSet;
import org.exist.dom.StoredNode;
import org.exist.indexing.spatial.AbstractGMLJDBCIndex.SpatialOperator;
import org.exist.numbering.DLN;
import org.exist.numbering.NodeId;
import org.exist.security.PermissionDeniedException;
import org.exist.storage.DBBroker;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.XPathException;
import org.exist.xquery.value.AtomicValue;
import org.exist.xquery.value.BooleanValue;
import org.exist.xquery.value.DoubleValue;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.ValueSequence;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import java.io.ByteArrayInputStream;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.Base64BinaryValueType;
import org.exist.xquery.value.BinaryValueFromInputStream;
public class GMLHSQLIndexWorker extends AbstractGMLJDBCIndexWorker {
private static final Logger LOG = Logger.getLogger(GMLHSQLIndexWorker.class);
public GMLHSQLIndexWorker(GMLHSQLIndex index, DBBroker broker) {
super(index, broker);
//TODO : evaluate one connection per worker
/*
try {
conn = DriverManager.getConnection("jdbc:hsqldb:" + index.getDataDir() + "/" +
index.db_file_name_prefix + ";shutdown=true", "sa", "");
} catch (SQLException e) {
LOG.error(e);
}
*/
}
@Override
protected boolean saveGeometryNode(Geometry geometry, String srsName, DocumentImpl doc, NodeId nodeId, PreparedStatement ps) throws SQLException {
try {
Geometry EPSG4326_geometry = null;
try {
EPSG4326_geometry = transformGeometry(geometry, srsName, "EPSG:4326");
} catch (SpatialIndexException e) {
//Transforms the exception into an SQLException.
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
}
ps.clearParameters();
/*DOCUMENT_URI*/ ps.setString(1, doc.getURI().toString());
/*NODE_ID_UNITS*/ ps.setInt(2, nodeId.units());
byte[] bytes = new byte[nodeId.size()];
nodeId.serialize(bytes, 0);
/*NODE_ID*/ ps.setBytes(3, bytes);
/*GEOMETRY_TYPE*/ ps.setString(4, geometry.getGeometryType());
/*SRS_NAME*/ ps.setString(5, srsName);
/*WKT*/ ps.setString(6, wktWriter.write(geometry));
/*WKB*/ ps.setBytes(7, wkbWriter.write(geometry));
/*MINX*/ ps.setDouble(8, geometry.getEnvelopeInternal().getMinX());
/*MAXX*/ ps.setDouble(9, geometry.getEnvelopeInternal().getMaxX());
/*MINY*/ ps.setDouble(10, geometry.getEnvelopeInternal().getMinY());
/*MAXY*/ ps.setDouble(11, geometry.getEnvelopeInternal().getMaxY());
/*CENTROID_X*/ ps.setDouble(12, geometry.getCentroid().getCoordinate().x);
/*CENTROID_Y*/ ps.setDouble(13, geometry.getCentroid().getCoordinate().y);
//geometry.getRepresentativePoint()
/*AREA*/ ps.setDouble(14, geometry.getArea());
//Boundary ?
/*EPSG4326_WKT*/ ps.setString(15, wktWriter.write(EPSG4326_geometry));
/*EPSG4326_WKB*/ ps.setBytes(16, wkbWriter.write(EPSG4326_geometry));
/*EPSG4326_MINX*/ ps.setDouble(17, EPSG4326_geometry.getEnvelopeInternal().getMinX());
/*EPSG4326_MAXX*/ ps.setDouble(18, EPSG4326_geometry.getEnvelopeInternal().getMaxX());
/*EPSG4326_MINY*/ ps.setDouble(19, EPSG4326_geometry.getEnvelopeInternal().getMinY());
/*EPSG4326_MAXY*/ ps.setDouble(20, EPSG4326_geometry.getEnvelopeInternal().getMaxY());
/*EPSG4326_CENTROID_X*/ ps.setDouble(21, EPSG4326_geometry.getCentroid().getCoordinate().x);
/*EPSG4326_CENTROID_Y*/ ps.setDouble(22, EPSG4326_geometry.getCentroid().getCoordinate().y);
//EPSG4326_geometry.getRepresentativePoint()
/*EPSG4326_AREA*/ ps.setDouble(23, EPSG4326_geometry.getArea());
//Boundary ?
//As discussed earlier, all instances of SFS geometry classes
//are topologically closed by definition.
//For empty Curves, isClosed is defined to have the value false.
/*IS_CLOSED*/ ps.setBoolean(24, !geometry.isEmpty());
/*IS_SIMPLE*/ ps.setBoolean(25, geometry.isSimple());
//Should always be true (the GML SAX parser makes a too severe check)
/*IS_VALID*/ ps.setBoolean(26, geometry.isValid());
return (ps.executeUpdate() == 1);
} finally {
//Let's help the garbage collector...
geometry = null;
ps.clearParameters();
}
}
@Override
protected boolean removeDocumentNode(DocumentImpl doc, NodeId nodeId, Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(
"DELETE FROM " + GMLHSQLIndex.TABLE_NAME +
" WHERE DOCUMENT_URI = ? AND NODE_ID_UNITS = ? AND NODE_ID = ?;"
);
ps.setString(1, doc.getURI().toString());
ps.setInt(2, nodeId.units());
byte[] bytes = new byte[nodeId.size()];
nodeId.serialize(bytes, 0);
ps.setBytes(3, bytes);
try {
return (ps.executeUpdate() == 1);
} finally {
ps.close();
}
}
@Override
protected int removeDocument(DocumentImpl doc, Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(
"DELETE FROM " + GMLHSQLIndex.TABLE_NAME + " WHERE DOCUMENT_URI = ?;"
);
ps.setString(1, doc.getURI().toString());
try {
return ps.executeUpdate();
} finally {
ps.close();
}
}
@Override
protected int removeCollection(Collection collection, Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(
"DELETE FROM " + GMLHSQLIndex.TABLE_NAME + " WHERE SUBSTRING(DOCUMENT_URI, 1, ?) = ?;"
);
ps.setInt(1, collection.getURI().toString().length());
ps.setString(2, collection.getURI().toString());
try {
return ps.executeUpdate();
} finally {
ps.close();
}
}
//Since an embedded HSQL has only one connection available (unless I'm totally dumb)
//acquire and release the connection from the index, which is *the* connection's owner
@Override
protected Connection acquireConnection() throws SQLException {
return index.acquireConnection(this.broker);
}
@Override
protected void releaseConnection(Connection conn) throws SQLException {
index.releaseConnection(this.broker);
}
@Override
protected NodeSet search(DBBroker broker, NodeSet contextSet, Geometry EPSG4326_geometry, int spatialOp, Connection conn) throws SQLException {
String extraSelection = null;
String bboxConstraint = null;
//TODO : generate it in AbstractGMLJDBCIndexWorker
String docConstraint = "";
boolean refine_query_on_doc = false;
if (contextSet != null) {
if(contextSet.getDocumentSet().getDocumentCount() <= index.getMaxDocsInContextToRefineQuery()) {
refine_query_on_doc = true;
DocumentImpl doc;
Iterator<DocumentImpl> it = contextSet.getDocumentSet().getDocumentIterator();
doc = it.next();
docConstraint = "(DOCUMENT_URI = '" + doc.getURI().toString() + "')";
while(it.hasNext()) {
doc = it.next();
docConstraint = docConstraint + " OR (DOCUMENT_URI = '" + doc.getURI().toString() + "')";
}
if (LOG.isDebugEnabled()) {
LOG.debug("Refine query on documents is enabled.");
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Refine query on documents is disabled.");
}
}
}
switch (spatialOp) {
//BBoxes are equal
case SpatialOperator.EQUALS:
bboxConstraint = "(EPSG4326_MINX = ? AND EPSG4326_MAXX = ?)" +
" AND (EPSG4326_MINY = ? AND EPSG4326_MAXY = ?)";
break;
//Nothing much we can do with the BBox at this stage
case SpatialOperator.DISJOINT:
//Retrieve the BBox though...
extraSelection = ", EPSG4326_MINX, EPSG4326_MAXX, EPSG4326_MINY, EPSG4326_MAXY";
break;
//BBoxes intersect themselves
case SpatialOperator.INTERSECTS:
case SpatialOperator.TOUCHES:
case SpatialOperator.CROSSES:
case SpatialOperator.OVERLAPS:
bboxConstraint = "(EPSG4326_MAXX >= ? AND EPSG4326_MINX <= ?)" +
" AND (EPSG4326_MAXY >= ? AND EPSG4326_MINY <= ?)";
break;
//BBox is fully within
case SpatialOperator.WITHIN:
bboxConstraint = "(EPSG4326_MINX >= ? AND EPSG4326_MAXX <= ?)" +
" AND (EPSG4326_MINY >= ? AND EPSG4326_MAXY <= ?)";
break;
//BBox fully contains
case SpatialOperator.CONTAINS:
bboxConstraint = "(EPSG4326_MINX <= ? AND EPSG4326_MAXX >= ?)" +
" AND (EPSG4326_MINY <= ? AND EPSG4326_MAXY >= ?)";
break;
default:
throw new IllegalArgumentException("Unsupported spatial operator:" + spatialOp);
}
PreparedStatement ps = conn.prepareStatement(
"SELECT EPSG4326_WKB, DOCUMENT_URI, NODE_ID_UNITS, NODE_ID" + (extraSelection == null ? "" : extraSelection) +
" FROM " + GMLHSQLIndex.TABLE_NAME +
(bboxConstraint == null ?
(refine_query_on_doc ? " WHERE " + docConstraint : "") :
" WHERE " + (refine_query_on_doc ? "(" + docConstraint + ") AND " : "") + bboxConstraint) + ";"
);
if (bboxConstraint != null) {
ps.setDouble(1, EPSG4326_geometry.getEnvelopeInternal().getMinX());
ps.setDouble(2, EPSG4326_geometry.getEnvelopeInternal().getMaxX());
ps.setDouble(3, EPSG4326_geometry.getEnvelopeInternal().getMinY());
ps.setDouble(4, EPSG4326_geometry.getEnvelopeInternal().getMaxY());
}
ResultSet rs = null;
NodeSet result = null;
try {
int disjointPostFiltered = 0;
rs = ps.executeQuery();
result = new ExtArrayNodeSet(); //new ExtArrayNodeSet(docs.getLength(), 250)
while (rs.next()) {
DocumentImpl doc = null;
try {
doc = (DocumentImpl)broker.getXMLResource(XmldbURI.create(rs.getString("DOCUMENT_URI")));
} catch (PermissionDeniedException e) {
LOG.debug(e);
//Ignore since the broker has no right on the document
continue;
}
//contextSet == null should be used to scan the whole index
if (contextSet == null || refine_query_on_doc || contextSet.getDocumentSet().contains(doc.getDocId())) {
NodeId nodeId = new DLN(rs.getInt("NODE_ID_UNITS"), rs.getBytes("NODE_ID"), 0);
NodeProxy p = new NodeProxy(doc, nodeId);
//Node is in the context : check if it is accurate
//contextSet.contains(p) would have made more sense but there is a problem with
//VirtualNodeSet when on the DESCENDANT_OR_SELF axis
if (contextSet == null || contextSet.get(p) != null) {
boolean geometryMatches = false;
if (spatialOp == SpatialOperator.DISJOINT) {
//No BBox intersection : obviously disjoint
if (rs.getDouble("EPSG4326_MAXX") < EPSG4326_geometry.getEnvelopeInternal().getMinX() ||
rs.getDouble("EPSG4326_MINX") > EPSG4326_geometry.getEnvelopeInternal().getMaxX() ||
rs.getDouble("EPSG4326_MAXY") < EPSG4326_geometry.getEnvelopeInternal().getMinY() ||
rs.getDouble("EPSG4326_MINY") > EPSG4326_geometry.getEnvelopeInternal().getMaxY()) {
geometryMatches = true;
disjointPostFiltered++;
}
}
//Possible match : check the geometry
if (!geometryMatches) {
try {
Geometry geometry = wkbReader.read(rs.getBytes("EPSG4326_WKB"));
switch (spatialOp) {
case SpatialOperator.EQUALS:
geometryMatches = geometry.equals(EPSG4326_geometry);
break;
case SpatialOperator.DISJOINT:
geometryMatches = geometry.disjoint(EPSG4326_geometry);
break;
case SpatialOperator.INTERSECTS:
geometryMatches = geometry.intersects(EPSG4326_geometry);
break;
case SpatialOperator.TOUCHES:
geometryMatches = geometry.touches(EPSG4326_geometry);
break;
case SpatialOperator.CROSSES:
geometryMatches = geometry.crosses(EPSG4326_geometry);
break;
case SpatialOperator.WITHIN:
geometryMatches = geometry.within(EPSG4326_geometry);
break;
case SpatialOperator.CONTAINS:
geometryMatches = geometry.contains(EPSG4326_geometry);
break;
case SpatialOperator.OVERLAPS:
geometryMatches = geometry.overlaps(EPSG4326_geometry);
break;
}
} catch (ParseException e) {
//Transforms the exception into an SQLException.
//Very unlikely to happen though...
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
}
}
if (geometryMatches)
result.add(p);
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug(rs.getRow() + " eligible geometries, " + result.getItemCount() + "selected" +
(spatialOp == SpatialOperator.DISJOINT ? "(" + disjointPostFiltered + " post filtered)" : ""));
}
return result;
} finally {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
}
}
@Override
protected Map<Geometry, String> getGeometriesForDocument(DocumentImpl doc, Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(
"SELECT EPSG4326_WKB, EPSG4326_WKT FROM " + GMLHSQLIndex.TABLE_NAME + " WHERE DOCUMENT_URI = ?;"
);
ps.setString(1, doc.getURI().toString());
ResultSet rs = null;
try {
rs = ps.executeQuery();
Map<Geometry, String> map = new TreeMap<Geometry, String>();
while (rs.next()) {
Geometry EPSG4326_geometry = wkbReader.read(rs.getBytes("EPSG4326_WKB"));
//Returns the EPSG:4326 WKT for every geometry to make occurrence aggregation consistent
map.put(EPSG4326_geometry, rs.getString("EPSG4326_WKT"));
}
return map;
} catch (ParseException e) {
//Transforms the exception into an SQLException.
//Very unlikely to happen though...
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
} finally {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
}
}
@Override
protected Geometry getGeometryForNode(DBBroker broker, NodeProxy p, boolean getEPSG4326, Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(
"SELECT " + (getEPSG4326 ? "EPSG4326_WKB" : "WKB") +
" FROM " + GMLHSQLIndex.TABLE_NAME +
" WHERE DOCUMENT_URI = ? AND NODE_ID_UNITS = ? AND NODE_ID = ?;"
);
ps.setString(1, p.getDocument().getURI().toString());
ps.setInt(2, p.getNodeId().units());
byte[] bytes = new byte[p.getNodeId().size()];
p.getNodeId().serialize(bytes, 0);
ps.setBytes(3, bytes);
ResultSet rs = null;
try {
rs = ps.executeQuery();
if (!rs.next())
//Nothing returned
return null;
Geometry geometry = wkbReader.read(rs.getBytes(1));
if (rs.next()) {
//Should be impossible
throw new SQLException("More than one geometry for node " + p);
}
return geometry;
} catch (ParseException e) {
//Transforms the exception into an SQLException.
//Very unlikely to happen though...
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
} finally {
if (rs != null)
rs.close();
ps.close();
}
}
@Override
protected Geometry[] getGeometriesForNodes(DBBroker broker, NodeSet contextSet, boolean getEPSG4326, Connection conn) throws SQLException {
//TODO : generate it in AbstractGMLJDBCIndexWorker
String docConstraint = "";
boolean refine_query_on_doc = false;
if (contextSet != null) {
if(contextSet.getDocumentSet().getDocumentCount() <= index.getMaxDocsInContextToRefineQuery()) {
DocumentImpl doc;
Iterator<DocumentImpl> it = contextSet.getDocumentSet().getDocumentIterator();
doc = it.next();
docConstraint = "(DOCUMENT_URI = '" + doc.getURI().toString() + "')";
while(it.hasNext()) {
doc = it.next();
docConstraint = docConstraint + " OR (DOCUMENT_URI = '" + doc.getURI().toString() + "')";
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Refine query on documents is " + (refine_query_on_doc ? "enabled." : "disabled."));
}
PreparedStatement ps = conn.prepareStatement(
"SELECT " + (getEPSG4326 ? "EPSG4326_WKB" : "WKB") + ", DOCUMENT_URI, NODE_ID_UNITS, NODE_ID" +
" FROM " + GMLHSQLIndex.TABLE_NAME + (refine_query_on_doc ? " WHERE " + docConstraint : "")
);
ResultSet rs = null;
try {
rs = ps.executeQuery();
Geometry[] result = new Geometry[contextSet.getLength()];
int index= 0;
while (rs.next()) {
DocumentImpl doc = null;
try {
doc = (DocumentImpl)broker.getXMLResource(XmldbURI.create(rs.getString("DOCUMENT_URI")));
} catch (PermissionDeniedException e) {
LOG.debug(e);
result[index++] = null;
//Ignore since the broker has no right on the document
continue;
}
if (contextSet == null || refine_query_on_doc || contextSet.getDocumentSet().contains(doc.getDocId())) {
NodeId nodeId = new DLN(rs.getInt("NODE_ID_UNITS"), rs.getBytes("NODE_ID"), 0);
NodeProxy p = new NodeProxy(doc, nodeId);
//Node is in the context : check if it is accurate
//contextSet.contains(p) would have made more sense but there is a problem with
//VirtualNodeSet when on the DESCENDANT_OR_SELF axis
if (contextSet.get(p) != null) {
Geometry geometry = wkbReader.read(rs.getBytes(1));
result[index++] = geometry;
}
}
}
return result;
} catch (ParseException e) {
//Transforms the exception into an SQLException.
//Very unlikely to happen though...
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
} finally {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
}
}
@Override
protected AtomicValue getGeometricPropertyForNode(XQueryContext context, NodeProxy p, Connection conn, String propertyName) throws SQLException, XPathException {
PreparedStatement ps = conn.prepareStatement(
"SELECT " + propertyName +
" FROM " + GMLHSQLIndex.TABLE_NAME +
" WHERE DOCUMENT_URI = ? AND NODE_ID_UNITS = ? AND NODE_ID = ?"
);
ps.setString(1, p.getDocument().getURI().toString());
ps.setInt(2, p.getNodeId().units());
byte[] bytes = new byte[p.getNodeId().size()];
p.getNodeId().serialize(bytes, 0);
ps.setBytes(3, bytes);
ResultSet rs = null;
try {
rs = ps.executeQuery();
if (!rs.next())
//Nothing returned
return AtomicValue.EMPTY_VALUE;
AtomicValue result = null;
if (rs.getMetaData().getColumnClassName(1).equals(Boolean.class.getName())) {
result = new BooleanValue(rs.getBoolean(1));
} else if (rs.getMetaData().getColumnClassName(1).equals(Double.class.getName())) {
result = new DoubleValue(rs.getDouble(1));
} else if (rs.getMetaData().getColumnClassName(1).equals(String.class.getName())) {
result = new StringValue(rs.getString(1));
} else if (rs.getMetaData().getColumnType(1) == java.sql.Types.BINARY) {
result = BinaryValueFromInputStream.getInstance(context, new Base64BinaryValueType(), new ByteArrayInputStream(rs.getBytes(1)));
} else
throw new SQLException("Unable to make an atomic value from '" + rs.getMetaData().getColumnClassName(1) + "'");
if (rs.next()) {
//Should be impossible
throw new SQLException("More than one geometry for node " + p);
}
return result;
} finally {
if (rs != null)
rs.close();
ps.close();
}
}
@Override
protected ValueSequence getGeometricPropertyForNodes(XQueryContext context, NodeSet contextSet, Connection conn, String propertyName) throws SQLException, XPathException {
//TODO : generate it in AbstractGMLJDBCIndexWorker
String docConstraint = "";
boolean refine_query_on_doc = false;
if (contextSet != null) {
if(contextSet.getDocumentSet().getDocumentCount() <= index.getMaxDocsInContextToRefineQuery()) {
DocumentImpl doc;
Iterator<DocumentImpl> it = contextSet.getDocumentSet().getDocumentIterator();
doc = it.next();
docConstraint = "(DOCUMENT_URI = '" + doc.getURI().toString() + "')";
while(it.hasNext()) {
doc = it.next();
docConstraint = docConstraint + " OR (DOCUMENT_URI = '" + doc.getURI().toString() + "')";
}
if (LOG.isDebugEnabled()) {
LOG.debug("Refine query on documents is enabled.");
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Refine query on documents is disabled.");
}
}
}
PreparedStatement ps = conn.prepareStatement(
"SELECT " + propertyName + ", DOCUMENT_URI, NODE_ID_UNITS, NODE_ID" +
" FROM " + GMLHSQLIndex.TABLE_NAME + (refine_query_on_doc ? " WHERE " + docConstraint : "")
);
ResultSet rs = null;
try {
rs = ps.executeQuery();
ValueSequence result;
if (contextSet == null)
result = new ValueSequence();
else
result = new ValueSequence(contextSet.getLength());
while (rs.next()) {
DocumentImpl doc = null;
try {
doc = (DocumentImpl)broker.getXMLResource(XmldbURI.create(rs.getString("DOCUMENT_URI")));
} catch (PermissionDeniedException e) {
LOG.debug(e);
//Untested, but that is roughly what should be returned.
if (rs.getMetaData().getColumnClassName(1).equals(Boolean.class.getName())) {
result.add(AtomicValue.EMPTY_VALUE);
} else if (rs.getMetaData().getColumnClassName(1).equals(Double.class.getName())) {
result.add(AtomicValue.EMPTY_VALUE);
} else if (rs.getMetaData().getColumnClassName(1).equals(String.class.getName())) {
result.add(AtomicValue.EMPTY_VALUE);
} else if (rs.getMetaData().getColumnType(1) == java.sql.Types.BINARY) {
result.add(AtomicValue.EMPTY_VALUE);
} else
throw new SQLException("Unable to make an atomic value from '" + rs.getMetaData().getColumnClassName(1) + "'");
//Ignore since the broker has no right on the document
continue;
}
if (contextSet.getDocumentSet().contains(doc.getDocId())) {
NodeId nodeId = new DLN(rs.getInt("NODE_ID_UNITS"), rs.getBytes("NODE_ID"), 0);
NodeProxy p = new NodeProxy(doc, nodeId);
//Node is in the context : check if it is accurate
//contextSet.contains(p) would have made more sense but there is a problem with
//VirtualNodeSet when on the DESCENDANT_OR_SELF axis
if (contextSet.get(p) != null) {
if (rs.getMetaData().getColumnClassName(1).equals(Boolean.class.getName())) {
result.add(new BooleanValue(rs.getBoolean(1)));
} else if (rs.getMetaData().getColumnClassName(1).equals(Double.class.getName())) {
result.add(new DoubleValue(rs.getDouble(1)));
} else if (rs.getMetaData().getColumnClassName(1).equals(String.class.getName())) {
result.add(new StringValue(rs.getString(1)));
} else if (rs.getMetaData().getColumnType(1) == java.sql.Types.BINARY) {
result.add(BinaryValueFromInputStream.getInstance(context, new Base64BinaryValueType(), new ByteArrayInputStream(rs.getBytes(1))));
} else
throw new SQLException("Unable to make an atomic value from '" + rs.getMetaData().getColumnClassName(1) + "'");
}
}
}
return result;
} finally {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
}
}
@Override
protected boolean checkIndex(DBBroker broker, Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM " + GMLHSQLIndex.TABLE_NAME + ";"
);
ResultSet rs = null;
try {
rs = ps.executeQuery();
while (rs.next()) {
Geometry original_geometry = wkbReader.read(rs.getBytes("WKB"));
if (!original_geometry.equals(wktReader.read(rs.getString("WKT")))) {
LOG.info("Inconsistent WKT : " + rs.getString("WKT"));
return false;
}
Geometry EPSG4326_geometry = wkbReader.read(rs.getBytes("EPSG4326_WKB"));
if (!EPSG4326_geometry.equals(wktReader.read(rs.getString("EPSG4326_WKT")))) {
LOG.info("Inconsistent WKT : " + rs.getString("EPSG4326_WKT"));
return false;
}
if (!original_geometry.getGeometryType().equals(rs.getString("GEOMETRY_TYPE"))) {
LOG.info("Inconsistent geometry type: " + rs.getDouble("GEOMETRY_TYPE"));
return false;
}
if (original_geometry.getEnvelopeInternal().getMinX() != rs.getDouble("MINX")) {
LOG.info("Inconsistent MinX: " + rs.getDouble("MINX"));
return false;
}
if (original_geometry.getEnvelopeInternal().getMaxX() != rs.getDouble("MAXX")) {
LOG.info("Inconsistent MaxX: " + rs.getDouble("MAXX"));
return false;
}
if (original_geometry.getEnvelopeInternal().getMinY() != rs.getDouble("MINY")) {
LOG.info("Inconsistent MinY: " + rs.getDouble("MINY"));
return false;
}
if (original_geometry.getEnvelopeInternal().getMaxY() != rs.getDouble("MAXY")) {
LOG.info("Inconsistent MaxY: " + rs.getDouble("MAXY"));
return false;
}
if (original_geometry.getCentroid().getCoordinate().x != rs.getDouble("CENTROID_X")) {
LOG.info("Inconsistent X for centroid : " + rs.getDouble("CENTROID_X"));
return false;
}
if (original_geometry.getCentroid().getCoordinate().y != rs.getDouble("CENTROID_Y")) {
LOG.info("Inconsistent Y for centroid : " + rs.getDouble("CENTROID_Y"));
return false;
}
if (original_geometry.getArea() != rs.getDouble("AREA")) {
LOG.info("Inconsistent area: " + rs.getDouble("AREA"));
return false;
}
String srsName = rs.getString("SRS_NAME");
try {
if (!transformGeometry(original_geometry, srsName, "EPSG:4326").equals(EPSG4326_geometry)) {
LOG.info("Transformed original geometry inconsistent with stored tranformed one");
return false;
}
} catch (SpatialIndexException e) {
//Transforms the exception into an SQLException.
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
}
if (EPSG4326_geometry.getEnvelopeInternal().getMinX() != rs.getDouble("EPSG4326_MINX")) {
LOG.info("Inconsistent MinX: " + rs.getDouble("EPSG4326_MINX"));
return false;
}
if (EPSG4326_geometry.getEnvelopeInternal().getMaxX() != rs.getDouble("EPSG4326_MAXX")) {
LOG.info("Inconsistent MaxX: " + rs.getDouble("EPSG4326_MAXX"));
return false;
}
if (EPSG4326_geometry.getEnvelopeInternal().getMinY() != rs.getDouble("EPSG4326_MINY")) {
LOG.info("Inconsistent MinY: " + rs.getDouble("EPSG4326_MINY"));
return false;
}
if (EPSG4326_geometry.getEnvelopeInternal().getMaxY() != rs.getDouble("EPSG4326_MAXY")) {
LOG.info("Inconsistent MaxY: " + rs.getDouble("EPSG4326_MAXY"));
return false;
}
if (EPSG4326_geometry.getCentroid().getCoordinate().x != rs.getDouble("EPSG4326_CENTROID_X")) {
LOG.info("Inconsistent X for centroid : " + rs.getDouble("EPSG4326_CENTROID_X"));
return false;
}
if (EPSG4326_geometry.getCentroid().getCoordinate().y != rs.getDouble("EPSG4326_CENTROID_Y")) {
LOG.info("Inconsistent Y for centroid : " + rs.getDouble("EPSG4326_CENTROID_Y"));
return false;
}
if (EPSG4326_geometry.getArea() != rs.getDouble("EPSG4326_AREA")) {
LOG.info("Inconsistent area: " + rs.getDouble("EPSG4326_AREA"));
return false;
}
if (original_geometry.isEmpty() == rs.getBoolean("IS_CLOSED")) {
LOG.info("Inconsistent area: " + rs.getBoolean("IS_CLOSED"));
return false;
}
if (original_geometry.isSimple() != rs.getBoolean("IS_SIMPLE")) {
LOG.info("Inconsistent area: " + rs.getBoolean("IS_SIMPLE"));
return false;
}
if (original_geometry.isValid() != rs.getBoolean("IS_VALID")) {
LOG.info("Inconsistent area: " + rs.getBoolean("IS_VALID"));
return false;
}
DocumentImpl doc = null;
try {
doc = (DocumentImpl)broker.getXMLResource(XmldbURI.create(rs.getString("DOCUMENT_URI")));
} catch (PermissionDeniedException e) {
//The broker has no right on the document
LOG.error(e);
return false;
}
NodeId nodeId = new DLN(rs.getInt("NODE_ID_UNITS"), rs.getBytes("NODE_ID"), 0);
StoredNode node = broker.objectWith(new NodeProxy(doc, nodeId));
if (node == null) {
LOG.info("Node " + nodeId + "doesn't exist");
return false;
}
if (!AbstractGMLJDBCIndexWorker.GML_NS.equals(node.getNamespaceURI())) {
LOG.info("GML indexed node (" + node.getNodeId()+ ") is in the '" +
node.getNamespaceURI() + "' namespace. '" +
AbstractGMLJDBCIndexWorker.GML_NS + "' was expected !");
return false;
}
if (!original_geometry.getGeometryType().equals(node.getLocalName())) {
if ("Box".equals(node.getLocalName()) && "Polygon".equals(original_geometry.getGeometryType())) {
LOG.debug("GML indexed node (" + node.getNodeId() + ") is a gml:Box indexed as a polygon");
} else {
LOG.info("GML indexed node (" + node.getNodeId() + ") has '" +
node.getLocalName() + "' as its local name. '" +
original_geometry.getGeometryType() + "' was expected !");
return false;
}
}
LOG.info(node);
}
return true;
} catch (ParseException e) {
//Transforms the exception into an SQLException.
//Very unlikely to happen though...
SQLException ee = new SQLException(e.getMessage());
ee.initCause(e);
throw ee;
} finally {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
}
}
}