summaryrefslogtreecommitdiff
path: root/src/interfaces/jdbc/postgresql
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1998-01-13 02:19:56 +0000
committerMarc G. Fournier <scrappy@hub.org>1998-01-13 02:19:56 +0000
commit06bad7807584351c2ab1cd544d677fa0ea087544 (patch)
treec17f9da61c34ca63d8f4e4ba56cf1980d6a62c13 /src/interfaces/jdbc/postgresql
parentc77c608aa2aba1d8b8e70f6d35371d09b48ff584 (diff)
downloadpostgresql-06bad7807584351c2ab1cd544d677fa0ea087544.tar.gz
Oops...missed over half the patch :(
Diffstat (limited to 'src/interfaces/jdbc/postgresql')
-rw-r--r--src/interfaces/jdbc/postgresql/fastpath/Fastpath.java291
-rw-r--r--src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java106
-rw-r--r--src/interfaces/jdbc/postgresql/geometric/PGbox.java105
-rw-r--r--src/interfaces/jdbc/postgresql/geometric/PGcircle.java108
-rw-r--r--src/interfaces/jdbc/postgresql/geometric/PGlseg.java100
-rw-r--r--src/interfaces/jdbc/postgresql/geometric/PGpath.java145
-rw-r--r--src/interfaces/jdbc/postgresql/geometric/PGpoint.java167
-rw-r--r--src/interfaces/jdbc/postgresql/geometric/PGpolygon.java105
-rw-r--r--src/interfaces/jdbc/postgresql/largeobject/LargeObject.java253
-rw-r--r--src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java205
-rw-r--r--src/interfaces/jdbc/postgresql/util/PGobject.java102
-rw-r--r--src/interfaces/jdbc/postgresql/util/PGtokenizer.java197
12 files changed, 1884 insertions, 0 deletions
diff --git a/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java
new file mode 100644
index 0000000000..ce278acef7
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/fastpath/Fastpath.java
@@ -0,0 +1,291 @@
+package postgresql.fastpath;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This class implements the Fastpath api.
+ *
+ * <p>This is a means of executing functions imbeded in the postgresql backend
+ * from within a java application.
+ *
+ * <p>It is based around the file src/interfaces/libpq/fe-exec.c
+ *
+ *
+ * <p><b>Implementation notes:</b>
+ *
+ * <p><b><em>Network protocol:</em></b>
+ *
+ * <p>The code within the backend reads integers in reverse.
+ *
+ * <p>There is work in progress to convert all of the protocol to
+ * network order but it may not be there for v6.3
+ *
+ * <p>When fastpath switches, simply replace SendIntegerReverse() with
+ * SendInteger()
+ *
+ * @see postgresql.FastpathFastpathArg
+ * @see postgresql.LargeObject
+ */
+public class Fastpath
+{
+ // This maps the functions names to their id's (possible unique just
+ // to a connection).
+ protected Hashtable func = new Hashtable();
+
+ protected postgresql.Connection conn; // our connection
+ protected postgresql.PG_Stream stream; // the network stream
+
+ /**
+ * Initialises the fastpath system
+ *
+ * <p><b>Important Notice</b>
+ * <br>This is called from postgresql.Connection, and should not be called
+ * from client code.
+ *
+ * @param conn postgresql.Connection to attach to
+ * @param stream The network stream to the backend
+ */
+ public Fastpath(postgresql.Connection conn,postgresql.PG_Stream stream)
+ {
+ this.conn=conn;
+ this.stream=stream;
+ DriverManager.println("Fastpath initialised");
+ }
+
+ /**
+ * Send a function call to the PostgreSQL backend
+ *
+ * @param fnid Function id
+ * @param resulttype True if the result is an integer, false for other results
+ * @param args FastpathArguments to pass to fastpath
+ * @return null if no data, Integer if an integer result, or byte[] otherwise
+ * @exception SQLException if a database-access error occurs.
+ */
+ public Object fastpath(int fnid,boolean resulttype,FastpathArg[] args) throws SQLException
+ {
+ // send the function call
+ try {
+ // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
+ // that confuses the backend. The 0 terminates the command line.
+ stream.SendInteger(70,1);
+ stream.SendInteger(0,1);
+
+ stream.SendIntegerReverse(fnid,4);
+ stream.SendIntegerReverse(args.length,4);
+
+ for(int i=0;i<args.length;i++)
+ args[i].send(stream);
+
+ // This is needed, otherwise data can be lost
+ stream.flush();
+
+ } catch(IOException ioe) {
+ throw new SQLException("Failed to send fastpath call "+fnid+"\n"+ioe);
+ }
+
+ // Now handle the result
+
+ // We should get 'V' on sucess or 'E' on error. Anything else is treated
+ // as an error.
+ //int in = stream.ReceiveChar();
+ //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
+ //if(in!='V') {
+ //if(in=='E')
+ //throw new SQLException(stream.ReceiveString(4096));
+ //throw new SQLException("Fastpath: expected 'V' from backend, got "+((char)in));
+ //}
+
+ // Now loop, reading the results
+ Object result = null; // our result
+ while(true) {
+ int in = stream.ReceiveChar();
+ DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
+ switch(in)
+ {
+ case 'V':
+ break;
+
+ //------------------------------
+ // Function returned properly
+ //
+ case 'G':
+ int sz = stream.ReceiveInteger(4);
+ DriverManager.println("G: size="+sz); //debug
+
+ // Return an Integer if
+ if(resulttype)
+ result = new Integer(stream.ReceiveInteger(sz));
+ else {
+ byte buf[] = new byte[sz];
+ stream.Receive(buf,0,sz);
+ result = buf;
+ }
+ break;
+
+ //------------------------------
+ // Error message returned
+ case 'E':
+ throw new SQLException("Fastpath: "+stream.ReceiveString(4096));
+
+ //------------------------------
+ // Notice from backend
+ case 'N':
+ conn.addWarning(stream.ReceiveString(4096));
+ break;
+
+ //------------------------------
+ // End of results
+ //
+ // Here we simply return res, which would contain the result
+ // processed earlier. If no result, this already contains null
+ case '0':
+ DriverManager.println("returning "+result);
+ return result;
+
+ default:
+ throw new SQLException("Fastpath: protocol error. Got '"+((char)in)+"'");
+ }
+ }
+ }
+
+ /**
+ * Send a function call to the PostgreSQL backend by name.
+ *
+ * Note: the mapping for the procedure name to function id needs to exist,
+ * usually to an earlier call to addfunction().
+ *
+ * This is the prefered method to call, as function id's can/may change
+ * between versions of the backend.
+ *
+ * For an example of how this works, refer to postgresql.LargeObject
+ *
+ * @param name Function name
+ * @param resulttype True if the result is an integer, false for other
+ * results
+ * @param args FastpathArguments to pass to fastpath
+ * @return null if no data, Integer if an integer result, or byte[] otherwise
+ * @exception SQLException if name is unknown or if a database-access error
+ * occurs.
+ * @see postgresql.LargeObject
+ */
+ public Object fastpath(String name,boolean resulttype,FastpathArg[] args) throws SQLException
+ {
+ DriverManager.println("Fastpath: calling "+name);
+ return fastpath(getID(name),resulttype,args);
+ }
+
+ /**
+ * This convenience method assumes that the return value is an Integer
+ * @param name Function name
+ * @param args Function arguments
+ * @return integer result
+ * @exception SQLException if a database-access error occurs or no result
+ */
+ public int getInteger(String name,FastpathArg[] args) throws SQLException
+ {
+ Integer i = (Integer)fastpath(name,true,args);
+ if(i==null)
+ throw new SQLException("Fastpath:"+name+": no result returned, expected integer");
+ return i.intValue();
+ }
+
+ /**
+ * This convenience method assumes that the return value is an Integer
+ * @param name Function name
+ * @param args Function arguments
+ * @return byte[] array containing result
+ * @exception SQLException if a database-access error occurs or no result
+ */
+ public byte[] getData(String name,FastpathArg[] args) throws SQLException
+ {
+ return (byte[])fastpath(name,false,args);
+ }
+
+ /**
+ * This adds a function to our lookup table.
+ *
+ * <p>User code should use the addFunctions method, which is based upon a
+ * query, rather than hard coding the oid. The oid for a function is not
+ * guaranteed to remain static, even on different servers of the same
+ * version.
+ *
+ * @param name Function name
+ * @param fnid Function id
+ */
+ public void addFunction(String name,int fnid)
+ {
+ func.put(name,new Integer(fnid));
+ }
+
+ /**
+ * This takes a ResultSet containing two columns. Column 1 contains the
+ * function name, Column 2 the oid.
+ *
+ * <p>It reads the entire ResultSet, loading the values into the function
+ * table.
+ *
+ * <p><b>REMEMBER</b> to close() the resultset after calling this!!
+ *
+ * <p><b><em>Implementation note about function name lookups:</em></b>
+ *
+ * <p>PostgreSQL stores the function id's and their corresponding names in
+ * the pg_proc table. To speed things up locally, instead of querying each
+ * function from that table when required, a Hashtable is used. Also, only
+ * the function's required are entered into this table, keeping connection
+ * times as fast as possible.
+ *
+ * <p>The postgresql.LargeObject class performs a query upon it's startup,
+ * and passes the returned ResultSet to the addFunctions() method here.
+ *
+ * <p>Once this has been done, the LargeObject api refers to the functions by
+ * name.
+ *
+ * <p>Dont think that manually converting them to the oid's will work. Ok,
+ * they will for now, but they can change during development (there was some
+ * discussion about this for V7.0), so this is implemented to prevent any
+ * unwarranted headaches in the future.
+ *
+ * @param rs ResultSet
+ * @exception SQLException if a database-access error occurs.
+ * @see postgresql.LargeObjectManager
+ */
+ public void addFunctions(ResultSet rs) throws SQLException
+ {
+ while(rs.next()) {
+ func.put(rs.getString(1),new Integer(rs.getInt(2)));
+ }
+ }
+
+ /**
+ * This returns the function id associated by its name
+ *
+ * <p>If addFunction() or addFunctions() have not been called for this name,
+ * then an SQLException is thrown.
+ *
+ * @param name Function name to lookup
+ * @return Function ID for fastpath call
+ * @exception SQLException is function is unknown.
+ */
+ public int getID(String name) throws SQLException
+ {
+ Integer id = (Integer)func.get(name);
+
+ // may be we could add a lookup to the database here, and store the result
+ // in our lookup table, throwing the exception if that fails.
+ // We must, however, ensure that if we do, any existing ResultSet is
+ // unaffected, otherwise we could break user code.
+ //
+ // so, until we know we can do this (needs testing, on the TODO list)
+ // for now, we throw the exception and do no lookups.
+ if(id==null)
+ throw new SQLException("Fastpath: function "+name+" is unknown");
+
+ return id.intValue();
+ }
+}
+
diff --git a/src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java b/src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java
new file mode 100644
index 0000000000..9b800073c2
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/fastpath/FastpathArg.java
@@ -0,0 +1,106 @@
+package postgresql.fastpath;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * Each fastpath call requires an array of arguments, the number and type
+ * dependent on the function being called.
+ *
+ * <p>This class implements methods needed to provide this capability.
+ *
+ * <p>For an example on how to use this, refer to the postgresql.largeobject
+ * package
+ *
+ * @see postgresql.fastpath.Fastpath
+ * @see postgresql.largeobject.LargeObjectManager
+ * @see postgresql.largeobject.LargeObject
+ */
+public class FastpathArg
+{
+ /**
+ * Type of argument, true=integer, false=byte[]
+ */
+ public boolean type;
+
+ /**
+ * Integer value if type=true
+ */
+ public int value;
+
+ /**
+ * Byte value if type=false;
+ */
+ public byte[] bytes;
+
+ /**
+ * Constructs an argument that consists of an integer value
+ * @param value int value to set
+ */
+ public FastpathArg(int value)
+ {
+ type=true;
+ this.value=value;
+ }
+
+ /**
+ * Constructs an argument that consists of an array of bytes
+ * @param bytes array to store
+ */
+ public FastpathArg(byte bytes[])
+ {
+ type=false;
+ this.bytes=bytes;
+ }
+
+ /**
+ * Constructs an argument that consists of part of a byte array
+ * @param buf source array
+ * @param off offset within array
+ * @param len length of data to include
+ */
+ public FastpathArg(byte buf[],int off,int len)
+ {
+ type=false;
+ bytes = new byte[len];
+ System.arraycopy(buf,off,bytes,0,len);
+ }
+
+ /**
+ * Constructs an argument that consists of a String.
+ * @param s String to store
+ */
+ public FastpathArg(String s)
+ {
+ this(s.getBytes());
+ }
+
+ /**
+ * This sends this argument down the network stream.
+ *
+ * <p>The stream sent consists of the length.int4 then the contents.
+ *
+ * <p><b>Note:</b> This is called from Fastpath, and cannot be called from
+ * client code.
+ *
+ * @param s output stream
+ * @exception IOException if something failed on the network stream
+ */
+ protected void send(postgresql.PG_Stream s) throws IOException
+ {
+ if(type) {
+ // argument is an integer
+ s.SendIntegerReverse(4,4); // size of an integer
+ s.SendIntegerReverse(value,4); // integer value of argument
+ } else {
+ // argument is a byte array
+ s.SendIntegerReverse(bytes.length,4); // size of array
+ s.Send(bytes);
+ }
+ }
+}
+
diff --git a/src/interfaces/jdbc/postgresql/geometric/PGbox.java b/src/interfaces/jdbc/postgresql/geometric/PGbox.java
new file mode 100644
index 0000000000..1814832690
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/geometric/PGbox.java
@@ -0,0 +1,105 @@
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This represents the box datatype within postgresql.
+ */
+public class PGbox extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * These are the two points.
+ */
+ public PGpoint point[] = new PGpoint[2];
+
+ /**
+ * @param x1 first x coordinate
+ * @param y1 first y coordinate
+ * @param x2 second x coordinate
+ * @param y2 second y coordinate
+ */
+ public PGbox(double x1,double y1,double x2,double y2)
+ {
+ this();
+ this.point[0] = new PGpoint(x1,y1);
+ this.point[1] = new PGpoint(x2,y2);
+ }
+
+ /**
+ * @param p1 first point
+ * @param p2 second point
+ */
+ public PGbox(PGpoint p1,PGpoint p2)
+ {
+ this();
+ this.point[0] = p1;
+ this.point[1] = p2;
+ }
+
+ /**
+ * @param s Box definition in PostgreSQL syntax
+ * @exception SQLException if definition is invalid
+ */
+ public PGbox(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * Required constructor
+ */
+ public PGbox()
+ {
+ setType("box");
+ }
+
+ /**
+ * This method sets the value of this object. It should be overidden,
+ * but still called by subclasses.
+ *
+ * @param value a string representation of the value of the object
+ * @exception SQLException thrown if value is invalid for this type
+ */
+ public void setValue(String value) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(value,',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of box failed - "+value);
+
+ point[0] = new PGpoint(t.getToken(0));
+ point[1] = new PGpoint(t.getToken(1));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGbox) {
+ PGbox p = (PGbox)obj;
+ return (p.point[0].equals(point[0]) && p.point[1].equals(point[1])) ||
+ (p.point[0].equals(point[1]) && p.point[1].equals(point[0]));
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGbox((PGpoint)point[0].clone(),(PGpoint)point[1].clone());
+ }
+
+ /**
+ * @return the PGbox in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return point[0].toString()+","+point[1].toString();
+ }
+}
diff --git a/src/interfaces/jdbc/postgresql/geometric/PGcircle.java b/src/interfaces/jdbc/postgresql/geometric/PGcircle.java
new file mode 100644
index 0000000000..105ed91a2f
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/geometric/PGcircle.java
@@ -0,0 +1,108 @@
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This represents postgresql's circle datatype, consisting of a point and
+ * a radius
+ */
+public class PGcircle extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * This is the centre point
+ */
+ public PGpoint center;
+
+ /**
+ * This is the radius
+ */
+ double radius;
+
+ /**
+ * @param x coordinate of centre
+ * @param y coordinate of centre
+ * @param r radius of circle
+ */
+ public PGcircle(double x,double y,double r)
+ {
+ this(new PGpoint(x,y),r);
+ }
+
+ /**
+ * @param c PGpoint describing the circle's centre
+ * @param r radius of circle
+ */
+ public PGcircle(PGpoint c,double r)
+ {
+ this();
+ this.center = c;
+ this.radius = r;
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGcircle(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * This constructor is used by the driver.
+ */
+ public PGcircle()
+ {
+ setType("circle");
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removeAngle(s),',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of circle failed - "+s);
+
+ try {
+ center = new PGpoint(t.getToken(0));
+ radius = Double.valueOf(t.getToken(1)).doubleValue();
+ } catch(NumberFormatException e) {
+ throw new SQLException("conversion of circle failed - "+s+" - +"+e.toString());
+ }
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGcircle) {
+ PGcircle p = (PGcircle)obj;
+ return p.center.equals(center) && p.radius==radius;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGcircle((PGpoint)center.clone(),radius);
+ }
+
+ /**
+ * @return the PGcircle in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return "<"+center+","+radius+">";
+ }
+}
diff --git a/src/interfaces/jdbc/postgresql/geometric/PGlseg.java b/src/interfaces/jdbc/postgresql/geometric/PGlseg.java
new file mode 100644
index 0000000000..9daddfc91a
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/geometric/PGlseg.java
@@ -0,0 +1,100 @@
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This implements a lseg (line segment) consisting of two points
+ */
+public class PGlseg extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * These are the two points.
+ */
+ public PGpoint point[] = new PGpoint[2];
+
+ /**
+ * @param x1 coordinate for first point
+ * @param y1 coordinate for first point
+ * @param x2 coordinate for second point
+ * @param y2 coordinate for second point
+ */
+ public PGlseg(double x1,double y1,double x2,double y2)
+ {
+ this(new PGpoint(x1,y1),new PGpoint(x2,y2));
+ }
+
+ /**
+ * @param p1 first point
+ * @param p2 second point
+ */
+ public PGlseg(PGpoint p1,PGpoint p2)
+ {
+ this();
+ this.point[0] = p1;
+ this.point[1] = p2;
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGlseg(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * reuired by the driver
+ */
+ public PGlseg()
+ {
+ setType("lseg");
+ }
+
+ /**
+ * @param s Definition of the line segment in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removeBox(s),',');
+ if(t.getSize() != 2)
+ throw new SQLException("conversion of lseg failed - "+s);
+
+ point[0] = new PGpoint(t.getToken(0));
+ point[1] = new PGpoint(t.getToken(1));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGlseg) {
+ PGlseg p = (PGlseg)obj;
+ return (p.point[0].equals(point[0]) && p.point[1].equals(point[1])) ||
+ (p.point[0].equals(point[1]) && p.point[1].equals(point[0]));
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGlseg((PGpoint)point[0].clone(),(PGpoint)point[1].clone());
+ }
+
+ /**
+ * @return the PGlseg in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return "["+point[0]+","+point[1]+"]";
+ }
+}
diff --git a/src/interfaces/jdbc/postgresql/geometric/PGpath.java b/src/interfaces/jdbc/postgresql/geometric/PGpath.java
new file mode 100644
index 0000000000..45c162ac8a
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/geometric/PGpath.java
@@ -0,0 +1,145 @@
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This implements a path (a multiple segmented line, which may be closed)
+ */
+public class PGpath extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * True if the path is open, false if closed
+ */
+ public boolean open;
+
+ /**
+ * The points defining this path
+ */
+ public PGpoint points[];
+
+ /**
+ * @param points the PGpoints that define the path
+ * @param open True if the path is open, false if closed
+ */
+ public PGpath(PGpoint[] points,boolean open)
+ {
+ this();
+ this.points = points;
+ this.open = open;
+ }
+
+ /**
+ * Required by the driver
+ */
+ public PGpath()
+ {
+ setType("path");
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGpath(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * @param s Definition of the path in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ // First test to see if were open
+ if(s.startsWith("[") && s.endsWith("]")) {
+ open = true;
+ s = PGtokenizer.removeBox(s);
+ } else if(s.startsWith("(") && s.endsWith(")")) {
+ open = false;
+ s = PGtokenizer.removePara(s);
+ } else
+ throw new SQLException("cannot tell if path is open or closed");
+
+ PGtokenizer t = new PGtokenizer(s,',');
+ int npoints = t.getSize();
+ points = new PGpoint[npoints];
+ for(int p=0;p<npoints;p++)
+ points[p] = new PGpoint(t.getToken(p));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGpath) {
+ PGpath p = (PGpath)obj;
+
+ if(p.points.length != points.length)
+ return false;
+
+ if(p.open != open)
+ return false;
+
+ for(int i=0;i<points.length;i++)
+ if(!points[i].equals(p.points[i]))
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ PGpoint ary[] = new PGpoint[points.length];
+ for(int i=0;i<points.length;i++)
+ ary[i]=(PGpoint)points[i].clone();
+ return new PGpath(ary,open);
+ }
+
+ /**
+ * This returns the polygon in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ StringBuffer b = new StringBuffer(open?"[":"(");
+
+ for(int p=0;p<points.length;p++) {
+ if(p>0) b.append(",");
+ b.append(points[p].toString());
+ }
+ b.append(open?"]":")");
+
+ return b.toString();
+ }
+
+ public boolean isOpen()
+ {
+ return open;
+ }
+
+ public boolean isClosed()
+ {
+ return !open;
+ }
+
+ public void closePath()
+ {
+ open = false;
+ }
+
+ public void openPath()
+ {
+ open = true;
+ }
+
+}
diff --git a/src/interfaces/jdbc/postgresql/geometric/PGpoint.java b/src/interfaces/jdbc/postgresql/geometric/PGpoint.java
new file mode 100644
index 0000000000..29f2c75346
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/geometric/PGpoint.java
@@ -0,0 +1,167 @@
+package postgresql.geometric;
+
+import java.awt.Point;
+import java.io.*;
+import java.sql.*;
+
+import postgresql.util.*;
+
+/**
+ * This implements a version of java.awt.Point, except it uses double
+ * to represent the coordinates.
+ *
+ * <p>It maps to the point datatype in postgresql.
+ */
+public class PGpoint extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * The X coordinate of the point
+ */
+ public double x;
+
+ /**
+ * The Y coordinate of the point
+ */
+ public double y;
+
+ /**
+ * @param x coordinate
+ * @param y coordinate
+ */
+ public PGpoint(double x,double y)
+ {
+ this();
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * This is called mainly from the other geometric types, when a
+ * point is imbeded within their definition.
+ *
+ * @param value Definition of this point in PostgreSQL's syntax
+ */
+ public PGpoint(String value) throws SQLException
+ {
+ this();
+ setValue(value);
+ }
+
+ /**
+ * Required by the driver
+ */
+ public PGpoint()
+ {
+ setType("point");
+ }
+
+ /**
+ * @param s Definition of this point in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),',');
+ try {
+ x = Double.valueOf(t.getToken(0)).doubleValue();
+ y = Double.valueOf(t.getToken(1)).doubleValue();
+ } catch(NumberFormatException e) {
+ throw new SQLException("conversion of point failed - "+e.toString());
+ }
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGpoint) {
+ PGpoint p = (PGpoint)obj;
+ return x == p.x && y == p.y;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ return new PGpoint(x,y);
+ }
+
+ /**
+ * @return the PGpoint in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ return "("+x+","+y+")";
+ }
+
+ /**
+ * Translate the point with the supplied amount.
+ * @param x integer amount to add on the x axis
+ * @param y integer amount to add on the y axis
+ */
+ public void translate(int x,int y)
+ {
+ translate((double)x,(double)y);
+ }
+
+ /**
+ * Translate the point with the supplied amount.
+ * @param x double amount to add on the x axis
+ * @param y double amount to add on the y axis
+ */
+ public void translate(double x,double y)
+ {
+ this.x += x;
+ this.y += y;
+ }
+
+ /**
+ * Moves the point to the supplied coordinates.
+ * @param x integer coordinate
+ * @param y integer coordinate
+ */
+ public void move(int x,int y)
+ {
+ setLocation(x,y);
+ }
+
+ /**
+ * Moves the point to the supplied coordinates.
+ * @param x double coordinate
+ * @param y double coordinate
+ */
+ public void move(double x,double y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Moves the point to the supplied coordinates.
+ * refer to java.awt.Point for description of this
+ * @param x integer coordinate
+ * @param y integer coordinate
+ * @see java.awt.Point
+ */
+ public void setLocation(int x,int y)
+ {
+ move((double)x,(double)y);
+ }
+
+ /**
+ * Moves the point to the supplied java.awt.Point
+ * refer to java.awt.Point for description of this
+ * @param p Point to move to
+ * @see java.awt.Point
+ */
+ public void setLocation(Point p)
+ {
+ setLocation(p.x,p.y);
+ }
+
+}
diff --git a/src/interfaces/jdbc/postgresql/geometric/PGpolygon.java b/src/interfaces/jdbc/postgresql/geometric/PGpolygon.java
new file mode 100644
index 0000000000..b60c16e8ce
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/geometric/PGpolygon.java
@@ -0,0 +1,105 @@
+package postgresql.geometric;
+
+import java.io.*;
+import java.sql.*;
+import postgresql.util.*;
+
+/**
+ * This implements the polygon datatype within PostgreSQL.
+ */
+public class PGpolygon extends PGobject implements Serializable,Cloneable
+{
+ /**
+ * The points defining the polygon
+ */
+ public PGpoint points[];
+
+ /**
+ * Creates a polygon using an array of PGpoints
+ *
+ * @param points the points defining the polygon
+ */
+ public PGpolygon(PGpoint[] points)
+ {
+ this();
+ this.points = points;
+ }
+
+ /**
+ * @param s definition of the circle in PostgreSQL's syntax.
+ * @exception SQLException on conversion failure
+ */
+ public PGpolygon(String s) throws SQLException
+ {
+ this();
+ setValue(s);
+ }
+
+ /**
+ * Required by the driver
+ */
+ public PGpolygon()
+ {
+ setType("polygon");
+ }
+
+ /**
+ * @param s Definition of the polygon in PostgreSQL's syntax
+ * @exception SQLException on conversion failure
+ */
+ public void setValue(String s) throws SQLException
+ {
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s),',');
+ int npoints = t.getSize();
+ points = new PGpoint[npoints];
+ for(int p=0;p<npoints;p++)
+ points[p] = new PGpoint(t.getToken(p));
+ }
+
+ /**
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGpolygon) {
+ PGpolygon p = (PGpolygon)obj;
+
+ if(p.points.length != points.length)
+ return false;
+
+ for(int i=0;i<points.length;i++)
+ if(!points[i].equals(p.points[i]))
+ return false;
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ PGpoint ary[] = new PGpoint[points.length];
+ for(int i=0;i<points.length;i++)
+ ary[i] = (PGpoint)points[i].clone();
+ return new PGpolygon(ary);
+ }
+
+ /**
+ * @return the PGpolygon in the syntax expected by postgresql
+ */
+ public String getValue()
+ {
+ StringBuffer b = new StringBuffer();
+ b.append("(");
+ for(int p=0;p<points.length;p++) {
+ if(p>0) b.append(",");
+ b.append(points[p].toString());
+ }
+ b.append(")");
+ return b.toString();
+ }
+}
diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java
new file mode 100644
index 0000000000..0cab20f8e3
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObject.java
@@ -0,0 +1,253 @@
+package postgresql.largeobject;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+
+import postgresql.fastpath.*;
+
+/**
+ * This class implements the large object interface to postgresql.
+ *
+ * <p>It provides the basic methods required to run the interface, plus
+ * a pair of methods that provide InputStream and OutputStream classes
+ * for this object.
+ *
+ * <p>Normally, client code would use the getAsciiStream, getBinaryStream,
+ * or getUnicodeStream methods in ResultSet, or setAsciiStream,
+ * setBinaryStream, or setUnicodeStream methods in PreparedStatement to
+ * access Large Objects.
+ *
+ * <p>However, sometimes lower level access to Large Objects are required,
+ * that are not supported by the JDBC specification.
+ *
+ * <p>Refer to postgresql.largeobject.LargeObjectManager on how to gain access
+ * to a Large Object, or how to create one.
+ *
+ * @see postgresql.largeobject.LargeObjectManager
+ * @see postgresql.ResultSet#getAsciiStream
+ * @see postgresql.ResultSet#getBinaryStream
+ * @see postgresql.ResultSet#getUnicodeStream
+ * @see postgresql.PreparedStatement#setAsciiStream
+ * @see postgresql.PreparedStatement#setBinaryStream
+ * @see postgresql.PreparedStatement#setUnicodeStream
+ * @see java.sql.ResultSet#getAsciiStream
+ * @see java.sql.ResultSet#getBinaryStream
+ * @see java.sql.ResultSet#getUnicodeStream
+ * @see java.sql.PreparedStatement#setAsciiStream
+ * @see java.sql.PreparedStatement#setBinaryStream
+ * @see java.sql.PreparedStatement#setUnicodeStream
+ *
+ */
+public class LargeObject
+{
+ /**
+ * Indicates a seek from the begining of a file
+ */
+ public static final int SEEK_SET = 0;
+
+ /**
+ * Indicates a seek from the current position
+ */
+ public static final int SEEK_CUR = 1;
+
+ /**
+ * Indicates a seek from the end of a file
+ */
+ public static final int SEEK_END = 2;
+
+ private Fastpath fp; // Fastpath API to use
+ private int oid; // OID of this object
+ private int fd; // the descriptor of the open large object
+
+ /**
+ * This opens a large object.
+ *
+ * <p>If the object does not exist, then an SQLException is thrown.
+ *
+ * @param fp FastPath API for the connection to use
+ * @param oid of the Large Object to open
+ * @param mode Mode of opening the large object
+ * (defined in LargeObjectManager)
+ * @exception SQLException if a database-access error occurs.
+ * @see postgresql.largeobject.LargeObjectManager
+ */
+ protected LargeObject(Fastpath fp,int oid,int mode) throws SQLException
+ {
+ this.fp = fp;
+ this.oid = oid;
+
+ FastpathArg args[] = new FastpathArg[2];
+ args[0] = new FastpathArg(oid);
+ args[1] = new FastpathArg(mode);
+ this.fd = fp.getInteger("lo_open",args);
+ }
+
+ /**
+ * @return the OID of this LargeObject
+ */
+ public int getOID()
+ {
+ return oid;
+ }
+
+ /**
+ * This method closes the object. You must not call methods in this
+ * object after this is called.
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void close() throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(fd);
+ fp.fastpath("lo_close",false,args); // true here as we dont care!!
+ }
+
+ /**
+ * Reads some data from the object, and return as a byte[] array
+ *
+ * @param len number of bytes to read
+ * @return byte[] array containing data read
+ * @exception SQLException if a database-access error occurs.
+ */
+ public byte[] read(int len) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[2];
+ args[0] = new FastpathArg(fd);
+ args[1] = new FastpathArg(len);
+ return fp.getData("loread",args);
+ }
+
+ /**
+ * Reads some data from the object into an existing array
+ *
+ * @param buf destination array
+ * @param off offset within array
+ * @param len number of bytes to read
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void read(byte buf[],int off,int len) throws SQLException
+ {
+ System.arraycopy(read(len),0,buf,off,len);
+ }
+
+ /**
+ * Writes an array to the object
+ *
+ * @param buf array to write
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void write(byte buf[]) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[2];
+ args[0] = new FastpathArg(fd);
+ args[1] = new FastpathArg(buf);
+ fp.fastpath("lowrite",false,args);
+ }
+
+ /**
+ * Writes some data from an array to the object
+ *
+ * @param buf destination array
+ * @param off offset within array
+ * @param len number of bytes to write
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void write(byte buf[],int off,int len) throws SQLException
+ {
+ byte data[] = new byte[len];
+ System.arraycopy(buf,off,data,0,len);
+ write(data);
+ }
+
+ /**
+ * Sets the current position within the object.
+ *
+ * <p>This is similar to the fseek() call in the standard C library. It
+ * allows you to have random access to the large object.
+ *
+ * @param pos position within object
+ * @param ref Either SEEK_SET, SEEK_CUR or SEEK_END
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void seek(int pos,int ref) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[3];
+ args[0] = new FastpathArg(fd);
+ args[1] = new FastpathArg(pos);
+ args[2] = new FastpathArg(ref);
+ fp.fastpath("lo_lseek",false,args);
+ }
+
+ /**
+ * Sets the current position within the object.
+ *
+ * <p>This is similar to the fseek() call in the standard C library. It
+ * allows you to have random access to the large object.
+ *
+ * @param pos position within object from begining
+ * @exception SQLException if a database-access error occurs.
+ */
+ public void seek(int pos) throws SQLException
+ {
+ seek(pos,SEEK_SET);
+ }
+
+ /**
+ * @return the current position within the object
+ * @exception SQLException if a database-access error occurs.
+ */
+ public int tell() throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(fd);
+ return fp.getInteger("lo_tell",args);
+ }
+
+ /**
+ * This method is inefficient, as the only way to find out the size of
+ * the object is to seek to the end, record the current position, then
+ * return to the original position.
+ *
+ * <p>A better method will be found in the future.
+ *
+ * @return the size of the large object
+ * @exception SQLException if a database-access error occurs.
+ */
+ public int size() throws SQLException
+ {
+ int cp = tell();
+ seek(0,SEEK_END);
+ int sz = tell();
+ seek(cp,SEEK_SET);
+ return sz;
+ }
+
+ /**
+ * Returns an InputStream from this object.
+ *
+ * <p>This InputStream can then be used in any method that requires an
+ * InputStream.
+ *
+ * @exception SQLException if a database-access error occurs.
+ */
+ public InputStream getInputStream() throws SQLException
+ {
+ throw new SQLException("LargeObject:getInputStream not implemented");
+ }
+
+ /**
+ * Returns an OutputStream to this object
+ *
+ * <p>This OutputStream can then be used in any method that requires an
+ * OutputStream.
+ *
+ * @exception SQLException if a database-access error occurs.
+ */
+ public OutputStream getOutputStream() throws SQLException
+ {
+ throw new SQLException("LargeObject:getOutputStream not implemented");
+ }
+}
diff --git a/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java
new file mode 100644
index 0000000000..c7798d15a1
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/largeobject/LargeObjectManager.java
@@ -0,0 +1,205 @@
+package postgresql.largeobject;
+
+import java.io.*;
+import java.lang.*;
+import java.net.*;
+import java.util.*;
+import java.sql.*;
+
+import postgresql.fastpath.*;
+
+/**
+ * This class implements the large object interface to postgresql.
+ *
+ * <p>It provides methods that allow client code to create, open and delete
+ * large objects from the database. When opening an object, an instance of
+ * postgresql.largeobject.LargeObject is returned, and its methods then allow
+ * access to the object.
+ *
+ * <p>This class can only be created by postgresql.Connection
+ *
+ * <p>To get access to this class, use the following segment of code:
+ * <br><pre>
+ * import postgresql.largeobject.*;
+ *
+ * Connection conn;
+ * LargeObjectManager lobj;
+ *
+ * ... code that opens a connection ...
+ *
+ * lobj = ((postgresql.Connection)myconn).getLargeObjectAPI();
+ * </pre>
+ *
+ * <p>Normally, client code would use the getAsciiStream, getBinaryStream,
+ * or getUnicodeStream methods in ResultSet, or setAsciiStream,
+ * setBinaryStream, or setUnicodeStream methods in PreparedStatement to
+ * access Large Objects.
+ *
+ * <p>However, sometimes lower level access to Large Objects are required,
+ * that are not supported by the JDBC specification.
+ *
+ * <p>Refer to postgresql.largeobject.LargeObject on how to manipulate the
+ * contents of a Large Object.
+ *
+ * @see postgresql.largeobject.LargeObject
+ * @see postgresql.ResultSet#getAsciiStream
+ * @see postgresql.ResultSet#getBinaryStream
+ * @see postgresql.ResultSet#getUnicodeStream
+ * @see postgresql.PreparedStatement#setAsciiStream
+ * @see postgresql.PreparedStatement#setBinaryStream
+ * @see postgresql.PreparedStatement#setUnicodeStream
+ * @see java.sql.ResultSet#getAsciiStream
+ * @see java.sql.ResultSet#getBinaryStream
+ * @see java.sql.ResultSet#getUnicodeStream
+ * @see java.sql.PreparedStatement#setAsciiStream
+ * @see java.sql.PreparedStatement#setBinaryStream
+ * @see java.sql.PreparedStatement#setUnicodeStream
+ */
+public class LargeObjectManager
+{
+ // the fastpath api for this connection
+ private Fastpath fp;
+
+ /**
+ * This mode indicates we want to write to an object
+ */
+ public static final int WRITE = 0x00020000;
+
+ /**
+ * This mode indicates we want to read an object
+ */
+ public static final int READ = 0x00040000;
+
+ /**
+ * This mode is the default. It indicates we want read and write access to
+ * a large object
+ */
+ public static final int READWRITE = READ | WRITE;
+
+ /**
+ * This prevents us being created by mere mortals
+ */
+ private LargeObjectManager()
+ {
+ }
+
+ /**
+ * Constructs the LargeObject API.
+ *
+ * <p><b>Important Notice</b>
+ * <br>This method should only be called by postgresql.Connection
+ *
+ * <p>There should only be one LargeObjectManager per Connection. The
+ * postgresql.Connection class keeps track of the various extension API's
+ * and it's advised you use those to gain access, and not going direct.
+ */
+ public LargeObjectManager(postgresql.Connection conn) throws SQLException
+ {
+ // We need Fastpath to do anything
+ this.fp = conn.getFastpathAPI();
+
+ // Now get the function oid's for the api
+ //
+ // This is an example of Fastpath.addFunctions();
+ //
+ ResultSet res = (postgresql.ResultSet)conn.createStatement().executeQuery("select proname, oid from pg_proc" +
+ " where proname = 'lo_open'" +
+ " or proname = 'lo_close'" +
+ " or proname = 'lo_creat'" +
+ " or proname = 'lo_unlink'" +
+ " or proname = 'lo_lseek'" +
+ " or proname = 'lo_tell'" +
+ " or proname = 'loread'" +
+ " or proname = 'lowrite'");
+
+ if(res==null)
+ throw new SQLException("failed to initialise LargeObject API");
+
+ fp.addFunctions(res);
+ res.close();
+ DriverManager.println("Large Object initialised");
+ }
+
+ /**
+ * This opens an existing large object, based on its OID. This method
+ * assumes that READ and WRITE access is required (the default).
+ *
+ * @param oid of large object
+ * @return LargeObject instance providing access to the object
+ * @exception SQLException on error
+ */
+ public LargeObject open(int oid) throws SQLException
+ {
+ return new LargeObject(fp,oid,READWRITE);
+ }
+
+ /**
+ * This opens an existing large object, based on its OID
+ *
+ * @param oid of large object
+ * @param mode mode of open
+ * @return LargeObject instance providing access to the object
+ * @exception SQLException on error
+ */
+ public LargeObject open(int oid,int mode) throws SQLException
+ {
+ return new LargeObject(fp,oid,mode);
+ }
+
+ /**
+ * This creates a large object, returning its OID.
+ *
+ * <p>It defaults to READWRITE for the new object's attributes.
+ *
+ * @return oid of new object
+ * @exception SQLException on error
+ */
+ public int create() throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(READWRITE);
+ return fp.getInteger("lo_creat",args);
+ }
+
+ /**
+ * This creates a large object, returning its OID
+ *
+ * @param mode a bitmask describing different attributes of the new object
+ * @return oid of new object
+ * @exception SQLException on error
+ */
+ public int create(int mode) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(mode);
+ return fp.getInteger("lo_creat",args);
+ }
+
+ /**
+ * This deletes a large object.
+ *
+ * @param oid describing object to delete
+ * @exception SQLException on error
+ */
+ public void delete(int oid) throws SQLException
+ {
+ FastpathArg args[] = new FastpathArg[1];
+ args[0] = new FastpathArg(oid);
+ fp.fastpath("lo_unlink",false,args);
+ }
+
+ /**
+ * This deletes a large object.
+ *
+ * <p>It is identical to the delete method, and is supplied as the C API uses
+ * unlink.
+ *
+ * @param oid describing object to delete
+ * @exception SQLException on error
+ */
+ public void unlink(int oid) throws SQLException
+ {
+ delete(oid);
+ }
+
+}
diff --git a/src/interfaces/jdbc/postgresql/util/PGobject.java b/src/interfaces/jdbc/postgresql/util/PGobject.java
new file mode 100644
index 0000000000..62b3d55f5e
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/util/PGobject.java
@@ -0,0 +1,102 @@
+package postgresql.util;
+
+import java.io.*;
+import java.lang.*;
+import java.sql.*;
+import java.util.*;
+
+/**
+ * postgresql.PG_Object is a class used to describe unknown types
+ * An unknown type is any type that is unknown by JDBC Standards
+ *
+ * <p>As of PostgreSQL 6.3, this allows user code to add their own
+ * handlers via a call to postgresql.Connection. These handlers
+ * must extend this class.
+ */
+public class PGobject implements Serializable,Cloneable
+{
+ protected String type;
+ protected String value;
+
+ /**
+ * This is called by postgresql.Connection.getObject() to create the
+ * object.
+ */
+ public PGobject()
+ {
+ }
+
+ /**
+ * This method sets the type of this object.
+ *
+ * <p>It should not be extended by subclasses, hence its final
+ *
+ * @param type a string describing the type of the object
+ */
+ public final void setType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * This method sets the value of this object. It must be overidden.
+ *
+ * @param value a string representation of the value of the object
+ * @exception SQLException thrown if value is invalid for this type
+ */
+ public void setValue(String value) throws SQLException
+ {
+ this.value = value;
+ }
+
+ /**
+ * As this cannot change during the life of the object, it's final.
+ * @return the type name of this object
+ */
+ public final String getType()
+ {
+ return type;
+ }
+
+ /**
+ * This must be overidden, to return the value of the object, in the
+ * form required by postgresql.
+ * @return the value of this object
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This must be overidden to allow comparisons of objects
+ * @param obj Object to compare with
+ * @return true if the two boxes are identical
+ */
+ public boolean equals(Object obj)
+ {
+ if(obj instanceof PGobject)
+ return ((PGobject)obj).getValue().equals(getValue());
+ return false;
+ }
+
+ /**
+ * This must be overidden to allow the object to be cloned
+ */
+ public Object clone()
+ {
+ PGobject obj = new PGobject();
+ obj.type=type;
+ obj.value=value;
+ return obj;
+ }
+
+ /**
+ * This is defined here, so user code need not overide it.
+ * @return the value of this object, in the syntax expected by postgresql
+ */
+ public String toString()
+ {
+ return value;
+ }
+}
diff --git a/src/interfaces/jdbc/postgresql/util/PGtokenizer.java b/src/interfaces/jdbc/postgresql/util/PGtokenizer.java
new file mode 100644
index 0000000000..0d07596891
--- /dev/null
+++ b/src/interfaces/jdbc/postgresql/util/PGtokenizer.java
@@ -0,0 +1,197 @@
+package postgresql.util;
+
+import java.sql.*;
+import java.util.*;
+
+/**
+ * This class is used to tokenize the text output of postgres.
+ *
+ * <p>It's mainly used by the geometric classes, but is useful in parsing any
+ * output from custom data types output from postgresql.
+ *
+ * @see postgresql.geometric.PGbox
+ * @see postgresql.geometric.PGcircle
+ * @see postgresql.geometric.PGlseg
+ * @see postgresql.geometric.PGpath
+ * @see postgresql.geometric.PGpoint
+ * @see postgresql.geometric.PGpolygon
+ */
+public class PGtokenizer
+{
+ // Our tokens
+ protected Vector tokens;
+
+ /**
+ * Create a tokeniser.
+ *
+ * <p>We could have used StringTokenizer to do this, however, we needed to
+ * handle nesting of '(' ')' '[' ']' '&lt;' and '&gt;' as these are used
+ * by the geometric data types.
+ *
+ * @param string containing tokens
+ * @param delim single character to split the tokens
+ */
+ public PGtokenizer(String string,char delim)
+ {
+ tokenize(string,delim);
+ }
+
+ /**
+ * This resets this tokenizer with a new string and/or delimiter.
+ *
+ * @param string containing tokens
+ * @param delim single character to split the tokens
+ */
+ public int tokenize(String string,char delim)
+ {
+ tokens = new Vector();
+
+ // nest holds how many levels we are in the current token.
+ // if this is > 0 then we don't split a token when delim is matched.
+ //
+ // The Geometric datatypes use this, because often a type may have others
+ // (usualls PGpoint) imbedded within a token.
+ //
+ // Peter 1998 Jan 6 - Added < and > to the nesting rules
+ int nest=0,p,s;
+
+ for(p=0,s=0;p<string.length();p++) {
+ char c = string.charAt(p);
+
+ // increase nesting if an open character is found
+ if(c == '(' || c == '[' || c == '<')
+ nest++;
+
+ // decrease nesting if a close character is found
+ if(c == ')' || c == ']' || c == '>')
+ nest--;
+
+ if(nest==0 && c==delim) {
+ tokens.addElement(string.substring(s,p));
+ s=p+1; // +1 to skip the delimiter
+ }
+
+ }
+
+ // Don't forget the last token ;-)
+ if(s<string.length())
+ tokens.addElement(string.substring(s));
+
+ return tokens.size();
+ }
+
+ /**
+ * @return the number of tokens available
+ */
+ public int getSize()
+ {
+ return tokens.size();
+ }
+
+ /**
+ * @param n Token number ( 0 ... getSize()-1 )
+ * @return The token value
+ */
+ public String getToken(int n)
+ {
+ return (String)tokens.elementAt(n);
+ }
+
+ /**
+ * This returns a new tokenizer based on one of our tokens.
+ *
+ * The geometric datatypes use this to process nested tokens (usually
+ * PGpoint).
+ *
+ * @param n Token number ( 0 ... getSize()-1 )
+ * @param delim The delimiter to use
+ * @return A new instance of PGtokenizer based on the token
+ */
+ public PGtokenizer tokenizeToken(int n,char delim)
+ {
+ return new PGtokenizer(getToken(n),delim);
+ }
+
+ /**
+ * This removes the lead/trailing strings from a string
+ * @param s Source string
+ * @param l Leading string to remove
+ * @param t Trailing string to remove
+ * @return String without the lead/trailing strings
+ */
+ public static String remove(String s,String l,String t)
+ {
+ if(s.startsWith(l)) s = s.substring(l.length());
+ if(s.endsWith(t)) s = s.substring(0,s.length()-t.length());
+ return s;
+ }
+
+ /**
+ * This removes the lead/trailing strings from all tokens
+ * @param l Leading string to remove
+ * @param t Trailing string to remove
+ */
+ public void remove(String l,String t)
+ {
+ for(int i=0;i<tokens.size();i++) {
+ tokens.setElementAt(remove((String)tokens.elementAt(i),l,t),i);
+ }
+ }
+
+ /**
+ * Removes ( and ) from the beginning and end of a string
+ * @param s String to remove from
+ * @return String without the ( or )
+ */
+ public static String removePara(String s)
+ {
+ return remove(s,"(",")");
+ }
+
+ /**
+ * Removes ( and ) from the beginning and end of all tokens
+ * @return String without the ( or )
+ */
+ public void removePara()
+ {
+ remove("(",")");
+ }
+
+ /**
+ * Removes [ and ] from the beginning and end of a string
+ * @param s String to remove from
+ * @return String without the [ or ]
+ */
+ public static String removeBox(String s)
+ {
+ return remove(s,"[","]");
+ }
+
+ /**
+ * Removes [ and ] from the beginning and end of all tokens
+ * @return String without the [ or ]
+ */
+ public void removeBox()
+ {
+ remove("[","]");
+ }
+
+ /**
+ * Removes &lt; and &gt; from the beginning and end of a string
+ * @param s String to remove from
+ * @return String without the &lt; or &gt;
+ */
+ public static String removeAngle(String s)
+ {
+ return remove(s,"<",">");
+ }
+
+ /**
+ * Removes &lt; and &gt; from the beginning and end of all tokens
+ * @return String without the &lt; or &gt;
+ */
+ public void removeAngle()
+ {
+ remove("<",">");
+ }
+}