Rev 49 | Rev 57 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*** $Id: OracleSpatial10gDialect.java 50 2007-11-05 22:00:25Z maesenka $** This file is part of Spatial Hibernate, an extension to the* hibernate ORM solution for geographic data.** Copyright © 2007 K.U. Leuven LRD, Spatial Applications Division, Belgium** This work was partially supported by the European Commission,* under the 6th Framework Programme, contract IST-2-004688-STP.** 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; either* version 2.1 of the License, or (at your option) any later version.** 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.** You should have received a copy of the GNU Lesser General Public* License along with this library; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA** For more information, visit: http://www.cadrie.com/*/package org.hibernatespatial.oracle;import java.io.IOException;import java.io.InputStream;import java.net.URL;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.hibernate.Hibernate;import org.hibernate.QueryException;import org.hibernate.dialect.Oracle9Dialect;import org.hibernate.dialect.function.StandardSQLFunction;import org.hibernate.engine.SessionFactoryImplementor;import org.hibernate.type.CustomType;import org.hibernate.type.Type;import org.hibernate.usertype.UserType;import org.hibernatespatial.SpatialAnalysis;import org.hibernatespatial.SpatialDialect;import org.hibernatespatial.SpatialRelation;import org.hibernatespatial.helper.PropertyFileReader;import com.vividsolutions.jts.geom.Geometry;/*** Spatial Dialect for Oracle10g databases.** @author Karel Maesen*/public class OracleSpatial10gDialect extends Oracle9Dialect implementsSpatialDialect {/*** Implementation of the OGC astext function for HQL.*/private class AsTextFunction extends StandardSQLFunction {public AsTextFunction() {super("astext", Hibernate.STRING);}public String render(final List args,final SessionFactoryImplementor factory) {StringBuffer buf = new StringBuffer();if (args.isEmpty()) {throw new IllegalArgumentException("First Argument in arglist must be object "+ "to which method is applied");}buf.append("TO_CHAR(SDO_UTIL.TO_WKTGEOMETRY(").append(args.get(0)).append("))");return buf.toString();}}/*** Implements OGC function dimension for HQL.*/private class DimensionFunction extends SDOObjectMethod {public DimensionFunction() {super("Get_GType", Hibernate.INTEGER);}public String render(final List args,final SessionFactoryImplementor factory) {StringBuffer buf = new StringBuffer();if (args.isEmpty()) {throw new IllegalArgumentException("First Argument in arglist must be object to "+ "which method is applied");}buf.append("CASE ").append(args.get(0)).append(".").append(getName()).append("()");buf.append(" WHEN 1 THEN 0").append(" WHEN 2 THEN 1").append(" WHEN 3 THEN 2").append(" WHEN 5 THEN 0").append(" WHEN 6 THEN 1").append(" WHEN 7 THEN 2").append(" END");return buf.toString();}}/*** An HQL function that is implemented using Oracle's OGC compliance* package.*/private class WrappedOGCFunction extends StandardSQLFunction {private final boolean[] geomArrays;/*** @param name* function name* @param type* return type of the function* @param geomArrays* indicates which argument places are occupied by* sdo_geometries*/public WrappedOGCFunction(final String name, final Type type,final boolean[] geomArrays) {super(name, type);this.geomArrays = geomArrays;}public String render(final List args,final SessionFactoryImplementor factory) {StringBuffer buf = new StringBuffer();buf.append("MDSYS.").append(getName()).append("(");for (int i = 0; i < args.size(); i++) {if (i > 0) {buf.append(",");}if (geomArrays[i]) {buf.append("MDSYS.ST_GEOMETRY.FROM_SDO_GEOM(").append(args.get(i)).append(")");} else {buf.append(args.get(i));}}buf.append(")");return (getType().getReturnedClass() == Geometry.class) ? buf.append(".geom").toString() : buf.toString();}}/*** HQL Implementation for the geometry ype function.*/private class GetGeometryTypeFunction extends SDOObjectMethod {public GetGeometryTypeFunction() {super("Get_GType", Hibernate.STRING);}public String render(final List args,final SessionFactoryImplementor factory) {StringBuffer buf = new StringBuffer();if (args.isEmpty()) {throw new IllegalArgumentException("First Argument in arglist must be object to which"+ " method is applied");}buf.append("CASE ").append(args.get(0)).append(".").append(getName()).append("()");buf.append(" WHEN 1 THEN 'POINT'").append(" WHEN 2 THEN 'LINESTRING'").append(" WHEN 3 THEN 'POLYGON'").append(" WHEN 5 THEN 'MULTIPOINT'").append(" WHEN 6 THEN 'MULTILINE'").append(" WHEN 7 THEN 'MULTIPOLGYON'").append(" END");return buf.toString();}}/*** HQL Spatial relation function.*/private class SpatialRelateFunction extends StandardSQLFunction {private final int relation;public SpatialRelateFunction(final String name, final int relation) {super(name, Hibernate.BOOLEAN);this.relation = relation;}public String render(final List args,final SessionFactoryImplementor factory) {if (args.size() < 2) {throw new QueryException("Spatial relate functions require at least two arguments");}return isOGCStrict() ? getOGCSpatialRelateSQL((String) args.get(0),(String) args.get(1), this.relation): getNativeSpatialRelateSQL((String) args.get(0),(String) args.get(1), this.relation);}}private class SpatialAnalysisFunction extends StandardSQLFunction {private final int analysis;public SpatialAnalysisFunction(String name, Type returnType,int analysis) {super(name, returnType);this.analysis = analysis;}public String render(List args, SessionFactoryImplementor factory) {return isOGCStrict() ? getSpatialAnalysisSQL(args, this.analysis,false) : getNativeSpatialAnalysisSQL(args, analysis);}}private class SpatialAggregationFunction extends StandardSQLFunction {private final int aggregation;private final boolean isProjection;public SpatialAggregationFunction(String name, Type returnType,boolean isProjection, int aggregation) {super(name, returnType);this.aggregation = aggregation;this.isProjection = isProjection;}public String render(List args, SessionFactoryImplementor factory) {return getNativeSpatialAggregateSQL((String) args.get(0),this.aggregation, isProjection);}}public final static String SHORT_NAME = "oraclespatial";private final static Log log = LogFactory.getLog(OracleSpatial10gDialect.class);private String OGC_STRICT = "OGC_STRICT";private Map<String, Boolean> features = new HashMap<String, Boolean>();public OracleSpatial10gDialect() {super();// initialise features to defaultfeatures.put(OGC_STRICT, new Boolean(true));// read configuration information from// classpathconfigure();// register geometry typeregisterColumnType(java.sql.Types.STRUCT, "geometry");// registering OGC functions// (spec_simplefeatures_sql_99-04.pdf)// section 2.1.1.1registerFunction("dimension", new DimensionFunction());registerFunction("geometrytype", new GetGeometryTypeFunction());registerFunction("srid", new SDOObjectProperty("SDO_SRID",Hibernate.INTEGER));registerFunction("envelope",new StandardSQLFunction("SDO_GEOM.SDO_MBR", new CustomType(SDOGeometryType.class, null)));registerFunction("astext", new AsTextFunction());// Can't get these functions to work on XE// registerFunction("asbinary", new// StandardSQLFunction("SDO_UTIL.TO_WKBGEOMETRY"));registerFunction("isempty", new WrappedOGCFunction("OGC_ISEMPTY",Hibernate.BOOLEAN, new boolean[] { true }));registerFunction("issimple", new WrappedOGCFunction("OGC_ISSIMPLE",Hibernate.BOOLEAN, new boolean[] { true }));registerFunction("boundary", new WrappedOGCFunction("OGC_BOUNDARY",new CustomType(SDOGeometryType.class, null),new boolean[] { true }));// registerFunction("area", new AreaFunction());// Register functions for spatial relation constructs// section 2.1.1.2registerFunction("overlaps", new SpatialRelateFunction("overlaps",SpatialRelation.OVERLAPS));registerFunction("intersects", new SpatialRelateFunction("intersects",SpatialRelation.INTERSECTS));registerFunction("contains", new SpatialRelateFunction("intersects",SpatialRelation.CONTAINS));registerFunction("crosses", new SpatialRelateFunction("intersects",SpatialRelation.CROSSES));registerFunction("disjoint", new SpatialRelateFunction("intersects",SpatialRelation.DISJOINT));registerFunction("equals", new SpatialRelateFunction("intersects",SpatialRelation.EQUALS));registerFunction("touches", new SpatialRelateFunction("intersects",SpatialRelation.TOUCHES));registerFunction("within", new SpatialRelateFunction("intersects",SpatialRelation.WITHIN));registerFunction("relate", new WrappedOGCFunction("OGC_RELATE",Hibernate.BOOLEAN, new boolean[] { true, true, false }));// Register spatial analysis functions.// Section 2.1.1.3registerFunction("distance", new SpatialAnalysisFunction("distance",Hibernate.DOUBLE, SpatialAnalysis.DISTANCE));registerFunction("buffer", new SpatialAnalysisFunction("buffer",new CustomType(SDOGeometryType.class, null),SpatialAnalysis.BUFFER));registerFunction("convexhull", new SpatialAnalysisFunction("convexhull", new CustomType(SDOGeometryType.class, null),SpatialAnalysis.CONVEXHULL));registerFunction("difference", new SpatialAnalysisFunction("difference", new CustomType(SDOGeometryType.class, null),SpatialAnalysis.DIFFERENCE));registerFunction("intersection", new SpatialAnalysisFunction("intersection", new CustomType(SDOGeometryType.class, null),SpatialAnalysis.INTERSECTION));registerFunction("symdifference", new SpatialAnalysisFunction("symdifference", new CustomType(SDOGeometryType.class, null),SpatialAnalysis.SYMDIFFERENCE));registerFunction("geomunion", new SpatialAnalysisFunction("union",new CustomType(SDOGeometryType.class, null),SpatialAnalysis.UNION));// we rename OGC union to geomunion because union is a reserved SQL// keyword. (See also postgis documentation).// Spatial Aggregation// registerFunction("lrsconcat", new SpatialAggregationFunction(// "lrsconcat", new CustomType(SDOGeometryType.class, null),// false, SpatialAggregation.LRS_CONCAT));}public UserType getGeometryUserType() {return new SDOGeometryType();}public String getNativeSpatialRelateSQL(String arg1, String arg2,int spatialRelation) {String mask = "";boolean negate = false;switch (spatialRelation) {case SpatialRelation.INTERSECTS:mask = "ANYINTERACT"; // OGC Compliance verifiedbreak;case SpatialRelation.CONTAINS:mask = "CONTAINS+COVERS";break;case SpatialRelation.CROSSES:throw new UnsupportedOperationException("Oracle Spatial does't have equivalent CROSSES relationship");case SpatialRelation.DISJOINT:mask = "ANYINTERACT";negate = true;break;case SpatialRelation.EQUALS:mask = "EQUAL";break;case SpatialRelation.OVERLAPS:mask = "OVERLAPBDYDISJOINT+OVERLAPBDYINTERSECT";break;case SpatialRelation.TOUCHES:mask = "TOUCH";break;case SpatialRelation.WITHIN:mask = "INSIDE+COVEREDBY";break;default:throw new IllegalArgumentException("undefined SpatialRelation passed (" + spatialRelation+ ")");}// The case formulation is necessary// to ensure that the expression can be used in the select and where// clauses.StringBuffer buffer = new StringBuffer("CASE SDO_RELATE(");buffer.append(arg1);buffer.append(",").append(arg2).append(",'mask=" + mask + "')");if (!negate) {buffer.append(" WHEN 'TRUE' THEN 1 ELSE 0 END");} else {buffer.append(" WHEN 'TRUE' THEN 0 ELSE 1 END");}return buffer.toString();}public String getOGCSpatialRelateSQL(String arg1, String arg2,int spatialRelation) {StringBuffer ogcFunction = new StringBuffer("MDSYS.");switch (spatialRelation) {case SpatialRelation.INTERSECTS:ogcFunction.append("OGC_INTERSECTS");break;case SpatialRelation.CONTAINS:ogcFunction.append("OGC_CONTAINS");break;case SpatialRelation.CROSSES:ogcFunction.append("OGC_CROSS");break;case SpatialRelation.DISJOINT:ogcFunction.append("OGC_DISJOINT");break;case SpatialRelation.EQUALS:ogcFunction.append("OGC_EQUALS");break;case SpatialRelation.OVERLAPS:ogcFunction.append("OGC_OVERLAP");break;case SpatialRelation.TOUCHES:ogcFunction.append("OGC_TOUCH");break;case SpatialRelation.WITHIN:ogcFunction.append("OGC_WITHIN");break;default:throw new IllegalArgumentException("Unknown SpatialRelation ("+ spatialRelation + ").");}ogcFunction.append("(").append("MDSYS.ST_GEOMETRY.FROM_SDO_GEOM(").append(arg1).append("),").append("MDSYS.ST_GEOMETRY.FROM_SDO_GEOM(").append(arg2).append(")").append(")");return ogcFunction.toString();}public String getNativeSpatialAggregateSQL(String arg1, int aggregation,boolean isProjection) {// StringBuffer aggregateFunction = new StringBuffer();//// SpatialAggregate sa = new SpatialAggregate(aggregation);//// if (sa._aggregateSyntax == null) {// throw new IllegalArgumentException("Unknown Spatial Aggregation ("// + aggregation + ").");// }//// aggregateFunction.append(sa._aggregateSyntax);//// aggregateFunction.append("(");// if (sa.isAggregateType()) {// aggregateFunction.append("SDOAGGRTYPE(");// }// aggregateFunction.append(arg1);// if (sa.isAggregateType()) {// aggregateFunction.append(", ").append(.001).append(")");// }// aggregateFunction.append(")");// if (isProjection) {// aggregateFunction.append(" as y");// }// return aggregateFunction.toString();return null;}private StringBuffer wrapInSTGeometry(String geomColumn, StringBuffer toAdd) {return toAdd.append("MDSYS.ST_GEOMETRY(").append(geomColumn).append(")");}public String getSpatialFilterExpression(String columnName) {StringBuffer buffer = new StringBuffer("SDO_FILTER(");// String pureColumnName =// columnName.substring(columnName.lastIndexOf(".")+1);// buffer.append("\"" + pureColumnName.toUpperCase() + "\"");buffer.append(columnName);buffer.append(",?) = 'TRUE' ");return buffer.toString();}public String getSpatialRelateSQL(String columnName, int spatialRelation,boolean useFilter) {String sql = (isOGCStrict() ? getOGCSpatialRelateSQL(columnName, "?",spatialRelation) : getNativeSpatialRelateSQL(columnName, "?",spatialRelation));sql += " = 1 and " + columnName + " is not null";return sql;}public String getSpatialAnalysisSQL(List args, int spatialAnalysisFunction,boolean useFilter) {return isOGCStrict() ? getOGCSpatialAnalysisSQL(args,spatialAnalysisFunction) : getNativeSpatialAnalysisSQL(args,spatialAnalysisFunction);}public String getSpatialAggregateSQL(String columnName,int spatialAggregateFunction, boolean isProjection) {return getNativeSpatialAggregateSQL(columnName,spatialAggregateFunction, isProjection);}private String getOGCSpatialAnalysisSQL(List args,int spatialAnalysisFunction) {boolean[] geomArgs;StringBuffer ogcFunction = new StringBuffer("MDSYS.");boolean isGeomReturn = true;switch (spatialAnalysisFunction) {case SpatialAnalysis.BUFFER:ogcFunction.append("OGC_BUFFER");geomArgs = new boolean[] { true, false };break;case SpatialAnalysis.CONVEXHULL:ogcFunction.append("OGC_CONVEXHULL");geomArgs = new boolean[] { true };break;case SpatialAnalysis.DIFFERENCE:ogcFunction.append("OGC_DIFFERENCE");geomArgs = new boolean[] { true, true };break;case SpatialAnalysis.DISTANCE:ogcFunction.append("OGC_DISTANCE");geomArgs = new boolean[] { true, true };isGeomReturn = false;break;case SpatialAnalysis.INTERSECTION:ogcFunction.append("OGC_INTERSECTION");geomArgs = new boolean[] { true, true };break;case SpatialAnalysis.SYMDIFFERENCE:ogcFunction.append("OGC_SYMMETRICDIFFERENCE");geomArgs = new boolean[] { true, true };break;case SpatialAnalysis.UNION:ogcFunction.append("OGC_UNION");geomArgs = new boolean[] { true, true };break;default:throw new IllegalArgumentException("Unknown SpatialAnalysisFunction ("+ spatialAnalysisFunction + ").");}if (args.size() < geomArgs.length)throw new QueryException("Insufficient arguments for spatial analysis function (function type: "+ spatialAnalysisFunction + ").");ogcFunction.append("(");for (int i = 0; i < geomArgs.length; i++) {if (i > 0)ogcFunction.append(",");if (geomArgs[i])wrapInSTGeometry((String) args.get(i), ogcFunction);elseogcFunction.append(args.get(i));}ogcFunction.append(")");if (isGeomReturn)ogcFunction.append(".geom");return ogcFunction.toString();}private String getNativeSpatialAnalysisSQL(List args, int spatialAnalysis) {return getOGCSpatialAnalysisSQL(args, spatialAnalysis);}private boolean isOGCStrict() {return ((Boolean) this.features.get(OGC_STRICT)).booleanValue();}/*** Returns the features supported by this Dialect.** @return Array of Feature names.*/public String[] getFeatures() {return this.features.keySet().toArray(new String[this.features.size()]);}public boolean getFeature(String name) {return this.features.get(name).booleanValue();}public void setFeature(String name, boolean value) {log.info("Setting feature: " + name + " to " + value);this.features.put(name, value);}private void configure() {ClassLoader loader = Thread.currentThread().getContextClassLoader();String propfileLoc = getClass().getCanonicalName() + ".properties";URL propfile = loader.getResource(propfileLoc);if (propfile != null) {InputStream is = null;log.info("properties file found: " + propfile);try {loader.getResource(getClass().getCanonicalName());is = propfile.openStream();PropertyFileReader reader = new PropertyFileReader(is);Properties props = reader.getProperties();for (String feature : getFeatures()) {String newVal = props.getProperty(feature);if (newVal != null) {setFeature(feature, Boolean.parseBoolean(newVal));}}} catch (IOException e) {log.warn("Problem reading properties file " + e);} finally {try {is.close();} catch (Exception e) {}}}}/*** Provides Aggregate type spatial function interpretation*/private class SpatialAggregate {boolean _aggregateType;String _aggregateSyntax;private final String SDO_AGGR = "SDO_AGGR_";protected SpatialAggregate() {}// private SpatialAggregate(int aggregation) {//// String specificAggrSyntax;//// switch (aggregation) {// case SpatialAggregation.LRS_CONCAT:// specificAggrSyntax = "LRS_CONCAT";// _aggregateType = true;// break;// case SpatialAggregation.CENTROID:// specificAggrSyntax = "CENTROID";// _aggregateType = true;// break;// case SpatialAggregation.CONCAT:// specificAggrSyntax = "CONCAT_LINES";// _aggregateType = false;// break;// default:// specificAggrSyntax = null;// break;// }// if (specificAggrSyntax != null) {// _aggregateSyntax = SDO_AGGR + specificAggrSyntax;// }// }public boolean isAggregateType() {return _aggregateType;}public String getAggregateSyntax() {return _aggregateSyntax;}}}