/*
* file: DataExportUtility.java
* author: Jon Iles
* copyright: (c) Packwood Software 2011
* date: 05/04/2011
*/
/*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sf.mpxj.utility;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Simple utility to export data to an XML file from an arbitrary database
* schema.
*/
public final class DataExportUtility
{
/**
* Command line entry point.
*
* @param argv command line arguments
*/
public static void main(String[] argv)
{
if (argv.length != 2)
{
System.out.println("DataExport <filename> <output directory>");
}
else
{
Connection connection = null;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url = "jdbc:odbc:DRIVER=Microsoft Access Driver (*.mdb);DBQ=" + argv[0];
connection = DriverManager.getConnection(url);
DataExportUtility dx = new DataExportUtility();
dx.process(connection, argv[1]);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (connection != null)
{
try
{
connection.close();
}
catch (SQLException ex)
{
// silently ignore exceptions when closing connection
}
}
}
}
}
/**
* Export data base contents to a directory using supplied connection.
*
* @param connection database connection
* @param directory target directory
* @throws Exception
*/
public void process(Connection connection, String directory) throws Exception
{
connection.setAutoCommit(true);
//
// Retrieve meta data about the connection
//
DatabaseMetaData dmd = connection.getMetaData();
String[] types =
{
"TABLE"
};
FileWriter fw = new FileWriter(directory);
PrintWriter pw = new PrintWriter(fw);
pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
pw.println();
pw.println("<database>");
ResultSet tables = dmd.getTables(null, null, null, types);
while (tables.next() == true)
{
processTable(pw, connection, tables.getString("TABLE_NAME"));
}
pw.println("</database>");
pw.close();
tables.close();
}
/**
* Process a single table.
*
* @param pw output print writer
* @param connection database connection
* @param name table name
* @throws Exception
*/
private void processTable(PrintWriter pw, Connection connection, String name) throws Exception
{
System.out.println("Processing " + name);
//
// Prepare statement to retrieve all data
//
PreparedStatement ps = connection.prepareStatement("select * from " + name);
//
// Execute the query
//
ResultSet rs = ps.executeQuery();
//
// Retrieve column meta data
//
ResultSetMetaData rmd = ps.getMetaData();
int index;
int columnCount = rmd.getColumnCount();
String[] columnNames = new String[columnCount];
int[] columnTypes = new int[columnCount];
int[] columnPrecision = new int[columnCount];
int[] columnScale = new int[columnCount];
for (index = 0; index < columnCount; index++)
{
columnNames[index] = rmd.getColumnName(index + 1);
columnTypes[index] = rmd.getColumnType(index + 1);
if (columnTypes[index] == Types.NUMERIC)
{
columnPrecision[index] = rmd.getPrecision(index + 1);
columnScale[index] = rmd.getScale(index + 1);
}
}
//
// Generate the output file
//
pw.println("<table name=\"" + name + "\">");
StringBuilder buffer = new StringBuilder(255);
DateFormat df = new SimpleDateFormat("dd/MM/yyyy hh:mm", Locale.UK);
while (rs.next() == true)
{
pw.println(" <row>");
for (index = 0; index < columnCount; index++)
{
switch (columnTypes[index])
{
case Types.BINARY:
case Types.BLOB:
case Types.LONGVARBINARY:
case Types.VARBINARY:
{
pw.print(" <column name=\"" + columnNames[index] + "\" type=\"" + columnTypes[index] + "\">");
pw.println("[BINARY DATA]");
pw.println("</column>");
break;
}
case Types.DATE:
case Types.TIME:
{
Date data = rs.getDate(index + 1);
//if (data != null)
{
pw.print(" <column name=\"" + columnNames[index] + "\" type=\"" + columnTypes[index] + "\">");
if (data != null)
{
pw.print(df.format(data));
}
pw.println("</column>");
}
break;
}
case Types.TIMESTAMP:
{
Timestamp data = rs.getTimestamp(index + 1);
//if (data != null)
{
pw.print(" <column name=\"" + columnNames[index] + "\" type=\"" + columnTypes[index] + "\">");
if (data != null)
{
pw.print(data.toString());
}
pw.println("</column>");
}
break;
}
case Types.NUMERIC:
{
//
// If we have a non-null value, map the value to a
// more specific type
//
String data = rs.getString(index + 1);
//if (data != null)
{
int type = Types.NUMERIC;
int precision = columnPrecision[index];
int scale = columnScale[index];
if (scale == 0)
{
if (precision == 10)
{
type = Types.INTEGER;
}
else
{
if (precision == 5)
{
type = Types.SMALLINT;
}
else
{
if (precision == 1)
{
type = Types.BIT;
}
}
}
}
else
{
if (precision > 125)
{
type = Types.DOUBLE;
}
}
pw.print(" <column name=\"" + columnNames[index] + "\" type=\"" + type + "\">");
if (data != null)
{
pw.print(data);
}
pw.println("</column>");
}
break;
}
default:
{
String data = rs.getString(index + 1);
//if (data != null)
{
pw.print(" <column name=\"" + columnNames[index] + "\" type=\"" + columnTypes[index] + "\">");
if (data != null)
{
pw.print(escapeText(buffer, data));
}
pw.println("</column>");
}
break;
}
}
}
pw.println(" </row>");
}
pw.println("</table>");
ps.close();
}
/**
* Quick and dirty XML text escape.
*
* @param sb working string buffer
* @param text input text
* @return escaped text
*/
private String escapeText(StringBuilder sb, String text)
{
int length = text.length();
char c;
sb.setLength(0);
for (int loop = 0; loop < length; loop++)
{
c = text.charAt(loop);
switch (c)
{
case '<':
{
sb.append("<");
break;
}
case '>':
{
sb.append(">");
break;
}
case '&':
{
sb.append("&");
break;
}
default:
{
if (validXMLCharacter(c) == true)
{
if (c > 127)
{
sb.append("" + (int) c + ";");
}
else
{
sb.append(c);
}
}
break;
}
}
}
return (sb.toString());
}
/**
* Quick and dirty valid XML character test.
*
* @param c input character
* @return Boolean flag
*/
private boolean validXMLCharacter(char c)
{
return (c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0x10FFFF));
}
}