Some more additions to contrib for JDBC

This commit is contained in:
Peter Mount 2001-01-23 10:22:22 +00:00
parent d09fc12044
commit 11cb9acb68
11 changed files with 4003 additions and 5 deletions

View File

@ -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.

View File

@ -2,7 +2,7 @@
build file to build the donated retep tools packages
$Id: build.xml,v 1.1 2001/01/18 14:50:14 peter Exp $
$Id: build.xml,v 1.2 2001/01/23 10:22:18 peter Exp $
-->
@ -44,7 +44,7 @@
<!-- Builds the various jar files -->
<target name="jar" depends="xml">
<jar jarfile="${jars}/retepTools.jar" basedir="${dest}">
<include name="${package}/xml/parser/**" if="xml" />
<include name="${package}/xml/**" if="xml" />
</jar>
</target>

View File

@ -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
>

2691
contrib/retep/data/cds.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
<property category="sys" name="CompanyLabel" value="Company:" />
<property category="sys" name="Copyright" value="Copyright (c) 2001" />
<property category="sys" name="CopyrightLabel" value="Copyright:" />
<property category="sys" name="DefaultPackage" value="org.postgresql.core" />
<property category="sys" name="DefaultPackage" value="uk.org.retep.xml.jdbc" />
<property category="sys" name="Description" value="" />
<property category="sys" name="DescriptionLabel" value="Description:" />
<property category="sys" name="DocPath" value="doc" />
@ -25,7 +25,7 @@
<property category="sys" name="InstanceVisibility" value="0" />
<property category="sys" name="JDK" value="java 1.3.0-C" />
<property category="sys" name="LastTag" value="0" />
<property category="sys" name="Libraries" value="JAXP" />
<property category="sys" name="Libraries" value="JAXP;Oracle JDBC" />
<property category="sys" name="MakeStable" value="0" />
<property category="sys" name="OutPath" value="build" />
<property category="sys" name="SourcePath" value="." />
@ -34,6 +34,8 @@
<property category="sys" name="Version" value="1.0" />
<property category="sys" name="VersionLabel" value="@version" />
<property category="sys" name="WorkingDirectory" value="." />
<node type="Folder" name="core" />
<node type="Package" name="uk.org.retep.xml.core" />
<node type="Package" name="uk.org.retep.xml.jdbc" />
<node type="Package" name="uk.org.retep.xml.parser" />
<file path="build.xml" />

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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: &lt;id&gt;1234&lt;/id&gt;
*/
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;
}
}

View File

@ -4,7 +4,6 @@ import java.io.CharArrayWriter;
import java.io.IOException;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -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();
}
}
}