| /* |
| * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code 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 General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.rowset.internal; |
| |
| import com.sun.rowset.JdbcRowSetResourceBundle; |
| import java.sql.*; |
| import javax.sql.*; |
| import java.io.*; |
| import java.text.MessageFormat; |
| import java.util.*; |
| |
| import javax.sql.rowset.*; |
| import javax.sql.rowset.spi.*; |
| |
| /** |
| * An implementation of the <code>XmlWriter</code> interface, which writes a |
| * <code>WebRowSet</code> object to an output stream as an XML document. |
| */ |
| |
| public class WebRowSetXmlWriter implements XmlWriter, Serializable { |
| |
| /** |
| * The <code>java.io.Writer</code> object to which this <code>WebRowSetXmlWriter</code> |
| * object will write when its <code>writeXML</code> method is called. The value |
| * for this field is set with the <code>java.io.Writer</code> object given |
| * as the second argument to the <code>writeXML</code> method. |
| */ |
| private java.io.Writer writer; |
| |
| /** |
| * The <code>java.util.Stack</code> object that this <code>WebRowSetXmlWriter</code> |
| * object will use for storing the tags to be used for writing the calling |
| * <code>WebRowSet</code> object as an XML document. |
| */ |
| private java.util.Stack stack; |
| |
| private JdbcRowSetResourceBundle resBundle; |
| |
| public WebRowSetXmlWriter() { |
| |
| try { |
| resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); |
| } catch(IOException ioe) { |
| throw new RuntimeException(ioe); |
| } |
| } |
| |
| /** |
| * Writes the given <code>WebRowSet</code> object as an XML document |
| * using the given <code>java.io.Writer</code> object. The XML document |
| * will include the <code>WebRowSet</code> object's data, metadata, and |
| * properties. If a data value has been updated, that information is also |
| * included. |
| * <P> |
| * This method is called by the <code>XmlWriter</code> object that is |
| * referenced in the calling <code>WebRowSet</code> object's |
| * <code>xmlWriter</code> field. The <code>XmlWriter.writeXML</code> |
| * method passes to this method the arguments that were supplied to it. |
| * |
| * @param caller the <code>WebRowSet</code> object to be written; must |
| * be a rowset for which this <code>WebRowSetXmlWriter</code> object |
| * is the writer |
| * @param wrt the <code>java.io.Writer</code> object to which |
| * <code>caller</code> will be written |
| * @exception SQLException if a database access error occurs or |
| * this <code>WebRowSetXmlWriter</code> object is not the writer |
| * for the given rowset |
| * @see XmlWriter#writeXML |
| */ |
| public void writeXML(WebRowSet caller, java.io.Writer wrt) |
| throws SQLException { |
| |
| // create a new stack for tag checking. |
| stack = new java.util.Stack(); |
| writer = wrt; |
| writeRowSet(caller); |
| } |
| |
| /** |
| * Writes the given <code>WebRowSet</code> object as an XML document |
| * using the given <code>java.io.OutputStream</code> object. The XML document |
| * will include the <code>WebRowSet</code> object's data, metadata, and |
| * properties. If a data value has been updated, that information is also |
| * included. |
| * <P> |
| * Using stream is a faster way than using <code>java.io.Writer<code/> |
| * |
| * This method is called by the <code>XmlWriter</code> object that is |
| * referenced in the calling <code>WebRowSet</code> object's |
| * <code>xmlWriter</code> field. The <code>XmlWriter.writeXML</code> |
| * method passes to this method the arguments that were supplied to it. |
| * |
| * @param caller the <code>WebRowSet</code> object to be written; must |
| * be a rowset for which this <code>WebRowSetXmlWriter</code> object |
| * is the writer |
| * @param oStream the <code>java.io.OutputStream</code> object to which |
| * <code>caller</code> will be written |
| * @throws SQLException if a database access error occurs or |
| * this <code>WebRowSetXmlWriter</code> object is not the writer |
| * for the given rowset |
| * @see XmlWriter#writeXML |
| */ |
| public void writeXML(WebRowSet caller, java.io.OutputStream oStream) |
| throws SQLException { |
| |
| // create a new stack for tag checking. |
| stack = new java.util.Stack(); |
| writer = new OutputStreamWriter(oStream); |
| writeRowSet(caller); |
| } |
| |
| /** |
| * |
| * |
| * @exception SQLException if a database access error occurs |
| */ |
| private void writeRowSet(WebRowSet caller) throws SQLException { |
| |
| try { |
| |
| startHeader(); |
| |
| writeProperties(caller); |
| writeMetaData(caller); |
| writeData(caller); |
| |
| endHeader(); |
| |
| } catch (java.io.IOException ex) { |
| throw new SQLException(MessageFormat.format(resBundle.handleGetObject("wrsxmlwriter.ioex").toString(), ex.getMessage())); |
| } |
| } |
| |
| private void startHeader() throws java.io.IOException { |
| |
| setTag("webRowSet"); |
| writer.write("<?xml version=\"1.0\"?>\n"); |
| writer.write("<webRowSet xmlns=\"http://java.sun.com/xml/ns/jdbc\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); |
| writer.write("xsi:schemaLocation=\"http://java.sun.com/xml/ns/jdbc http://java.sun.com/xml/ns/jdbc/webrowset.xsd\">\n"); |
| } |
| |
| private void endHeader() throws java.io.IOException { |
| endTag("webRowSet"); |
| } |
| |
| /** |
| * |
| * |
| * @exception SQLException if a database access error occurs |
| */ |
| private void writeProperties(WebRowSet caller) throws java.io.IOException { |
| |
| beginSection("properties"); |
| |
| try { |
| propString("command", processSpecialCharacters(caller.getCommand())); |
| propInteger("concurrency", caller.getConcurrency()); |
| propString("datasource", caller.getDataSourceName()); |
| propBoolean("escape-processing", |
| caller.getEscapeProcessing()); |
| |
| try { |
| propInteger("fetch-direction", caller.getFetchDirection()); |
| } catch(SQLException sqle) { |
| // it may be the case that fetch direction has not been set |
| // fetchDir == 0 |
| // in that case it will throw a SQLException. |
| // To avoid that catch it here |
| } |
| |
| propInteger("fetch-size", caller.getFetchSize()); |
| propInteger("isolation-level", |
| caller.getTransactionIsolation()); |
| |
| beginSection("key-columns"); |
| |
| int[] kc = caller.getKeyColumns(); |
| for (int i = 0; kc != null && i < kc.length; i++) |
| propInteger("column", kc[i]); |
| |
| endSection("key-columns"); |
| |
| //Changed to beginSection and endSection for maps for proper indentation |
| beginSection("map"); |
| java.util.Map typeMap = caller.getTypeMap(); |
| if (typeMap != null) { |
| Iterator i = typeMap.keySet().iterator(); |
| Class c; |
| String type; |
| while (i.hasNext()) { |
| type = (String)i.next(); |
| c = (Class)typeMap.get(type); |
| propString("type", type); |
| propString("class", c.getName()); |
| } |
| } |
| endSection("map"); |
| |
| propInteger("max-field-size", caller.getMaxFieldSize()); |
| propInteger("max-rows", caller.getMaxRows()); |
| propInteger("query-timeout", caller.getQueryTimeout()); |
| propBoolean("read-only", caller.isReadOnly()); |
| |
| int itype = caller.getType(); |
| String strType = ""; |
| |
| if(itype == 1003) { |
| strType = "ResultSet.TYPE_FORWARD_ONLY"; |
| } else if(itype == 1004) { |
| strType = "ResultSet.TYPE_SCROLL_INSENSITIVE"; |
| } else if(itype == 1005) { |
| strType = "ResultSet.TYPE_SCROLL_SENSITIVE"; |
| } |
| |
| propString("rowset-type", strType); |
| |
| propBoolean("show-deleted", caller.getShowDeleted()); |
| propString("table-name", caller.getTableName()); |
| propString("url", caller.getUrl()); |
| |
| beginSection("sync-provider"); |
| // Remove the string after "@xxxx" |
| // before writing it to the xml file. |
| String strProviderInstance = (caller.getSyncProvider()).toString(); |
| String strProvider = strProviderInstance.substring(0, (caller.getSyncProvider()).toString().indexOf("@")); |
| |
| propString("sync-provider-name", strProvider); |
| propString("sync-provider-vendor", "Sun Microsystems Inc."); |
| propString("sync-provider-version", "1.0"); |
| propInteger("sync-provider-grade", caller.getSyncProvider().getProviderGrade()); |
| propInteger("data-source-lock", caller.getSyncProvider().getDataSourceLock()); |
| |
| endSection("sync-provider"); |
| |
| } catch (SQLException ex) { |
| throw new java.io.IOException(MessageFormat.format(resBundle.handleGetObject("wrsxmlwriter.sqlex").toString(), ex.getMessage())); |
| } |
| |
| endSection("properties"); |
| } |
| |
| /** |
| * |
| * |
| * @exception SQLException if a database access error occurs |
| */ |
| private void writeMetaData(WebRowSet caller) throws java.io.IOException { |
| int columnCount; |
| |
| beginSection("metadata"); |
| |
| try { |
| |
| ResultSetMetaData rsmd = caller.getMetaData(); |
| columnCount = rsmd.getColumnCount(); |
| propInteger("column-count", columnCount); |
| |
| for (int colIndex = 1; colIndex <= columnCount; colIndex++) { |
| beginSection("column-definition"); |
| |
| propInteger("column-index", colIndex); |
| propBoolean("auto-increment", rsmd.isAutoIncrement(colIndex)); |
| propBoolean("case-sensitive", rsmd.isCaseSensitive(colIndex)); |
| propBoolean("currency", rsmd.isCurrency(colIndex)); |
| propInteger("nullable", rsmd.isNullable(colIndex)); |
| propBoolean("signed", rsmd.isSigned(colIndex)); |
| propBoolean("searchable", rsmd.isSearchable(colIndex)); |
| propInteger("column-display-size",rsmd.getColumnDisplaySize(colIndex)); |
| propString("column-label", rsmd.getColumnLabel(colIndex)); |
| propString("column-name", rsmd.getColumnName(colIndex)); |
| propString("schema-name", rsmd.getSchemaName(colIndex)); |
| propInteger("column-precision", rsmd.getPrecision(colIndex)); |
| propInteger("column-scale", rsmd.getScale(colIndex)); |
| propString("table-name", rsmd.getTableName(colIndex)); |
| propString("catalog-name", rsmd.getCatalogName(colIndex)); |
| propInteger("column-type", rsmd.getColumnType(colIndex)); |
| propString("column-type-name", rsmd.getColumnTypeName(colIndex)); |
| |
| endSection("column-definition"); |
| } |
| } catch (SQLException ex) { |
| throw new java.io.IOException(MessageFormat.format(resBundle.handleGetObject("wrsxmlwriter.sqlex").toString(), ex.getMessage())); |
| } |
| |
| endSection("metadata"); |
| } |
| |
| /** |
| * |
| * |
| * @exception SQLException if a database access error occurs |
| */ |
| private void writeData(WebRowSet caller) throws java.io.IOException { |
| ResultSet rs; |
| |
| try { |
| ResultSetMetaData rsmd = caller.getMetaData(); |
| int columnCount = rsmd.getColumnCount(); |
| int i; |
| |
| beginSection("data"); |
| |
| caller.beforeFirst(); |
| caller.setShowDeleted(true); |
| while (caller.next()) { |
| if (caller.rowDeleted() && caller.rowInserted()) { |
| beginSection("modifyRow"); |
| } else if (caller.rowDeleted()) { |
| beginSection("deleteRow"); |
| } else if (caller.rowInserted()) { |
| beginSection("insertRow"); |
| } else { |
| beginSection("currentRow"); |
| } |
| |
| for (i = 1; i <= columnCount; i++) { |
| if (caller.columnUpdated(i)) { |
| rs = caller.getOriginalRow(); |
| rs.next(); |
| beginTag("columnValue"); |
| writeValue(i, (RowSet)rs); |
| endTag("columnValue"); |
| beginTag("updateRow"); |
| writeValue(i, caller); |
| endTag("updateRow"); |
| } else { |
| beginTag("columnValue"); |
| writeValue(i, caller); |
| endTag("columnValue"); |
| } |
| } |
| |
| endSection(); // this is unchecked |
| } |
| endSection("data"); |
| } catch (SQLException ex) { |
| throw new java.io.IOException(MessageFormat.format(resBundle.handleGetObject("wrsxmlwriter.sqlex").toString(), ex.getMessage())); |
| } |
| } |
| |
| private void writeValue(int idx, RowSet caller) throws java.io.IOException { |
| try { |
| int type = caller.getMetaData().getColumnType(idx); |
| |
| switch (type) { |
| case java.sql.Types.BIT: |
| case java.sql.Types.BOOLEAN: |
| boolean b = caller.getBoolean(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeBoolean(b); |
| break; |
| case java.sql.Types.TINYINT: |
| case java.sql.Types.SMALLINT: |
| short s = caller.getShort(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeShort(s); |
| break; |
| case java.sql.Types.INTEGER: |
| int i = caller.getInt(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeInteger(caller.getInt(idx)); |
| break; |
| case java.sql.Types.BIGINT: |
| long l = caller.getLong(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeLong(l); |
| break; |
| case java.sql.Types.REAL: |
| case java.sql.Types.FLOAT: |
| float f = caller.getFloat(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeFloat(f); |
| break; |
| case java.sql.Types.DOUBLE: |
| double d = caller.getDouble(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeDouble(d); |
| break; |
| case java.sql.Types.NUMERIC: |
| case java.sql.Types.DECIMAL: |
| writeBigDecimal(caller.getBigDecimal(idx)); |
| break; |
| case java.sql.Types.BINARY: |
| case java.sql.Types.VARBINARY: |
| case java.sql.Types.LONGVARBINARY: |
| break; |
| case java.sql.Types.DATE: |
| java.sql.Date date = caller.getDate(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeLong(date.getTime()); |
| break; |
| case java.sql.Types.TIME: |
| java.sql.Time time = caller.getTime(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeLong(time.getTime()); |
| break; |
| case java.sql.Types.TIMESTAMP: |
| java.sql.Timestamp ts = caller.getTimestamp(idx); |
| if (caller.wasNull()) |
| writeNull(); |
| else |
| writeLong(ts.getTime()); |
| break; |
| case java.sql.Types.CHAR: |
| case java.sql.Types.VARCHAR: |
| case java.sql.Types.LONGVARCHAR: |
| writeStringData(caller.getString(idx)); |
| break; |
| default: |
| System.out.println(resBundle.handleGetObject("wsrxmlwriter.notproper").toString()); |
| //Need to take care of BLOB, CLOB, Array, Ref here |
| } |
| } catch (SQLException ex) { |
| throw new java.io.IOException(resBundle.handleGetObject("wrsxmlwriter.failedwrite").toString()+ ex.getMessage()); |
| } |
| } |
| |
| /* |
| * This begins a new tag with a indent |
| * |
| */ |
| private void beginSection(String tag) throws java.io.IOException { |
| // store the current tag |
| setTag(tag); |
| |
| writeIndent(stack.size()); |
| |
| // write it out |
| writer.write("<" + tag + ">\n"); |
| } |
| |
| /* |
| * This closes a tag started by beginTag with a indent |
| * |
| */ |
| private void endSection(String tag) throws java.io.IOException { |
| writeIndent(stack.size()); |
| |
| String beginTag = getTag(); |
| |
| if(beginTag.indexOf("webRowSet") != -1) { |
| beginTag ="webRowSet"; |
| } |
| |
| if (tag.equals(beginTag) ) { |
| // get the current tag and write it out |
| writer.write("</" + beginTag + ">\n"); |
| } else { |
| ; |
| } |
| writer.flush(); |
| } |
| |
| private void endSection() throws java.io.IOException { |
| writeIndent(stack.size()); |
| |
| // get the current tag and write it out |
| String beginTag = getTag(); |
| writer.write("</" + beginTag + ">\n"); |
| |
| writer.flush(); |
| } |
| |
| private void beginTag(String tag) throws java.io.IOException { |
| // store the current tag |
| setTag(tag); |
| |
| writeIndent(stack.size()); |
| |
| // write tag out |
| writer.write("<" + tag + ">"); |
| } |
| |
| private void endTag(String tag) throws java.io.IOException { |
| String beginTag = getTag(); |
| if (tag.equals(beginTag)) { |
| // get the current tag and write it out |
| writer.write("</" + beginTag + ">\n"); |
| } else { |
| ; |
| } |
| writer.flush(); |
| } |
| |
| private void emptyTag(String tag) throws java.io.IOException { |
| // write an emptyTag |
| writer.write("<" + tag + "/>"); |
| } |
| |
| private void setTag(String tag) { |
| // add the tag to stack |
| stack.push(tag); |
| } |
| |
| private String getTag() { |
| return (String)stack.pop(); |
| } |
| |
| private void writeNull() throws java.io.IOException { |
| emptyTag("null"); |
| } |
| |
| private void writeStringData(String s) throws java.io.IOException { |
| if (s == null) { |
| writeNull(); |
| } else if (s.equals("")) { |
| writeEmptyString(); |
| } else { |
| |
| s = processSpecialCharacters(s); |
| |
| writer.write(s); |
| } |
| } |
| |
| private void writeString(String s) throws java.io.IOException { |
| if (s != null) { |
| writer.write(s); |
| } else { |
| writeNull(); |
| } |
| } |
| |
| |
| private void writeShort(short s) throws java.io.IOException { |
| writer.write(Short.toString(s)); |
| } |
| |
| private void writeLong(long l) throws java.io.IOException { |
| writer.write(Long.toString(l)); |
| } |
| |
| private void writeInteger(int i) throws java.io.IOException { |
| writer.write(Integer.toString(i)); |
| } |
| |
| private void writeBoolean(boolean b) throws java.io.IOException { |
| writer.write(new Boolean(b).toString()); |
| } |
| |
| private void writeFloat(float f) throws java.io.IOException { |
| writer.write(Float.toString(f)); |
| } |
| |
| private void writeDouble(double d) throws java.io.IOException { |
| writer.write(Double.toString(d)); |
| } |
| |
| private void writeBigDecimal(java.math.BigDecimal bd) throws java.io.IOException { |
| if (bd != null) |
| writer.write(bd.toString()); |
| else |
| emptyTag("null"); |
| } |
| |
| private void writeIndent(int tabs) throws java.io.IOException { |
| // indent... |
| for (int i = 1; i < tabs; i++) { |
| writer.write(" "); |
| } |
| } |
| |
| private void propString(String tag, String s) throws java.io.IOException { |
| beginTag(tag); |
| writeString(s); |
| endTag(tag); |
| } |
| |
| private void propInteger(String tag, int i) throws java.io.IOException { |
| beginTag(tag); |
| writeInteger(i); |
| endTag(tag); |
| } |
| |
| private void propBoolean(String tag, boolean b) throws java.io.IOException { |
| beginTag(tag); |
| writeBoolean(b); |
| endTag(tag); |
| } |
| |
| private void writeEmptyString() throws java.io.IOException { |
| emptyTag("emptyString"); |
| } |
| /** |
| * Purely for code coverage purposes.. |
| */ |
| public boolean writeData(RowSetInternal caller) { |
| return false; |
| } |
| |
| |
| /** |
| * This function has been added for the processing of special characters |
| * lik <,>,'," and & in the data to be serialized. These have to be taken |
| * of specifically or else there will be parsing error while trying to read |
| * the contents of the XML file. |
| **/ |
| |
| private String processSpecialCharacters(String s) { |
| |
| if(s == null) { |
| return null; |
| } |
| char []charStr = s.toCharArray(); |
| String specialStr = new String(); |
| |
| for(int i = 0; i < charStr.length; i++) { |
| if(charStr[i] == '&') { |
| specialStr = specialStr.concat("&"); |
| } else if(charStr[i] == '<') { |
| specialStr = specialStr.concat("<"); |
| } else if(charStr[i] == '>') { |
| specialStr = specialStr.concat(">"); |
| } else if(charStr[i] == '\'') { |
| specialStr = specialStr.concat("'"); |
| } else if(charStr[i] == '\"') { |
| specialStr = specialStr.concat("""); |
| } else { |
| specialStr = specialStr.concat(String.valueOf(charStr[i])); |
| } |
| } |
| |
| s = specialStr; |
| return s; |
| } |
| |
| } |