Package org.dbunit.ext.oracle

Source Code of org.dbunit.ext.oracle.OracleSdoGeometryDataType

/*
*
* The DbUnit Database Testing Framework
* Copyright (C)2002-2008, DbUnit.org
*
* 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
*
*/

package org.dbunit.ext.oracle;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import oracle.jdbc.OracleResultSet;
import oracle.jdbc.OraclePreparedStatement;
import oracle.sql.ORAData;

import org.dbunit.dataset.datatype.AbstractDataType;
import org.dbunit.dataset.datatype.TypeCastException;
import org.dbunit.dataset.ITable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* This class implements DataType for Oracle SDO_GEOMETRY type used in Oracle Spatial.
* See the Oracle Spatial Developer's Guide for details on SDO_GEOMETRY.  This class
* handles values similar to:
* <ul>
* <li>SDO_GEOMETRY(NULL, NULL, NULL, NULL, NULL)</li>
* <li>NULL</li>
* <li>SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(71.2988, 42.8052, NULL), NULL, NULL)</li>
* <li>SDO_GEOMETRY(3302, NULL, SDO_POINT_TYPE(96.8233, 32.5261, NULL), SDO_ELEM_INFO_ARRAY(1, 2, 1), SDO_ORDINATE_ARRAY(2, 2, 0, 2, 4, 2, 8, 4, 8, 12, 4, 12, 12, 10, NULL, 8, 10, 22, 5, 14, 27))</li>
* </ul>
*
* <p>
* For more information on oracle spatial support go to http://tahiti.oracle.com
* and search for &quot;spatial&quot;.  The developers guide is available at
* http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28400/toc.htm
* </p>
*
* <p>
* example table:
* <code>
*   CREATE TABLE cola_markets (
*     mkt_id NUMBER PRIMARY KEY,
*     name VARCHAR2(32),
*     shape SDO_GEOMETRY);
* </code>
* </p>
*
* <p>
* example insert:
* <code>
*   INSERT INTO cola_markets VALUES(
*     2,
*     'cola_b',
*     SDO_GEOMETRY(
*       2003,  -- two-dimensional polygon
*       NULL,
*       NULL,
*       SDO_ELEM_INFO_ARRAY(1,1003,1), -- one polygon (exterior polygon ring)
*       SDO_ORDINATE_ARRAY(5,1, 8,1, 8,6, 5,7, 5,1)
*     )
*    );
* </code>
* </p>
*
* <p>
* This class uses the following objects which were rendered using oracle jpub and then
* slightly customized to work with dbunit:
* <ul>
* <li>OracleSdoGeometry - corresponds to oracle SDO_GEOMETRY data type</li>
* <li>OracleSdoPointType - corresponds to oracle SDO_POINT_TYPE data type</li>
* <li>OracleSdoElemInfoArray - corresponds to oracle SDO_ELEM_INFO_ARRAY data type</li>
* <li>OracleSdoOridinateArray - corresponds to oracle SDO_ORDINATE_ARRAY data type</li>
* </ul>
* These classes were rendered via jpub
* (http://download.oracle.com/otn/utilities_drivers/jdbc/10201/jpub_102.zip)
* with the following command syntax:
* <code>
* ./jpub -user=YOUR_USER_ID/YOUR_PASSWORD -url=YOUR_JDBC_URL
*      -sql mdsys.sdo_geometry:OracleSdoGeometry,
*            mdsys.sdo_point_type:OracleSdoPointType,
*            mdsys.sdo_elem_info_array:OracleSdoElemInfoArray,
*            mdsys.sdo_ordinate_array:OracleSdoOrdinateArray
*      -dir=output_dir -methods=none -package=org.dbunit.ext.oracle -tostring=true
* </code>
* The equals and hashCode methods were then added so that the objects could be compared
* in test cases. Note that I did have to bash the jpub startup script (change classpath)
* because it assumes oracle 10g database but I ran it with 11g.  Theoretically, this
* process can be repeated for other custom oracle object data types.
* </p>
*
* @author clucas@e-miles.com
* @author Last changed by: $Author$
* @version $Revision$ $Date$
* @since <dbunit-version>
*/
public class OracleSdoGeometryDataType extends AbstractDataType
{
    /**
     * Logger for this class
     */
    private static final Logger logger = LoggerFactory.getLogger(OracleSdoGeometryDataType.class);

    private static final String NULL = "NULL";
    private static final String SDO_GEOMETRY = "SDO_GEOMETRY";

    // patterns for parsing out the various pieces of the string
    // representation of an sdo_geometry object
    private static final Pattern sdoGeometryPattern = Pattern.compile(
        "^(?:MDSYS\\.)?SDO_GEOMETRY\\s*\\(\\s*([^,\\s]+)\\s*,\\s*([^,\\s]+)\\s*,\\s*");
    private static final Pattern sdoPointTypePattern = Pattern.compile(
        "^(?:(?:(?:MDSYS\\.)?SDO_POINT_TYPE\\s*\\(\\s*([^,\\s]+)\\s*,\\s*([^,\\s]+)\\s*,\\s*([^,\\s\\)]+)\\s*\\))|(NULL))\\s*,\\s*");
    private static final Pattern sdoElemInfoArrayPattern = Pattern.compile(
        "^(?:(?:(?:(?:MDSYS\\.)?SDO_ELEM_INFO_ARRAY\\s*\\(([^\\)]*)\\))|(NULL)))\\s*,\\s*");
    private static final Pattern sdoOrdinateArrayPattern = Pattern.compile(
        "^(?:(?:(?:(?:MDSYS\\.)?SDO_ORDINATE_ARRAY\\s*\\(([^\\)]*)\\))|(NULL)))\\s*\\)\\s*");

    OracleSdoGeometryDataType ()
    {
        super(SDO_GEOMETRY, Types.STRUCT, OracleSdoGeometry.class, false);
    }

    public Object typeCast(Object value) throws TypeCastException
    {
        logger.debug("typeCast(value={}) - start", value);

        if (value == null || value == ITable.NO_VALUE)
        {
            return null;
        }


        if (value instanceof OracleSdoGeometry)
        {
            return (OracleSdoGeometry) value;
        }

        if (value instanceof String)
        {
            // attempt to parse the SDO_GEOMETRY
            try
            {
                // all upper case for parse purposes
                String upperVal = ((String) value).toUpperCase().trim();
                if (NULL.equals(upperVal))
                {
                    return null;
                }

                // parse out sdo_geometry
                Matcher sdoGeometryMatcher = sdoGeometryPattern.matcher(upperVal);
                if (! sdoGeometryMatcher.find())
                {
                    throw new TypeCastException(value, this);
                }
                BigDecimal gtype = NULL.equals(sdoGeometryMatcher.group(1)) ?
                    null : new BigDecimal(sdoGeometryMatcher.group(1));
                BigDecimal srid = NULL.equals(sdoGeometryMatcher.group(2)) ?
                    null : new BigDecimal(sdoGeometryMatcher.group(2));

                // parse out sdo_point_type
                upperVal = upperVal.substring(sdoGeometryMatcher.end());
                Matcher sdoPointTypeMatcher = sdoPointTypePattern.matcher(upperVal);
                if (! sdoPointTypeMatcher.find())
                {
                    throw new TypeCastException(value, this);
                }

                OracleSdoPointType sdoPoint;
                if (NULL.equals(sdoPointTypeMatcher.group(4)))
                {
                    sdoPoint = null;
                }
                else
                {
                    sdoPoint = new OracleSdoPointType(
                        NULL.equals(sdoPointTypeMatcher.group(1)) ? null :
                            new BigDecimal(sdoPointTypeMatcher.group(1)),
                        NULL.equals(sdoPointTypeMatcher.group(2)) ? null :
                            new BigDecimal(sdoPointTypeMatcher.group(2)),
                        NULL.equals(sdoPointTypeMatcher.group(3)) ? null :
                            new BigDecimal(sdoPointTypeMatcher.group(3)));
                }

                // parse out sdo_elem_info_array
                upperVal = upperVal.substring(sdoPointTypeMatcher.end());
                Matcher sdoElemInfoArrayMatcher = sdoElemInfoArrayPattern.matcher(upperVal);
                if (! sdoElemInfoArrayMatcher.find())
                {
                    throw new TypeCastException(value, this);
                }

                OracleSdoElemInfoArray sdoElemInfoArray;
                if (NULL.equals(sdoElemInfoArrayMatcher.group(2)))
                {
                    sdoElemInfoArray = null;
                }
                else
                {
                    String [] elemInfoStrings = sdoElemInfoArrayMatcher.group(1).
                        trim().split("\\s*,\\s*");
                    if (elemInfoStrings.length == 1 && "".equals(elemInfoStrings[0]))
                    {
                        sdoElemInfoArray = new OracleSdoElemInfoArray();
                    }
                    else
                    {
                        BigDecimal [] elemInfos = new BigDecimal[elemInfoStrings.length];
                        for (int index = 0; index < elemInfoStrings.length; index++)
                        {
                            elemInfos[index] = NULL.equals(elemInfoStrings[index]) ?
                                null : new BigDecimal(elemInfoStrings[index]);
                        }
                        sdoElemInfoArray = new OracleSdoElemInfoArray(elemInfos);
                    }
                }

                // parse out sdo_ordinate_array
                upperVal = upperVal.substring(sdoElemInfoArrayMatcher.end());
                Matcher sdoOrdinateArrayMatcher = sdoOrdinateArrayPattern.matcher(upperVal);
                if (! sdoOrdinateArrayMatcher.find())
                {
                    throw new TypeCastException(value, this);
                }

                OracleSdoOrdinateArray sdoOrdinateArray;
                if (NULL.equals(sdoOrdinateArrayMatcher.group(2)))
                {
                    sdoOrdinateArray = null;
                }
                else
                {
                    String [] ordinateStrings = sdoOrdinateArrayMatcher.group(1).
                        trim().split("\\s*,\\s*");
                    if (ordinateStrings.length == 1 && "".equals(ordinateStrings[0]))
                    {
                        sdoOrdinateArray = new OracleSdoOrdinateArray();
                    }
                    else
                    {
                        BigDecimal [] ordinates = new BigDecimal[ordinateStrings.length];
                        for (int index = 0; index < ordinateStrings.length; index++)
                        {
                            ordinates[index] = NULL.equals(ordinateStrings[index]) ?
                                null : new BigDecimal(ordinateStrings[index]);
                        }
                        sdoOrdinateArray = new OracleSdoOrdinateArray(ordinates);
                    }
                }

                OracleSdoGeometry sdoGeometry = new OracleSdoGeometry(
                    gtype, srid, sdoPoint, sdoElemInfoArray, sdoOrdinateArray);

                return sdoGeometry;
            }
            catch (SQLException e)
            {
                throw new TypeCastException(value, this, e);
            }
            catch (NumberFormatException e)
            {
                throw new TypeCastException(value, this, e);
            }
        }

        throw new TypeCastException(value, this);
    }


    public Object getSqlValue(int column, ResultSet resultSet)
        throws SQLException, TypeCastException
    {
        if(logger.isDebugEnabled())
            logger.debug("getSqlValue(column={}, resultSet={}) - start",
            new Integer(column), resultSet);

        Object data = null;
        try
        {
            data =  ((OracleResultSet) resultSet).
                getORAData(column, OracleSdoGeometry.getORADataFactory());

            // It would be preferable to return the actual object, but there are
            // a few dbunit issues with this:
            //
            // 1. Dbunit does not support nulls for user defined types (at least
            //    with oracle.)  PreparedStatement.setNull(int, int) is always used
            //    but PreparedStatement.setNull(int, int, String) is required
            //    for sdo_geometry (and other similar custom object types).
            //
            // 2. Dbunit does not support rendering custom objects (such as
            //    OracleSdoGeometry) as strings.
            //
            // So, instead we return the object as a String or "NULL".

            // return data;

            if (data != null)
            {
                return data.toString();
            }
            else
            {
                // return a string instead of null so that it can be interpreted
                // in typeCast.  DBUnit does not handle PreparedStatement.setNull
                // for user defined types.
                return NULL;
            }

        }
        catch (SQLException e)
        {
            throw new TypeCastException(data, this, e);
        }
    }

    public void setSqlValue(Object value, int column, PreparedStatement statement)
        throws SQLException, TypeCastException
    {
        Object castValue = typeCast(value);
        if (castValue == null)
        {
            statement.setNull(column, OracleSdoGeometry._SQL_TYPECODE,
                OracleSdoGeometry._SQL_NAME);
        }
        else
        {
            ((OraclePreparedStatement) statement).setORAData(column, (ORAData) castValue);
        }
    }

    /**
     * This method is copied from AbstractDataType and customized to call equals
     * after the typeCast because OracleSdoGeometry objects are not Comparables
     * but can test for equality (via equals method.)  It is needed for test
     * cases that check for equality between data in xml files and data read
     * from the database.
     */
    public int compare(Object o1, Object o2) throws TypeCastException
    {
        logger.debug("compare(o1={}, o2={}) - start", o1, o2);

        try
        {
            // New in 2.3: Object level check for equality - should give massive performance improvements
            // in the most cases because the typecast can be avoided (null values and equal objects)
            if(areObjectsEqual(o1, o2))
            {
                return 0;
            }


            // Comparable check based on the results of method "typeCast"
            Object value1 = typeCast(o1);
            Object value2 = typeCast(o2);

            // Check for "null"s again because typeCast can produce them

            if (value1 == null && value2 == null)
            {
                return 0;
            }

            if (value1 == null && value2 != null)
            {
                return -1;
            }

            if (value1 != null && value2 == null)
            {
                return 1;
            }

            if (value1.equals(value2))
            {
                return 0;
            }

            return compareNonNulls(value1, value2);

        }
        catch (ClassCastException e)
        {
            throw new TypeCastException(e);
        }
    }

}
TOP

Related Classes of org.dbunit.ext.oracle.OracleSdoGeometryDataType

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.