mirror of https://github.com/postgres/postgres
parent
d09fc12044
commit
11cb9acb68
@ -0,0 +1,4 @@ |
||||
Tue Jan 23 10:19:00 GMT 2001 peter@retep.org.uk |
||||
- Finished the XML Export classes |
||||
- First of the test data suite now in CVS. |
||||
|
@ -0,0 +1,16 @@ |
||||
<!ELEMENT album (track*)+> |
||||
<!ATTLIST album |
||||
title CDATA #IMPLIED |
||||
aid CDATA #IMPLIED |
||||
> |
||||
<!ELEMENT catalogue (group)> |
||||
<!ELEMENT group (album*)> |
||||
<!ATTLIST group |
||||
name CDATA #IMPLIED |
||||
> |
||||
<!ELEMENT track (#PCDATA)> |
||||
<!ATTLIST track |
||||
tid CDATA #IMPLIED |
||||
id CDATA #IMPLIED |
||||
> |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,334 @@ |
||||
package uk.org.retep.xml.core; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.Writer; |
||||
|
||||
/** |
||||
* An XMLFactory is used to render XML Tags, accounting for nesting etc |
||||
*/ |
||||
public class XMLFactory |
||||
{ |
||||
/** |
||||
* The lest level (ie, how many tags down the tree we are) |
||||
*/ |
||||
protected int level; |
||||
|
||||
/** |
||||
* The size of our tag name cache |
||||
*/ |
||||
protected int maxlevel; |
||||
|
||||
/** |
||||
* Our tag name cache |
||||
*/ |
||||
protected String[] names; |
||||
|
||||
/** |
||||
* Used to keep track of how formatting is done |
||||
*/ |
||||
protected boolean hascontent; |
||||
protected boolean[] contbuf; |
||||
|
||||
/** |
||||
* Scratch used by nest() |
||||
*/ |
||||
private char[] nestbuf; |
||||
|
||||
/** |
||||
* The destination Writer |
||||
*/ |
||||
protected Writer out; |
||||
|
||||
/** |
||||
* True if we are still within a tag |
||||
*/ |
||||
protected boolean inTag; |
||||
|
||||
/** |
||||
* True if we have just created a tag so parameters are valid |
||||
*/ |
||||
protected boolean inArg; |
||||
|
||||
/** |
||||
* Constructs an XMLFactory with no output Writer |
||||
*/ |
||||
public XMLFactory() |
||||
{ |
||||
this(10); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an XMLFactory with no output Writer |
||||
* @param m Expected number of leaves in the XML Tree |
||||
*/ |
||||
public XMLFactory(int m) |
||||
{ |
||||
// Initialise the names cache
|
||||
level=0; |
||||
maxlevel=m; |
||||
names=new String[maxlevel]; |
||||
contbuf=new boolean[maxlevel]; |
||||
|
||||
// This is used by nest()
|
||||
nestbuf=new char[maxlevel]; |
||||
for(int i=0;i<maxlevel;i++) |
||||
nestbuf[i]=' '; |
||||
} |
||||
|
||||
/** |
||||
* Constructs an XMLFactory |
||||
* @param out Writer to send the output to |
||||
*/ |
||||
public XMLFactory(Writer out) |
||||
throws IOException |
||||
{ |
||||
this(); |
||||
setWriter(out); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an XMLFactory |
||||
* @param out Writer to send the output to |
||||
* @param encoding The XML encoding |
||||
*/ |
||||
public XMLFactory(Writer out,String encoding) |
||||
throws IOException |
||||
{ |
||||
this(); |
||||
setWriter(out,encoding); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an XMLFactory |
||||
* @param out Writer to send the output to |
||||
* @param m Expected number of leaves in the XML Tree |
||||
*/ |
||||
public XMLFactory(int m,Writer out) |
||||
throws IOException |
||||
{ |
||||
this(m); |
||||
setWriter(out); |
||||
} |
||||
|
||||
/** |
||||
* Constructs an XMLFactory |
||||
* @param out Writer to send the output to |
||||
* @param encoding The XML encoding |
||||
* @param m Expected number of leaves in the XML Tree |
||||
*/ |
||||
public XMLFactory(int m,Writer out,String encoding) |
||||
throws IOException |
||||
{ |
||||
this(m); |
||||
setWriter(out,encoding); |
||||
} |
||||
|
||||
/** |
||||
* Sets the Writer to send the output to. This call will also send the |
||||
* XML header. |
||||
* |
||||
* @param out Writer to send output to |
||||
*/ |
||||
public void setWriter(Writer out) |
||||
throws IOException |
||||
{ |
||||
setWriter(out,"ISO-8859-1"); |
||||
} |
||||
|
||||
/** |
||||
* Sets the Writer to send the output to. This call will also send the |
||||
* XML header using the supplied encoding. It is up to the user code to |
||||
* implement this encoding. |
||||
* |
||||
* @param out Writer to send output to |
||||
* @param encoding Encoding of the XML Output |
||||
*/ |
||||
public void setWriter(Writer out,String encoding) |
||||
throws IOException |
||||
{ |
||||
this.out=out; |
||||
out.write("<?xml version=\"1.0\" encoding=\""); |
||||
out.write(encoding); |
||||
out.write("\" ?>\n"); |
||||
} |
||||
|
||||
/** |
||||
* @return Writer the XML is being sent out on. |
||||
*/ |
||||
public Writer getWriter() { |
||||
return out; |
||||
} |
||||
|
||||
/** |
||||
* This starts a tag |
||||
* @param name The tag name |
||||
*/ |
||||
public void startTag(String name) |
||||
throws IOException |
||||
{ |
||||
if(inTag && inArg) { |
||||
// Handles two startTag() calls in succession.
|
||||
out.write(">"); |
||||
} |
||||
|
||||
nest(level); |
||||
out.write('<'); |
||||
out.write(name); |
||||
inTag=true; |
||||
inArg=true; |
||||
|
||||
// cache the current tag name
|
||||
names[level]=name; |
||||
|
||||
// cache the current hascontent value & reset
|
||||
contbuf[level]=hascontent; |
||||
hascontent=false; |
||||
|
||||
// increase the level and the cache's as necessary
|
||||
level++; |
||||
if(level>maxlevel) { |
||||
maxlevel=maxlevel+10; |
||||
|
||||
String n[]=new String[maxlevel]; |
||||
System.arraycopy(names,0,n,0,level); |
||||
names=n; |
||||
|
||||
boolean b[] = new boolean[maxlevel]; |
||||
System.arraycopy(contbuf,0,b,0,level); |
||||
contbuf=b; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This ends a tag |
||||
*/ |
||||
public void endTag() |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
if(level<1) |
||||
throw new XMLFactoryException("endTag called above root node"); |
||||
|
||||
level--; |
||||
|
||||
if(inArg) { |
||||
// We are still within the opening tag
|
||||
out.write(" />"); |
||||
} else { |
||||
// We must have written some content or child tags
|
||||
|
||||
// hascontent is true if addContent() was called. If it was never called
|
||||
// to get here some child tags must have been written, so we call nest()
|
||||
// so that the close tag is on it's own line, and everything looks neat
|
||||
// and tidy.
|
||||
if(!hascontent) |
||||
nest(level); |
||||
|
||||
out.write("</"); |
||||
out.write(names[level]); |
||||
out.write('>'); |
||||
} |
||||
|
||||
inArg=false; // The parent tag must be told it now has content
|
||||
inTag= level>0; // Are we still in a tag?
|
||||
hascontent=contbuf[level]; // retrieve this level's hascontent value
|
||||
} |
||||
|
||||
/** |
||||
* This completes the document releasing any open resources. |
||||
*/ |
||||
public void close() |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
while(level>0) |
||||
endTag(); |
||||
out.write('\n'); |
||||
out.flush(); |
||||
} |
||||
|
||||
/** |
||||
* This writes an attribute to the current tag. If the value is null, then no action is taken. |
||||
* @param name Name of the parameter |
||||
* @param value Value of the parameter |
||||
* @throw XMLFactoryException if out of context |
||||
*/ |
||||
public void addAttribute(String name,Object value) |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
if(value==null) |
||||
return; |
||||
|
||||
if(inArg) { |
||||
out.write(' '); |
||||
out.write(name); |
||||
out.write("=\""); |
||||
out.write(encode(value.toString())); |
||||
out.write("\""); |
||||
} else |
||||
throw new XMLFactoryException("Cannot add attribute outside of a tag"); |
||||
} |
||||
|
||||
/** |
||||
* This writes some content to the current tag. Once this has been called, |
||||
* you cannot add any more attributes to the current tag. Note, if c is null, |
||||
* no action is taken. |
||||
* @param c content to add. |
||||
*/ |
||||
public void addContent(Object c) |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
if(c==null) |
||||
return; |
||||
|
||||
if(inTag) { |
||||
if(inArg) { |
||||
// close the open tag
|
||||
out.write('>'); |
||||
inArg=false; |
||||
} |
||||
out.write(c.toString()); |
||||
|
||||
// This is used by endTag()
|
||||
hascontent=true; |
||||
} else |
||||
throw new XMLFactoryException("Cannot add content outside of a tag"); |
||||
} |
||||
|
||||
/** |
||||
* This adds a comment to the XML file. This is normally used at the start of |
||||
* any XML output. |
||||
* @parm c Comment to include |
||||
*/ |
||||
public void addComment(Object c) |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
if(inTag) |
||||
throw new XMLFactoryException("Cannot add comments within a tag"); |
||||
|
||||
out.write("\n<!-- "); |
||||
out.write(c.toString()); |
||||
out.write(" -->"); |
||||
} |
||||
|
||||
/** |
||||
* Indents the output according to the level |
||||
* @param level The indent level to generate |
||||
*/ |
||||
protected void nest(int level) |
||||
throws IOException |
||||
{ |
||||
out.write('\n'); |
||||
while(level>nestbuf.length) { |
||||
out.write(nestbuf,0,nestbuf.length); |
||||
level-=nestbuf.length; |
||||
} |
||||
out.write(nestbuf,0,level); |
||||
} |
||||
|
||||
/** |
||||
* Encodes the string so that any XML tag chars are translated |
||||
*/ |
||||
protected String encode(String s) { |
||||
return s; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,19 @@ |
||||
package uk.org.retep.xml.core; |
||||
|
||||
/** |
||||
* Title: |
||||
* Description: |
||||
* Copyright: Copyright (c) 2001 |
||||
* Company: |
||||
* @author |
||||
* @version 1.0 |
||||
*/ |
||||
|
||||
public class XMLFactoryException extends Exception |
||||
{ |
||||
|
||||
public XMLFactoryException(String s) |
||||
{ |
||||
super(s); |
||||
} |
||||
} |
@ -0,0 +1,237 @@ |
||||
package uk.org.retep.xml.jdbc; |
||||
|
||||
import java.io.IOException; |
||||
import java.sql.Connection; |
||||
import java.sql.DatabaseMetaData; |
||||
import java.sql.ResultSet; |
||||
import java.sql.SQLException; |
||||
import uk.org.retep.xml.core.XMLFactory; |
||||
import uk.org.retep.xml.core.XMLFactoryException; |
||||
|
||||
public class XMLDatabase |
||||
{ |
||||
/** |
||||
* The XMLFactory being used by this instance |
||||
*/ |
||||
protected XMLFactory factory; |
||||
|
||||
/** |
||||
* Constructor. setXMLFactory() must be called if this method is used. |
||||
*/ |
||||
public XMLDatabase() |
||||
{ |
||||
} |
||||
|
||||
/** |
||||
* Constructor |
||||
* @param fac XMLFactory to use |
||||
*/ |
||||
public XMLDatabase(XMLFactory fac) |
||||
{ |
||||
this(); |
||||
setXMLFactory(fac); |
||||
} |
||||
|
||||
/** |
||||
* Sets the factory to use. |
||||
* @param factory XMLFactory to use |
||||
*/ |
||||
public void setXMLFactory(XMLFactory factory) |
||||
{ |
||||
this.factory=factory; |
||||
} |
||||
|
||||
/** |
||||
* @return the XMLFactory being used. |
||||
*/ |
||||
public XMLFactory getXMLFactory() |
||||
{ |
||||
return factory; |
||||
} |
||||
|
||||
/** |
||||
* Flushes all output to the Writer. |
||||
* @throw IOException from Writer |
||||
* @throw XMLFactoryException from XMLFactory |
||||
*/ |
||||
public void close() |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
factory.close(); |
||||
} |
||||
|
||||
/** |
||||
* writes the schema of a table. |
||||
* @param con Connection to database |
||||
* @param table Table name |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
*/ |
||||
public void writeTable(Connection con,String table) |
||||
throws IOException,SQLException,XMLFactoryException |
||||
{ |
||||
writeTable(con.getMetaData(),table); |
||||
} |
||||
|
||||
/** |
||||
* writes the schema of a table. |
||||
* @param db DatabaseMetaData for the database |
||||
* @param table Table name |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
*/ |
||||
public void writeTable(DatabaseMetaData db,String table) |
||||
throws IOException,SQLException,XMLFactoryException |
||||
{ |
||||
writeTable(db,null,null,table); |
||||
} |
||||
|
||||
/** |
||||
* writes the schema of a table. |
||||
* @param db DatabaseMetaData for the database |
||||
* @param table Table name |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
*/ |
||||
public void writeTable(DatabaseMetaData db,String cat,String schem,String table) |
||||
throws IOException,SQLException,XMLFactoryException |
||||
{ |
||||
ResultSet trs; |
||||
|
||||
factory.startTag("TABLE"); |
||||
factory.addAttribute("NAME",table); |
||||
// fetch the remarks for this table (if any)
|
||||
trs = db.getTables(null,null,table,null); |
||||
if(trs!=null) { |
||||
if(trs.next()) { |
||||
String rem = trs.getString(5); |
||||
if(rem!=null) |
||||
factory.addContent(rem); |
||||
} |
||||
trs.close(); |
||||
} |
||||
|
||||
trs = db.getColumns(null,null,table,"%"); |
||||
if(trs!=null) { |
||||
while(trs.next()) { |
||||
factory.startTag("COLUMN"); |
||||
factory.addAttribute("NAME",trs.getString(4)); |
||||
factory.addAttribute("TYPE",trs.getString(6)); |
||||
factory.addAttribute("COLUMN_SIZE",trs.getString(7)); |
||||
factory.addAttribute("DECIMAL_DIGITS",trs.getString(9)); |
||||
factory.addAttribute("NUM_PREC_RADIX",trs.getString(10)); |
||||
factory.addAttribute("NULLABLE",trs.getString(11)); |
||||
factory.addAttribute("COLUMN_DEF",trs.getString(13)); |
||||
factory.addAttribute("CHAR_OCTET_LENGTH",trs.getString(16)); |
||||
factory.addAttribute("ORDINAL_POSITION",trs.getString(17)); |
||||
factory.addAttribute("IS_NULLABLE",trs.getString(18)); |
||||
factory.addAttribute("TABLE_CAT",trs.getString(1)); |
||||
factory.addAttribute("TABLE_SCHEM",trs.getString(2)); |
||||
String rem = trs.getString(12); |
||||
if(rem!=null) |
||||
factory.addContent(rem); |
||||
factory.endTag(); |
||||
} |
||||
trs.close(); |
||||
} |
||||
|
||||
factory.endTag(); |
||||
} |
||||
|
||||
/** |
||||
* This generates the schema of an entire database. |
||||
* @param db Connection to database |
||||
* @param table Table pattern |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
* @see java.sql.DatabaseMetaData.getTables() |
||||
*/ |
||||
public void writeDatabase(Connection db,String table) |
||||
throws IOException, SQLException, XMLFactoryException |
||||
{ |
||||
writeDatabase(db.getMetaData(),null,null,table); |
||||
} |
||||
|
||||
/** |
||||
* This generates the schema of an entire database. |
||||
* @param db DatabaseMetaData of database |
||||
* @param table Table pattern |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
* @see java.sql.DatabaseMetaData.getTables() |
||||
*/ |
||||
public void writeDatabase(DatabaseMetaData db,String table) |
||||
throws IOException, SQLException, XMLFactoryException |
||||
{ |
||||
writeDatabase(db,null,null,table); |
||||
} |
||||
|
||||
/** |
||||
* This generates the schema of an entire database. |
||||
* @param db DatabaseMetaData of database |
||||
* @param cat Catalog (may be null) |
||||
* @param schem Schema (may be null) |
||||
* @param table Table pattern |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
* @see java.sql.DatabaseMetaData.getTables() |
||||
*/ |
||||
public void writeDatabase(Connection db) |
||||
throws IOException, SQLException, XMLFactoryException |
||||
{ |
||||
writeDatabase(db.getMetaData(),null,null,"%"); |
||||
} |
||||
|
||||
/** |
||||
* This generates the schema of an entire database. |
||||
* @param db DatabaseMetaData of database |
||||
* @param cat Catalog (may be null) |
||||
* @param schem Schema (may be null) |
||||
* @param table Table pattern |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
* @see java.sql.DatabaseMetaData.getTables() |
||||
*/ |
||||
public void writeDatabase(DatabaseMetaData db) |
||||
throws IOException, SQLException, XMLFactoryException |
||||
{ |
||||
writeDatabase(db,null,null,"%"); |
||||
} |
||||
|
||||
/** |
||||
* This generates the schema of an entire database. |
||||
* @param db DatabaseMetaData of database |
||||
* @param cat Catalog (may be null) |
||||
* @param schem Schema (may be null) |
||||
* @param table Table pattern |
||||
* @throw IOException from Writer |
||||
* @throw SQLException from JDBC |
||||
* @throw XMLFactoryException from XMLFactory |
||||
* @see java.sql.DatabaseMetaData.getTables() |
||||
*/ |
||||
public void writeDatabase(DatabaseMetaData db,String cat,String schem,String table) |
||||
throws IOException, SQLException, XMLFactoryException |
||||
{ |
||||
ResultSet rs = db.getTables(cat,schem,table,null); |
||||
if(rs!=null) { |
||||
factory.startTag("DATABASE"); |
||||
factory.addAttribute("PRODUCT",db.getDatabaseProductName()); |
||||
factory.addAttribute("VERSION",db.getDatabaseProductVersion()); |
||||
|
||||
while(rs.next()) { |
||||
writeTable(db,rs.getString(1),rs.getString(2),rs.getString(3)); |
||||
} |
||||
|
||||
factory.endTag(); |
||||
rs.close(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,505 @@ |
||||
package uk.org.retep.xml.jdbc; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.Writer; |
||||
import java.sql.ResultSet; |
||||
import java.sql.ResultSetMetaData; |
||||
import java.sql.SQLException; |
||||
import java.util.Properties; |
||||
import uk.org.retep.xml.core.XMLFactory; |
||||
import uk.org.retep.xml.core.XMLFactoryException; |
||||
|
||||
/** |
||||
* This class takes a java.sql.ResultSet object and generates an XML stream |
||||
* based on it's contents. |
||||
* |
||||
* $Id: XMLResultSet.java,v 1.1 2001/01/23 10:22:20 peter Exp $ |
||||
*/ |
||||
public class XMLResultSet |
||||
{ |
||||
/** |
||||
* The current ResultSet to process |
||||
*/ |
||||
protected ResultSet rs; |
||||
|
||||
/** |
||||
* The XMLFactory being used by this instance |
||||
*/ |
||||
protected XMLFactory factory; |
||||
|
||||
/** |
||||
* The default properties used when none are supplied by the user |
||||
*/ |
||||
protected static Properties defaults; |
||||
|
||||
/** |
||||
* The default property name for defining the tag name used to define a |
||||
* ResultSet |
||||
*/ |
||||
public static String RESULTSET_NAME = "resultset.name"; |
||||
|
||||
/** |
||||
* The default tag name for a resultset |
||||
*/ |
||||
public static String DEFAULT_RESULTSET_NAME = "RESULTSET"; |
||||
|
||||
/** |
||||
* The default property name for defining the tag name used to define a row |
||||
*/ |
||||
public static String ROW_NAME = "row.name"; |
||||
|
||||
/** |
||||
* The default tag name for a row |
||||
*/ |
||||
public static String DEFAULT_ROW_NAME = "RECORD"; |
||||
|
||||
/** |
||||
* The default tag name for a resultset |
||||
*/ |
||||
public static String COLNAME = ".name"; |
||||
|
||||
/** |
||||
* The value of the property (named as its related column) used to define |
||||
* how the column is generated. This indicates that the columns data is |
||||
* enclosed within a pair of tags, ie: <id>1234</id> |
||||
*/ |
||||
public static String CONTENT = "content"; |
||||
|
||||
/** |
||||
* The value of the property (named as its related column) used to define |
||||
* how the column is generated. This indicates that the columns data is |
||||
* an attribute in the columns tag. ie: <id value="1234" /> |
||||
*/ |
||||
public static String ATTRIBUTE = "attribute"; |
||||
|
||||
/** |
||||
* This is the default attribute name used when the ATTRIBUTE option is set. |
||||
*/ |
||||
public static String DEFAULT_ATTRIBUTE = "VALUE"; |
||||
|
||||
/** |
||||
* The value of the property (named as its related column) used to define |
||||
* how the column is generated. This indicates that the columns data is |
||||
* an attribute in the parent's tag. ie: <row id="1234" /> |
||||
*/ |
||||
public static String ROW_ATTRIBUTE = "row"; |
||||
|
||||
/** |
||||
* This property name marks the begining row number within the ResultSet to |
||||
* start processing. |
||||
*/ |
||||
public static String FIRST_ROW = "row.first"; |
||||
|
||||
/** |
||||
* This property name marks the last row number within the ResultSet to |
||||
* end processing. |
||||
*/ |
||||
public static String LAST_ROW = "row.last"; |
||||
|
||||
/** |
||||
* Constructor |
||||
*/ |
||||
public XMLResultSet() |
||||
{ |
||||
factory = new XMLFactory(); |
||||
} |
||||
|
||||
/** |
||||
* Constructor |
||||
*/ |
||||
public XMLResultSet(ResultSet rs) |
||||
{ |
||||
this(); |
||||
setResultSet(rs); |
||||
} |
||||
|
||||
/** |
||||
* Sets the ResultSet to use |
||||
* @param rs ResultSet |
||||
*/ |
||||
public void setResultSet(ResultSet rs) |
||||
{ |
||||
this.rs=rs; |
||||
} |
||||
|
||||
/** |
||||
* @return the current ResultSet |
||||
* |
||||
*/ |
||||
public ResultSet getResultSet() |
||||
{ |
||||
return rs; |
||||
} |
||||
|
||||
/** |
||||
* Sets the Writer to send all output to |
||||
* @param out Writer |
||||
* @throws IOException from XMLFactory |
||||
* @see XMLFactory.setWriter |
||||
*/ |
||||
public void setWriter(Writer out) |
||||
throws IOException |
||||
{ |
||||
factory.setWriter(out); |
||||
} |
||||
|
||||
/** |
||||
* @return Writer output is going to |
||||
*/ |
||||
public Writer getWriter() |
||||
{ |
||||
return factory.getWriter(); |
||||
} |
||||
|
||||
/** |
||||
* @return XMLFactory being used |
||||
*/ |
||||
public XMLFactory getXMLFactory() |
||||
{ |
||||
return factory; |
||||
} |
||||
|
||||
/** |
||||
* Flushes all output to the Writer |
||||
* @throw IOException from Writer |
||||
* @throw XMLFactoryException from XMLFactory |
||||
*/ |
||||
public void close() |
||||
throws IOException, XMLFactoryException |
||||
{ |
||||
factory.close(); |
||||
} |
||||
|
||||
/** |
||||
* Returns the default properties used by translate() and buildDTD() |
||||
* @return Properties default property settings |
||||
*/ |
||||
public static Properties getDefaultProperties() |
||||
{ |
||||
if(defaults==null) { |
||||
defaults=new Properties(); |
||||
defaults.setProperty(RESULTSET_NAME,DEFAULT_RESULTSET_NAME); |
||||
defaults.setProperty(ROW_NAME,DEFAULT_ROW_NAME); |
||||
} |
||||
return defaults; |
||||
} |
||||
|
||||
/** |
||||
* This generates an XML version of a ResultSet sending it to the supplied |
||||
* Writer. |
||||
* @param rs ResultSet to convert |
||||
* @param p Properties for the conversion |
||||
* @param out Writer to send output to (replaces existing one) |
||||
* @throws XMLFactoryException from XMLFactory |
||||
* @throws IOException from Writer |
||||
* @throws SQLException from ResultSet |
||||
*/ |
||||
public void translate(ResultSet rs,Properties p,Writer out) |
||||
throws XMLFactoryException, IOException, SQLException |
||||
{ |
||||
factory.setWriter(out); |
||||
translate(rs,p); |
||||
} |
||||
|
||||
/** |
||||
* This generates an XML version of a ResultSet sending it to the supplied |
||||
* Writer using a default tag struct |
||||
* @param rs ResultSet to convert |
||||
* @param out Writer to send output to (replaces existing one) |
||||
* @throws XMLFactoryException from XMLFactory |
||||
* @throws IOException from Writer |
||||
* @throws SQLException from ResultSet |
||||
*/ |
||||
public void translate(ResultSet rs,Writer out) |
||||
throws XMLFactoryException, IOException, SQLException |
||||
{ |
||||
factory.setWriter(out); |
||||
translate(rs,(Properties)null); |
||||
} |
||||
|
||||
/** |
||||
* This generates an XML version of a ResultSet sending it to the current |
||||
* output stream using a default tag structure. |
||||
* @param rs ResultSet to convert |
||||
* @throws XMLFactoryException from XMLFactory |
||||
* @throws IOException from Writer |
||||
* @throws SQLException from ResultSet |
||||
*/ |
||||
public void translate(ResultSet rs) |
||||
throws XMLFactoryException, IOException, SQLException |
||||
{ |
||||
translate(rs,(Properties)null); |
||||
} |
||||
|
||||
/** |
||||
* This generates an XML version of a ResultSet sending it to the current |
||||
* output stream. |
||||
* @param rs ResultSet to convert |
||||
* @param p Properties for the conversion |
||||
* @throws XMLFactoryException from XMLFactory |
||||
* @throws IOException from Writer |
||||
* @throws SQLException from ResultSet |
||||
*/ |
||||
public void translate(ResultSet rs,Properties p) |
||||
throws XMLFactoryException, IOException, SQLException |
||||
{ |
||||
// if we don't pass any properties, create an empty one and cache it if
|
||||
// further calls do the same
|
||||
if(p==null) { |
||||
p=getDefaultProperties(); |
||||
} |
||||
|
||||
// Fetch some common values
|
||||
String setName = p.getProperty(RESULTSET_NAME,DEFAULT_RESULTSET_NAME); |
||||
String rowName = p.getProperty(ROW_NAME,DEFAULT_ROW_NAME); |
||||
|
||||
ResultSetMetaData rsmd = rs.getMetaData(); |
||||
int numcols = rsmd.getColumnCount(); |
||||
|
||||
String colname[] = new String[numcols]; // field name cache
|
||||
int coltype[] = new int[numcols]; // true to use attribute false content
|
||||
String colattr[] = new String[numcols]; // Attribute name
|
||||
|
||||
// These deal with when an attribute is to go into the row's tag parameters
|
||||
int parentFields[] = getRowAttributes(numcols,colname,colattr,coltype,rsmd,p); // used to cache the id's
|
||||
int numParents= parentFields==null ? 0 : parentFields.length; // number of parent fields
|
||||
boolean haveParent= numParents>0; // true only if we need to us these
|
||||
|
||||
// This allows some limiting of the output result
|
||||
int firstRow = Integer.parseInt(p.getProperty(FIRST_ROW,"0")); |
||||
int lastRow = Integer.parseInt(p.getProperty(LAST_ROW,"0")); |
||||
int curRow=0; |
||||
|
||||
// Start the result set's tag
|
||||
factory.startTag(setName); |
||||
|
||||
while(rs.next()) { |
||||
if(firstRow<=curRow && (lastRow==0 || curRow<lastRow)) { |
||||
factory.startTag(rowName); |
||||
|
||||
if(haveParent) { |
||||
// Add any ROW_ATTRIBUTE entries
|
||||
for(int i=0;i<numParents;i++) |
||||
factory.addAttribute(colname[i],rs.getString(i+1)); |
||||
} |
||||
|
||||
// Process any CONTENT & ATTRIBUTE entries.
|
||||
// This skips if all the entries are ROW_ATTRIBUTE's
|
||||
if(numParents < numcols) { |
||||
for(int i=1;i<=numcols;i++) { |
||||
// Now do we write the value as an argument or as PCDATA?
|
||||
switch(coltype[i-1]) { |
||||
case 1: |
||||
factory.startTag(colname[i-1]); |
||||
factory.addAttribute(colattr[i-1],rs.getString(i)); |
||||
factory.endTag(); |
||||
break; |
||||
|
||||
case 0: |
||||
factory.startTag(colname[i-1]); |
||||
factory.addContent(rs.getString(i)); |
||||
factory.endTag(); |
||||
break; |
||||
|
||||
default: |
||||
// Unknown type. This should only be called for ROW_ATTRIBUTE which
|
||||
// is handled before this loop.
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// End the row
|
||||
factory.endTag(); |
||||
} |
||||
curRow++; |
||||
|
||||
} // check for firstRow <= curRow <= lastRow
|
||||
|
||||
// Close the result set's tag
|
||||
factory.endTag(); |
||||
} |
||||
|
||||
/** |
||||
* This method takes a ResultSet and writes it's DTD to the current writer |
||||
* @param rs ResultSet |
||||
*/ |
||||
public void buildDTD(ResultSet rs) |
||||
throws IOException, SQLException |
||||
{ |
||||
buildDTD(rs,null,getWriter()); |
||||
} |
||||
|
||||
/** |
||||
* This method takes a ResultSet and writes it's DTD to the current writer |
||||
* @param rs ResultSet |
||||
* @param out Writer to send output to |
||||
*/ |
||||
public void buildDTD(ResultSet rs,Writer out) |
||||
throws IOException, SQLException |
||||
{ |
||||
buildDTD(rs,null,out); |
||||
} |
||||
|
||||
/** |
||||
* This method takes a ResultSet and writes it's DTD to the current writer |
||||
* @param rs ResultSet |
||||
* @param out Writer to send output to |
||||
*/ |
||||
public void buildDTD(ResultSet rs,Properties p) |
||||
throws IOException, SQLException |
||||
{ |
||||
buildDTD(rs,p,getWriter()); |
||||
} |
||||
|
||||
/** |
||||
* This method takes a ResultSet and writes it's DTD to the current a. |
||||
* |
||||
* <p>ToDo:<ol> |
||||
* <li>Add ability to have NULLABLE columns appear as optional (ie instead of |
||||
* x, have x? (DTD for Optional). Can't use + or * as that indicates more than |
||||
* 1 instance). |
||||
* </ol> |
||||
* |
||||
* @param rs ResultSet |
||||
* @param p Properties defining tag types (as translate) |
||||
* @param out Writer to send output to |
||||
*/ |
||||
public void buildDTD(ResultSet rs,Properties p,Writer out) |
||||
throws IOException, SQLException |
||||
{ |
||||
// if we don't pass any properties, create an empty one and cache it if
|
||||
// further calls do the same
|
||||
if(p==null) { |
||||
p=getDefaultProperties(); |
||||
} |
||||
|
||||
// Fetch some common values
|
||||
String setName = p.getProperty(RESULTSET_NAME,DEFAULT_RESULTSET_NAME); |
||||
String rowName = p.getProperty(ROW_NAME,DEFAULT_ROW_NAME); |
||||
|
||||
ResultSetMetaData rsmd = rs.getMetaData(); |
||||
int numcols = rsmd.getColumnCount(); |
||||
|
||||
String colname[] = new String[numcols]; // field name cache
|
||||
int coltype[] = new int[numcols]; // true to use attribute false content
|
||||
String colattr[] = new String[numcols]; // Attribute name
|
||||
|
||||
// These deal with when an attribute is to go into the row's tag parameters
|
||||
int parentFields[] = getRowAttributes(numcols,colname,colattr,coltype,rsmd,p); // used to cache the id's
|
||||
int numParents= parentFields==null ? 0 : parentFields.length; // number of parent fields
|
||||
boolean haveParent= numParents>0; // true only if we need to us these
|
||||
|
||||
// Now the dtd defining the ResultSet
|
||||
out.write("<!ELEMENT "); |
||||
out.write(setName); |
||||
out.write(" ("); |
||||
out.write(rowName); |
||||
out.write("*)>\n"); |
||||
|
||||
// Now the dtd defining each row
|
||||
out.write("<!ELEMENT "); |
||||
out.write(rowName); |
||||
out.write(" ("); |
||||
boolean s=false; |
||||
for(int i=0;i<numcols;i++) { |
||||
if(coltype[i]!=2) { // not ROW_ATTRIBUTE
|
||||
if(s) |
||||
out.write(","); |
||||
out.write(colname[i]); |
||||
s=true; |
||||
} |
||||
} |
||||
out.write(")>\n"); |
||||
|
||||
// Now handle any ROW_ATTRIBUTE's
|
||||
if(haveParent) { |
||||
out.write("<!ATTLIST "); |
||||
out.write(rowName); |
||||
for(int i=0;i<numParents;i++) { |
||||
out.write("\n "); |
||||
out.write(colname[parentFields[i]]); |
||||
out.write(" CDATA #IMPLIED"); |
||||
} |
||||
out.write("\n>\n"); |
||||
} |
||||
|
||||
// Now add any CONTENT & ATTRIBUTE fields
|
||||
for(int i=0;i<numcols;i++) { |
||||
if(coltype[i]!=2) { |
||||
out.write("<!ELEMENT "); |
||||
out.write(colname[i]); |
||||
|
||||
// CONTENT
|
||||
if(coltype[i]==0) { |
||||
out.write(" (#PCDATA)"); |
||||
} else { |
||||
out.write(" EMPTY"); |
||||
} |
||||
|
||||
out.write(">\n"); |
||||
|
||||
// ATTRIBUTE
|
||||
if(coltype[i]==1) { |
||||
out.write("<!ATTLIST "); |
||||
out.write(colname[i]); |
||||
out.write("\n "); |
||||
out.write(colattr[i]); |
||||
out.write(" CDATA #IMPLIED\n>\n"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Private method used by the core translate and buildDTD methods. |
||||
* @param numcols Number of columns in ResultSet |
||||
* @param colname Array of column names |
||||
* @param colattr Array of column attribute names |
||||
* @param coltype Array of column types |
||||
* @param rsmd ResultSetMetaData for ResultSet |
||||
* @param p Properties being used |
||||
* @return array containing field numbers which should appear as attributes |
||||
* within the rows tag. |
||||
* @throws SQLException from JDBC |
||||
*/ |
||||
private int[] getRowAttributes(int numcols, |
||||
String colname[],String colattr[], |
||||
int coltype[], |
||||
ResultSetMetaData rsmd,Properties p) |
||||
throws SQLException |
||||
{ |
||||
int pf[] = null; |
||||
int nf = 0; |
||||
|
||||
// Now we put a columns value as an attribute if the property
|
||||
// fieldname=attribute (ie myname=attribute)
|
||||
// and if the fieldname.name property exists, use it as the attribute name
|
||||
for(int i=0;i<numcols;i++) { |
||||
colname[i] = rsmd.getColumnName(i+1); |
||||
colattr[i] = p.getProperty(colname[i]+COLNAME,DEFAULT_ATTRIBUTE); |
||||
if(p.getProperty(colname[i],CONTENT).equals(ROW_ATTRIBUTE)) { |
||||
// Ok, ROW_ATTRIBUTE's need to be cached, so add them in here
|
||||
coltype[i]=2; |
||||
if(pf==null) { |
||||
pf = new int[numcols]; // Max possible number of entries
|
||||
} |
||||
pf[nf++] = i; |
||||
} else { |
||||
// Normal CONTENT or ATTRIBUTE entry
|
||||
coltype[i] = p.getProperty(colname[i],CONTENT).equals(ATTRIBUTE) ? 1 : 0; |
||||
} |
||||
} |
||||
|
||||
// Form an array exactly nf elements long
|
||||
if(nf>0) { |
||||
int r[] = new int[nf]; |
||||
System.arraycopy(pf,0,r,0,nf); |
||||
return r; |
||||
} |
||||
|
||||
// Return null if no tags are to appear as attributes to the row's tag
|
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,191 @@ |
||||
package uk.org.retep.xml.test; |
||||
|
||||
import java.lang.Exception; |
||||
import java.io.*; |
||||
import java.sql.*; |
||||
import java.util.Properties; |
||||
import uk.org.retep.xml.core.XMLFactoryException; |
||||
import uk.org.retep.xml.jdbc.XMLDatabase; |
||||
import uk.org.retep.xml.jdbc.XMLResultSet; |
||||
|
||||
/** |
||||
* This "test" class is a fully functional tool in its own right. It utilises |
||||
* the xml classes to query and export to XML, or to dump database structures |
||||
* into XML. |
||||
*/ |
||||
|
||||
public class XMLExport |
||||
{ |
||||
/** |
||||
* The current Database Connection |
||||
*/ |
||||
protected Connection conn; |
||||
protected Statement stat; |
||||
protected String drvr,url,table; |
||||
|
||||
protected XMLResultSet xrs; |
||||
protected XMLDatabase xdb; |
||||
protected Properties prop; |
||||
protected boolean outXML; |
||||
protected boolean outDTD; |
||||
protected boolean outTAB; |
||||
protected int maxRows=0; |
||||
|
||||
public XMLExport(String[] args) |
||||
throws IOException,SQLException,XMLFactoryException,ClassNotFoundException |
||||
{ |
||||
xrs = new XMLResultSet(); |
||||
xrs.setWriter(new OutputStreamWriter(System.out)); |
||||
//Properties p = new Properties(xrs.getDefaultProperties());
|
||||
prop = (Properties) xrs.getDefaultProperties().clone(); |
||||
|
||||
xdb = new XMLDatabase(xrs.getXMLFactory()); |
||||
|
||||
for(int i=0;i<args.length;i++) { |
||||
String arg=args[i]; |
||||
if(arg.startsWith("-D")) { |
||||
// Load JDBC Driver
|
||||
drvr=arg.substring(2); |
||||
Class.forName(drvr); |
||||
System.out.println("Now using JDBC Driver: "+drvr); |
||||
} else if(arg.startsWith("-J")) { |
||||
// Open a JDBC Connection (closing the existing one, if any)
|
||||
close(); |
||||
url = arg.substring(2); |
||||
conn = DriverManager.getConnection(url); |
||||
System.out.println("Opened "+url); |
||||
stat=null; |
||||
} else if(arg.startsWith("-M")) { |
||||
// Set the maximum number of rows to process (0=no limit)
|
||||
maxRows=Integer.parseInt(arg.substring(2)); |
||||
if(maxRows<0) |
||||
maxRows=0; |
||||
prop.setProperty(XMLResultSet.FIRST_ROW,"0"); |
||||
prop.setProperty(XMLResultSet.LAST_ROW,Integer.toString(maxRows)); |
||||
} else if(arg.startsWith("-O")) { |
||||
// Set the output file for XML & DTD
|
||||
xrs.setWriter(new FileWriter(arg.substring(2))); |
||||
System.out.println("XML/DTD Output now going to "+arg.substring(2)); |
||||
} else if(arg.startsWith("-P")) { |
||||
// Set a parameter for XML & DTD
|
||||
int p = arg.indexOf('='); |
||||
prop.setProperty(arg.substring(2,p),arg.substring(p+1)); |
||||
} else if(arg.startsWith("-S")) { |
||||
// -Stable generate schema of just table
|
||||
// -S generate schema of entire database
|
||||
if(arg.length()>2) { |
||||
String table=arg.substring(2); |
||||
System.out.println("Generating XML Schema of table "+table); |
||||
xdb.writeTable(conn,table); |
||||
xdb.close(); |
||||
} else { |
||||
System.out.println("Generating XML Schema of database"); |
||||
xdb.writeDatabase(conn); |
||||
xdb.close(); |
||||
} |
||||
} else if(arg.equals("-V")) { |
||||
// Select table output
|
||||
outXML=outDTD=false; |
||||
} else if(arg.equals("-X")) { |
||||
// Select XML output
|
||||
outXML=true; |
||||
outDTD=outTAB=false; |
||||
} else if(arg.equals("-Y")) { |
||||
// Select DTD output
|
||||
outXML=outTAB=false; |
||||
outDTD=true; |
||||
} else if(arg.startsWith("-")) { |
||||
System.err.println("Unknown argument: "+arg); |
||||
System.exit(1); |
||||
} else { |
||||
// Ok, anything not starting with "-" are queries
|
||||
if(stat==null) |
||||
stat=conn.createStatement(); |
||||
|
||||
System.out.println("Executing "+arg); |
||||
ResultSet rs = stat.executeQuery(arg); |
||||
if(rs!=null) { |
||||
if(outXML) { |
||||
xrs.translate(rs,prop); |
||||
xrs.close(); |
||||
} else if(outDTD) { |
||||
// Output the DTD
|
||||
xrs.buildDTD(rs,prop); |
||||
xrs.close(); |
||||
} else { |
||||
// Normal resultset output
|
||||
int rc=0; |
||||
|
||||
ResultSetMetaData rsmd = rs.getMetaData(); |
||||
int nc = rsmd.getColumnCount(); |
||||
boolean us=false; |
||||
for(int c=0;c<nc;c++) { |
||||
if(us) |
||||
System.out.print("\t"); |
||||
System.out.print(rsmd.getColumnName(c+1)); |
||||
us=true; |
||||
} |
||||
System.out.println(); |
||||
|
||||
while(rs.next() && (maxRows==0 || rc<maxRows)) { |
||||
us=false; |
||||
for(int c=0;c<nc;c++) { |
||||
if(us) |
||||
System.out.print("\t"); |
||||
System.out.print(rs.getString(c+1)); |
||||
us=true; |
||||
} |
||||
System.out.println(); |
||||
rc++; |
||||
} |
||||
|
||||
System.out.println("Returned "+rc+" rows."); |
||||
} |
||||
rs.close(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
close(); |
||||
} |
||||
|
||||
public void close() throws SQLException |
||||
{ |
||||
if(conn!=null) { |
||||
conn.close(); |
||||
System.out.println("Closed "+url); |
||||
conn=null; |
||||
stat=null; |
||||
} |
||||
} |
||||
|
||||
public static void main(String[] args) |
||||
{ |
||||
if(args.length==0) { |
||||
System.out.println("Useage: java uk.org.retep.xml.test.XMLExport [args ...]\nwhere args are:\n"+ |
||||
"-Dclass.name JDBC Driver Class\n"+ |
||||
"-Jurl JDBC URL\n"+ |
||||
"-Mmax Maximum number of rows to process\n"+ |
||||
"-Ofilename Send all XML or DTD output to file\n"+ |
||||
"-Pkey=value Property passed on to XMLResultSet\n"+ |
||||
"-S[table] Write XML description of table. Whole DB if table left out.\n"+ |
||||
"-V Default: Write result to System.out\n"+ |
||||
"-X Write result in XML to System.out\n"+ |
||||
"-Y Write DTD describing result to System.out\n"+ |
||||
"\nAny other argument not starting with - is treated as an SQL Query\n"+ |
||||
"\nFor example:\n"+ |
||||
"To dump the table structure of a database into db.xml, use\n $ java uk.org.retep.xml.test.XMLExport -Doracle.jdbc.driver.OracleDriver -Jjdbc:oracle:thin:dbname/username@localhost:1521:ISORCL -Odb.xml -S\n"+ |
||||
"To dump the structure of a single table PRODUCTS and write into products.xml, use\n $ clear;java uk.org.retep.xml.test.XMLExport -Doracle.jdbc.driver.OracleDriver-Jjdbc:oracle:thin:dbname/username@localhost:1521:ISORCL -Oproducts.xml -SPRODUCT\n"+ |
||||
"To query a table and write the results into standard out as XML, use\n $ java uk.org.retep.xml.test.XMLExport -Doracle.jdbc.driver.OracleDriver -Jjdbc:oracle:thin:dbname/username@localhost:1521:ISORCL -M5 -PSKU=row -PIMAGE=attribute -X \"select sku,image,template from product\"\n"+ |
||||
"To query a table and write a DTD describing the ResultSet, use\n $ java uk.org.retep.xml.test.XMLExport -Doracle.jdbc.driver.OracleDriver -Jjdbc:oracle:thin:dbname/username@localhost:1521:ISORCL -M5 -PSKU=row -PIMAGE=attribute -Y \"select sku,image,template from product\"\n" |
||||
); |
||||
System.exit(1); |
||||
} |
||||
|
||||
try { |
||||
XMLExport XMLExport1 = new XMLExport(args); |
||||
} catch(Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue