001: /*
002: * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.rowset.internal;
027:
028: import com.sun.rowset.JdbcRowSetResourceBundle;
029: import java.sql.*;
030: import javax.sql.*;
031: import java.io.*;
032: import java.text.MessageFormat;
033: import java.util.*;
034:
035: import javax.sql.rowset.*;
036: import javax.sql.rowset.spi.*;
037:
038: /**
039: * An implementation of the <code>XmlWriter</code> interface, which writes a
040: * <code>WebRowSet</code> object to an output stream as an XML document.
041: */
042:
043: public class WebRowSetXmlWriter implements XmlWriter, Serializable {
044:
045: /**
046: * The <code>java.io.Writer</code> object to which this <code>WebRowSetXmlWriter</code>
047: * object will write when its <code>writeXML</code> method is called. The value
048: * for this field is set with the <code>java.io.Writer</code> object given
049: * as the second argument to the <code>writeXML</code> method.
050: */
051: private java.io.Writer writer;
052:
053: /**
054: * The <code>java.util.Stack</code> object that this <code>WebRowSetXmlWriter</code>
055: * object will use for storing the tags to be used for writing the calling
056: * <code>WebRowSet</code> object as an XML document.
057: */
058: private java.util.Stack stack;
059:
060: private JdbcRowSetResourceBundle resBundle;
061:
062: public WebRowSetXmlWriter() {
063:
064: try {
065: resBundle = JdbcRowSetResourceBundle
066: .getJdbcRowSetResourceBundle();
067: } catch (IOException ioe) {
068: throw new RuntimeException(ioe);
069: }
070: }
071:
072: /**
073: * Writes the given <code>WebRowSet</code> object as an XML document
074: * using the given <code>java.io.Writer</code> object. The XML document
075: * will include the <code>WebRowSet</code> object's data, metadata, and
076: * properties. If a data value has been updated, that information is also
077: * included.
078: * <P>
079: * This method is called by the <code>XmlWriter</code> object that is
080: * referenced in the calling <code>WebRowSet</code> object's
081: * <code>xmlWriter</code> field. The <code>XmlWriter.writeXML</code>
082: * method passes to this method the arguments that were supplied to it.
083: *
084: * @param caller the <code>WebRowSet</code> object to be written; must
085: * be a rowset for which this <code>WebRowSetXmlWriter</code> object
086: * is the writer
087: * @param wrt the <code>java.io.Writer</code> object to which
088: * <code>caller</code> will be written
089: * @exception SQLException if a database access error occurs or
090: * this <code>WebRowSetXmlWriter</code> object is not the writer
091: * for the given rowset
092: * @see XmlWriter#writeXML
093: */
094: public void writeXML(WebRowSet caller, java.io.Writer wrt)
095: throws SQLException {
096:
097: // create a new stack for tag checking.
098: stack = new java.util.Stack();
099: writer = wrt;
100: writeRowSet(caller);
101: }
102:
103: /**
104: * Writes the given <code>WebRowSet</code> object as an XML document
105: * using the given <code>java.io.OutputStream</code> object. The XML document
106: * will include the <code>WebRowSet</code> object's data, metadata, and
107: * properties. If a data value has been updated, that information is also
108: * included.
109: * <P>
110: * Using stream is a faster way than using <code>java.io.Writer<code/>
111: *
112: * This method is called by the <code>XmlWriter</code> object that is
113: * referenced in the calling <code>WebRowSet</code> object's
114: * <code>xmlWriter</code> field. The <code>XmlWriter.writeXML</code>
115: * method passes to this method the arguments that were supplied to it.
116: *
117: * @param caller the <code>WebRowSet</code> object to be written; must
118: * be a rowset for which this <code>WebRowSetXmlWriter</code> object
119: * is the writer
120: * @param oStream the <code>java.io.OutputStream</code> object to which
121: * <code>caller</code> will be written
122: * @throws SQLException if a database access error occurs or
123: * this <code>WebRowSetXmlWriter</code> object is not the writer
124: * for the given rowset
125: * @see XmlWriter#writeXML
126: */
127: public void writeXML(WebRowSet caller, java.io.OutputStream oStream)
128: throws SQLException {
129:
130: // create a new stack for tag checking.
131: stack = new java.util.Stack();
132: writer = new OutputStreamWriter(oStream);
133: writeRowSet(caller);
134: }
135:
136: /**
137: *
138: *
139: * @exception SQLException if a database access error occurs
140: */
141: private void writeRowSet(WebRowSet caller) throws SQLException {
142:
143: try {
144:
145: startHeader();
146:
147: writeProperties(caller);
148: writeMetaData(caller);
149: writeData(caller);
150:
151: endHeader();
152:
153: } catch (java.io.IOException ex) {
154: throw new SQLException(MessageFormat.format(resBundle
155: .handleGetObject("wrsxmlwriter.ioex").toString(),
156: ex.getMessage()));
157: }
158: }
159:
160: private void startHeader() throws java.io.IOException {
161:
162: setTag("webRowSet");
163: writer.write("<?xml version=\"1.0\"?>\n");
164: writer
165: .write("<webRowSet xmlns=\"http://java.sun.com/xml/ns/jdbc\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
166: writer
167: .write("xsi:schemaLocation=\"http://java.sun.com/xml/ns/jdbc http://java.sun.com/xml/ns/jdbc/webrowset.xsd\">\n");
168: }
169:
170: private void endHeader() throws java.io.IOException {
171: endTag("webRowSet");
172: }
173:
174: /**
175: *
176: *
177: * @exception SQLException if a database access error occurs
178: */
179: private void writeProperties(WebRowSet caller)
180: throws java.io.IOException {
181:
182: beginSection("properties");
183:
184: try {
185: propString("command", processSpecialCharacters(caller
186: .getCommand()));
187: propInteger("concurrency", caller.getConcurrency());
188: propString("datasource", caller.getDataSourceName());
189: propBoolean("escape-processing", caller
190: .getEscapeProcessing());
191:
192: try {
193: propInteger("fetch-direction", caller
194: .getFetchDirection());
195: } catch (SQLException sqle) {
196: // it may be the case that fetch direction has not been set
197: // fetchDir == 0
198: // in that case it will throw a SQLException.
199: // To avoid that catch it here
200: }
201:
202: propInteger("fetch-size", caller.getFetchSize());
203: propInteger("isolation-level", caller
204: .getTransactionIsolation());
205:
206: beginSection("key-columns");
207:
208: int[] kc = caller.getKeyColumns();
209: for (int i = 0; kc != null && i < kc.length; i++)
210: propInteger("column", kc[i]);
211:
212: endSection("key-columns");
213:
214: //Changed to beginSection and endSection for maps for proper indentation
215: beginSection("map");
216: java.util.Map typeMap = caller.getTypeMap();
217: if (typeMap != null) {
218: Iterator i = typeMap.keySet().iterator();
219: Class c;
220: String type;
221: while (i.hasNext()) {
222: type = (String) i.next();
223: c = (Class) typeMap.get(type);
224: propString("type", type);
225: propString("class", c.getName());
226: }
227: }
228: endSection("map");
229:
230: propInteger("max-field-size", caller.getMaxFieldSize());
231: propInteger("max-rows", caller.getMaxRows());
232: propInteger("query-timeout", caller.getQueryTimeout());
233: propBoolean("read-only", caller.isReadOnly());
234:
235: int itype = caller.getType();
236: String strType = "";
237:
238: if (itype == 1003) {
239: strType = "ResultSet.TYPE_FORWARD_ONLY";
240: } else if (itype == 1004) {
241: strType = "ResultSet.TYPE_SCROLL_INSENSITIVE";
242: } else if (itype == 1005) {
243: strType = "ResultSet.TYPE_SCROLL_SENSITIVE";
244: }
245:
246: propString("rowset-type", strType);
247:
248: propBoolean("show-deleted", caller.getShowDeleted());
249: propString("table-name", caller.getTableName());
250: propString("url", caller.getUrl());
251:
252: beginSection("sync-provider");
253: // Remove the string after "@xxxx"
254: // before writing it to the xml file.
255: String strProviderInstance = (caller.getSyncProvider())
256: .toString();
257: String strProvider = strProviderInstance.substring(0,
258: (caller.getSyncProvider()).toString().indexOf("@"));
259:
260: propString("sync-provider-name", strProvider);
261: propString("sync-provider-vendor", "Sun Microsystems Inc.");
262: propString("sync-provider-version", "1.0");
263: propInteger("sync-provider-grade", caller.getSyncProvider()
264: .getProviderGrade());
265: propInteger("data-source-lock", caller.getSyncProvider()
266: .getDataSourceLock());
267:
268: endSection("sync-provider");
269:
270: } catch (SQLException ex) {
271: throw new java.io.IOException(MessageFormat.format(
272: resBundle.handleGetObject("wrsxmlwriter.sqlex")
273: .toString(), ex.getMessage()));
274: }
275:
276: endSection("properties");
277: }
278:
279: /**
280: *
281: *
282: * @exception SQLException if a database access error occurs
283: */
284: private void writeMetaData(WebRowSet caller)
285: throws java.io.IOException {
286: int columnCount;
287:
288: beginSection("metadata");
289:
290: try {
291:
292: ResultSetMetaData rsmd = caller.getMetaData();
293: columnCount = rsmd.getColumnCount();
294: propInteger("column-count", columnCount);
295:
296: for (int colIndex = 1; colIndex <= columnCount; colIndex++) {
297: beginSection("column-definition");
298:
299: propInteger("column-index", colIndex);
300: propBoolean("auto-increment", rsmd
301: .isAutoIncrement(colIndex));
302: propBoolean("case-sensitive", rsmd
303: .isCaseSensitive(colIndex));
304: propBoolean("currency", rsmd.isCurrency(colIndex));
305: propInteger("nullable", rsmd.isNullable(colIndex));
306: propBoolean("signed", rsmd.isSigned(colIndex));
307: propBoolean("searchable", rsmd.isSearchable(colIndex));
308: propInteger("column-display-size", rsmd
309: .getColumnDisplaySize(colIndex));
310: propString("column-label", rsmd
311: .getColumnLabel(colIndex));
312: propString("column-name", rsmd.getColumnName(colIndex));
313: propString("schema-name", rsmd.getSchemaName(colIndex));
314: propInteger("column-precision", rsmd
315: .getPrecision(colIndex));
316: propInteger("column-scale", rsmd.getScale(colIndex));
317: propString("table-name", rsmd.getTableName(colIndex));
318: propString("catalog-name", rsmd
319: .getCatalogName(colIndex));
320: propInteger("column-type", rsmd.getColumnType(colIndex));
321: propString("column-type-name", rsmd
322: .getColumnTypeName(colIndex));
323:
324: endSection("column-definition");
325: }
326: } catch (SQLException ex) {
327: throw new java.io.IOException(MessageFormat.format(
328: resBundle.handleGetObject("wrsxmlwriter.sqlex")
329: .toString(), ex.getMessage()));
330: }
331:
332: endSection("metadata");
333: }
334:
335: /**
336: *
337: *
338: * @exception SQLException if a database access error occurs
339: */
340: private void writeData(WebRowSet caller) throws java.io.IOException {
341: ResultSet rs;
342:
343: try {
344: ResultSetMetaData rsmd = caller.getMetaData();
345: int columnCount = rsmd.getColumnCount();
346: int i;
347:
348: beginSection("data");
349:
350: caller.beforeFirst();
351: caller.setShowDeleted(true);
352: while (caller.next()) {
353: if (caller.rowDeleted() && caller.rowInserted()) {
354: beginSection("modifyRow");
355: } else if (caller.rowDeleted()) {
356: beginSection("deleteRow");
357: } else if (caller.rowInserted()) {
358: beginSection("insertRow");
359: } else {
360: beginSection("currentRow");
361: }
362:
363: for (i = 1; i <= columnCount; i++) {
364: if (caller.columnUpdated(i)) {
365: rs = caller.getOriginalRow();
366: rs.next();
367: beginTag("columnValue");
368: writeValue(i, (RowSet) rs);
369: endTag("columnValue");
370: beginTag("updateRow");
371: writeValue(i, caller);
372: endTag("updateRow");
373: } else {
374: beginTag("columnValue");
375: writeValue(i, caller);
376: endTag("columnValue");
377: }
378: }
379:
380: endSection(); // this is unchecked
381: }
382: endSection("data");
383: } catch (SQLException ex) {
384: throw new java.io.IOException(MessageFormat.format(
385: resBundle.handleGetObject("wrsxmlwriter.sqlex")
386: .toString(), ex.getMessage()));
387: }
388: }
389:
390: private void writeValue(int idx, RowSet caller)
391: throws java.io.IOException {
392: try {
393: int type = caller.getMetaData().getColumnType(idx);
394:
395: switch (type) {
396: case java.sql.Types.BIT:
397: case java.sql.Types.BOOLEAN:
398: boolean b = caller.getBoolean(idx);
399: if (caller.wasNull())
400: writeNull();
401: else
402: writeBoolean(b);
403: break;
404: case java.sql.Types.TINYINT:
405: case java.sql.Types.SMALLINT:
406: short s = caller.getShort(idx);
407: if (caller.wasNull())
408: writeNull();
409: else
410: writeShort(s);
411: break;
412: case java.sql.Types.INTEGER:
413: int i = caller.getInt(idx);
414: if (caller.wasNull())
415: writeNull();
416: else
417: writeInteger(caller.getInt(idx));
418: break;
419: case java.sql.Types.BIGINT:
420: long l = caller.getLong(idx);
421: if (caller.wasNull())
422: writeNull();
423: else
424: writeLong(l);
425: break;
426: case java.sql.Types.REAL:
427: case java.sql.Types.FLOAT:
428: float f = caller.getFloat(idx);
429: if (caller.wasNull())
430: writeNull();
431: else
432: writeFloat(f);
433: break;
434: case java.sql.Types.DOUBLE:
435: double d = caller.getDouble(idx);
436: if (caller.wasNull())
437: writeNull();
438: else
439: writeDouble(d);
440: break;
441: case java.sql.Types.NUMERIC:
442: case java.sql.Types.DECIMAL:
443: writeBigDecimal(caller.getBigDecimal(idx));
444: break;
445: case java.sql.Types.BINARY:
446: case java.sql.Types.VARBINARY:
447: case java.sql.Types.LONGVARBINARY:
448: break;
449: case java.sql.Types.DATE:
450: java.sql.Date date = caller.getDate(idx);
451: if (caller.wasNull())
452: writeNull();
453: else
454: writeLong(date.getTime());
455: break;
456: case java.sql.Types.TIME:
457: java.sql.Time time = caller.getTime(idx);
458: if (caller.wasNull())
459: writeNull();
460: else
461: writeLong(time.getTime());
462: break;
463: case java.sql.Types.TIMESTAMP:
464: java.sql.Timestamp ts = caller.getTimestamp(idx);
465: if (caller.wasNull())
466: writeNull();
467: else
468: writeLong(ts.getTime());
469: break;
470: case java.sql.Types.CHAR:
471: case java.sql.Types.VARCHAR:
472: case java.sql.Types.LONGVARCHAR:
473: writeStringData(caller.getString(idx));
474: break;
475: default:
476: System.out.println(resBundle.handleGetObject(
477: "wsrxmlwriter.notproper").toString());
478: //Need to take care of BLOB, CLOB, Array, Ref here
479: }
480: } catch (SQLException ex) {
481: throw new java.io.IOException(resBundle.handleGetObject(
482: "wrsxmlwriter.failedwrite").toString()
483: + ex.getMessage());
484: }
485: }
486:
487: /*
488: * This begins a new tag with a indent
489: *
490: */
491: private void beginSection(String tag) throws java.io.IOException {
492: // store the current tag
493: setTag(tag);
494:
495: writeIndent(stack.size());
496:
497: // write it out
498: writer.write("<" + tag + ">\n");
499: }
500:
501: /*
502: * This closes a tag started by beginTag with a indent
503: *
504: */
505: private void endSection(String tag) throws java.io.IOException {
506: writeIndent(stack.size());
507:
508: String beginTag = getTag();
509:
510: if (beginTag.indexOf("webRowSet") != -1) {
511: beginTag = "webRowSet";
512: }
513:
514: if (tag.equals(beginTag)) {
515: // get the current tag and write it out
516: writer.write("</" + beginTag + ">\n");
517: } else {
518: ;
519: }
520: writer.flush();
521: }
522:
523: private void endSection() throws java.io.IOException {
524: writeIndent(stack.size());
525:
526: // get the current tag and write it out
527: String beginTag = getTag();
528: writer.write("</" + beginTag + ">\n");
529:
530: writer.flush();
531: }
532:
533: private void beginTag(String tag) throws java.io.IOException {
534: // store the current tag
535: setTag(tag);
536:
537: writeIndent(stack.size());
538:
539: // write tag out
540: writer.write("<" + tag + ">");
541: }
542:
543: private void endTag(String tag) throws java.io.IOException {
544: String beginTag = getTag();
545: if (tag.equals(beginTag)) {
546: // get the current tag and write it out
547: writer.write("</" + beginTag + ">\n");
548: } else {
549: ;
550: }
551: writer.flush();
552: }
553:
554: private void emptyTag(String tag) throws java.io.IOException {
555: // write an emptyTag
556: writer.write("<" + tag + "/>");
557: }
558:
559: private void setTag(String tag) {
560: // add the tag to stack
561: stack.push(tag);
562: }
563:
564: private String getTag() {
565: return (String) stack.pop();
566: }
567:
568: private void writeNull() throws java.io.IOException {
569: emptyTag("null");
570: }
571:
572: private void writeStringData(String s) throws java.io.IOException {
573: if (s == null) {
574: writeNull();
575: } else if (s.equals("")) {
576: writeEmptyString();
577: } else {
578:
579: s = processSpecialCharacters(s);
580:
581: writer.write(s);
582: }
583: }
584:
585: private void writeString(String s) throws java.io.IOException {
586: if (s != null) {
587: writer.write(s);
588: } else {
589: writeNull();
590: }
591: }
592:
593: private void writeShort(short s) throws java.io.IOException {
594: writer.write(Short.toString(s));
595: }
596:
597: private void writeLong(long l) throws java.io.IOException {
598: writer.write(Long.toString(l));
599: }
600:
601: private void writeInteger(int i) throws java.io.IOException {
602: writer.write(Integer.toString(i));
603: }
604:
605: private void writeBoolean(boolean b) throws java.io.IOException {
606: writer.write(new Boolean(b).toString());
607: }
608:
609: private void writeFloat(float f) throws java.io.IOException {
610: writer.write(Float.toString(f));
611: }
612:
613: private void writeDouble(double d) throws java.io.IOException {
614: writer.write(Double.toString(d));
615: }
616:
617: private void writeBigDecimal(java.math.BigDecimal bd)
618: throws java.io.IOException {
619: if (bd != null)
620: writer.write(bd.toString());
621: else
622: emptyTag("null");
623: }
624:
625: private void writeIndent(int tabs) throws java.io.IOException {
626: // indent...
627: for (int i = 1; i < tabs; i++) {
628: writer.write(" ");
629: }
630: }
631:
632: private void propString(String tag, String s)
633: throws java.io.IOException {
634: beginTag(tag);
635: writeString(s);
636: endTag(tag);
637: }
638:
639: private void propInteger(String tag, int i)
640: throws java.io.IOException {
641: beginTag(tag);
642: writeInteger(i);
643: endTag(tag);
644: }
645:
646: private void propBoolean(String tag, boolean b)
647: throws java.io.IOException {
648: beginTag(tag);
649: writeBoolean(b);
650: endTag(tag);
651: }
652:
653: private void writeEmptyString() throws java.io.IOException {
654: emptyTag("emptyString");
655: }
656:
657: /**
658: * Purely for code coverage purposes..
659: */
660: public boolean writeData(RowSetInternal caller) {
661: return false;
662: }
663:
664: /**
665: * This function has been added for the processing of special characters
666: * lik <,>,'," and & in the data to be serialized. These have to be taken
667: * of specifically or else there will be parsing error while trying to read
668: * the contents of the XML file.
669: **/
670:
671: private String processSpecialCharacters(String s) {
672:
673: if (s == null) {
674: return null;
675: }
676: char[] charStr = s.toCharArray();
677: String specialStr = new String();
678:
679: for (int i = 0; i < charStr.length; i++) {
680: if (charStr[i] == '&') {
681: specialStr = specialStr.concat("&");
682: } else if (charStr[i] == '<') {
683: specialStr = specialStr.concat("<");
684: } else if (charStr[i] == '>') {
685: specialStr = specialStr.concat(">");
686: } else if (charStr[i] == '\'') {
687: specialStr = specialStr.concat("'");
688: } else if (charStr[i] == '\"') {
689: specialStr = specialStr.concat(""");
690: } else {
691: specialStr = specialStr.concat(String
692: .valueOf(charStr[i]));
693: }
694: }
695:
696: s = specialStr;
697: return s;
698: }
699:
700: }
|