/*
* Copyright (c) 2004-2015 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
* which accompanies this distribution, and is available at
* http://opensource.org/licenses/BSD-3-Clause
*
* Contributors:
* Tada AB
* Chapman Flack
*/
package org.postgresql.pljava.example.annotation;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
import java.util.logging.Logger;
import org.postgresql.pljava.annotation.Function;
import org.postgresql.pljava.annotation.SQLAction;
import org.postgresql.pljava.annotation.SQLType;
import org.postgresql.pljava.annotation.BaseUDT;
import static org.postgresql.pljava.annotation.Function.Effects.IMMUTABLE;
import static
org.postgresql.pljava.annotation.Function.OnNullInput.RETURNS_NULL;
/**
* Complex (re and im parts are doubles) implemented in Java as a scalar UDT.
*/
@SQLAction(requires={
"scalar complex type", "complex assertHasValues", "complexscalar boot fn"
}, install={
"SELECT javatest.complexscalar()",
"SELECT javatest.assertHasValues(" +
" CAST('(1,2)' AS javatest.complex), 1, 2)"
}
)
@BaseUDT(schema="javatest", name="complex", provides="scalar complex type",
internalLength=16, alignment=BaseUDT.Alignment.DOUBLE)
public class ComplexScalar implements SQLData {
private static Logger s_logger = Logger.getAnonymousLogger();
/**
* Return the same 'complex' passed in, logging its contents at level INFO.
* @param cpl any instance of this UDT
* @return the same instance passed in
*/
@Function(requires="scalar complex type", type="javatest.complex",
schema="javatest", name="logcomplex", effects=IMMUTABLE,
onNullInput=RETURNS_NULL)
public static ComplexScalar logAndReturn(
@SQLType("javatest.complex") ComplexScalar cpl) {
s_logger.info(cpl.getSQLTypeName() + cpl);
return cpl;
}
/**
* Assert a 'complex' has given re and im values, to test that its
* representation in Java corresponds to what PostgreSQL sees.
* @param cpl an instance of this UDT
* @param re the 'real' value it should have
* @param im the 'imaginary' value it should have
* @throws SQLException if the values do not match
*/
@Function(schema="javatest",
requires="scalar complex type", provides="complex assertHasValues",
effects=IMMUTABLE, onNullInput=RETURNS_NULL)
public static void assertHasValues(
@SQLType("javatest.complex") ComplexScalar cpl, double re, double im)
throws SQLException
{
if ( cpl.m_x != re || cpl.m_y != im )
throw new SQLException("assertHasValues fails");
}
@Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
public static ComplexScalar parse(String input, String typeName)
throws SQLException {
try {
StreamTokenizer tz = new StreamTokenizer(new StringReader(input));
if (tz.nextToken() == '('
&& tz.nextToken() == StreamTokenizer.TT_NUMBER) {
double x = tz.nval;
if (tz.nextToken() == ','
&& tz.nextToken() == StreamTokenizer.TT_NUMBER) {
double y = tz.nval;
if (tz.nextToken() == ')') {
s_logger.info(typeName + " from string");
return new ComplexScalar(x, y, typeName);
}
}
}
throw new SQLException("Unable to parse complex from string \""
+ input + '"');
} catch (IOException e) {
throw new SQLException(e.getMessage());
}
}
private double m_x;
private double m_y;
private String m_typeName;
public ComplexScalar() {
}
public ComplexScalar(double x, double y, String typeName) {
m_x = x;
m_y = y;
m_typeName = typeName;
}
@Override
public String getSQLTypeName() {
return m_typeName;
}
@Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
@Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
s_logger.info(typeName + " from SQLInput");
m_x = stream.readDouble();
m_y = stream.readDouble();
m_typeName = typeName;
}
@Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
@Override
public String toString() {
s_logger.info(m_typeName + " toString");
StringBuffer sb = new StringBuffer();
sb.append('(');
sb.append(m_x);
sb.append(',');
sb.append(m_y);
sb.append(')');
return sb.toString();
}
@Function(effects=IMMUTABLE, onNullInput=RETURNS_NULL)
@Override
public void writeSQL(SQLOutput stream) throws SQLException {
s_logger.info(m_typeName + " to SQLOutput");
stream.writeDouble(m_x);
stream.writeDouble(m_y);
}
/**
* A no-op function that forces the ComplexScalar class to be loaded.
* This is only necessary because the deployment-descriptor install
* actions contain a query making use of this type, and PostgreSQL does
* not expect type in/out/send/recv functions to need an updated
* snapshot, so it will try to find this class in the snapshot from
* before the jar was installed, and fail. By providing this function,
* which defaults to volatile so it gets an updated snapshot, and
* calling it first, the class will be found and loaded; once it is
* loaded, the user-defined type operations are able to find it.
* <p>
* Again, this is only an issue when trying to make use of the newly
* loaded UDT from right within the deployment descriptor for the jar.
*/
@Function(schema="javatest", provides="complexscalar boot fn")
public static void ComplexScalar()
{
}
}