/* Copyright (c) 2005 Ricebridge. All Rights Reserved. * * This file is available under the terms and conditions of the * Ricebridge "Open Source API" policy; Ricebridge grants use of this * copyrighted work under the terms of a BSD-style license only. See * http://www.opensource.org/licenses/bsd-license.php for more * information. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of the Ricebridge nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.ricebridge.xmlman; import org.jostraca.util.Internal; import org.jostraca.util.Standard; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.*; /** An implementation of {@link RecordProvider} that provides data records for saving * from a {@link java.sql.ResultSet} object. * <p>This class is used to implement the * {@link XmlManager#saveResultSet(File,RecordSpec,ResultSet) saveResultSet(*,RecordSpec,ResultSet)} * methods in {@link XmlManager}. * It simply reads the next data record from the specified <code>ResultSet</code>, * maintaining an internal index of the current record.</p> * <p><code>ResultSetRecordProvider</code> is designed to be subclassed. You can change the default implementation by calling the * {@link com.ricebridge.xmlman.in.XmlManagerStoreBase#setResultSetRecordProviderClass setResultSetRecordProviderClass} method of * {@link com.ricebridge.xmlman.in.XmlManagerStore}, and providing a subclass of <code>ResultSetRecordProvider</code>. * <code>XmlManagerStore</code> can be accessed using {@link XmlManager#getXmlManagerStore}.</p> * <p>The <b><a href="ResultSetRecordProvider.java.html">Source Code</a></b> of this Java class * is available under a <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style license</a>.</p> */ public class ResultSetRecordProvider extends RecordProviderSupport { // public /** Output headers as first data record * (name for {@link XmlSpec#setProperty XmlSpec.setProperty}: <code>ResultSet.saveHeaders</code>). */ public static final String PROP_ResultSet_saveHeaders = "ResultSet.saveHeaders"; /** Reset {@link ResultSet} after saving * (name for {@link XmlSpec#setProperty XmlSpec.setProperty}: <code>ResultSet.resetBeforeFirst</code>). */ public static final String PROP_ResultSet_resetBeforeFirst = "ResultSet.resetBeforeFirst"; // protected instance /** {@link ResultSet} containing source data. */ protected ResultSet iResultSet = null; /** Save the column names as first data record. See {@link #setXmlSpecImpl setXmlSpecImpl}. */ protected boolean iSaveHeaders = false; /** Column names. */ protected String[] iHeaders = null; /** Indicate that internal state is at the first record. */ protected boolean iFirstRecord = true; /** Number of data fields in record, determined by {@link ResultSetMetaData}. */ protected int iFieldCount = 0; /** Type of {@link ResultSet}, determines if it can be reset. */ protected int iResultSetType = ResultSet.TYPE_FORWARD_ONLY; /** Reset {@link ResultSet} to beginning after saving data. */ protected boolean iResetBeforeFirst = false; // constructors /** Create uninitialised for use with {@link #setResultSet}. */ public ResultSetRecordProvider() { // init with setResultSet } /** Create initialised. See {@link #setResultSet setResultSet} for details. * @param pResultSet <code>ResultSet</code> providing data */ public ResultSetRecordProvider( ResultSet pResultSet ) { setResultSet( pResultSet ); } // public methods /** Set the <code>ResultSet</code> providing data to save. * <p>The default number of fields per record is taken from * {@link java.sql.ResultSetMetaData#getColumnCount()} * and the headers (output as the first record when the property <code>ResultSet.saveHeaders</code> is true), * are taken from {@link java.sql.ResultSetMetaData#getColumnName(int)}.</p> * @param pResultSet <code>ResultSet</code> providing data */ public void setResultSet( ResultSet pResultSet ) { iResultSet = (ResultSet) Internal.null_arg( pResultSet ); ResultSetMetaData rsmd = null; try { rsmd = iResultSet.getMetaData(); } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_resultset_method, new String[] { "resultset", String.valueOf(iResultSet), "params", "", "method", "getMetaData" }, e ); } try { iFieldCount = rsmd.getColumnCount(); } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_rsmd_method, new String[] { "resultset", String.valueOf(iResultSet), "resultsetmetadata", String.valueOf(rsmd), "params", "", "method", "getColumnCount"}, e ); } try { iResultSetType = iResultSet.getType(); } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_resultset_method, new String[] { "resultset", String.valueOf(iResultSet), "params", "", "method", "getType"}, e ); } if( iSaveHeaders && -1 < iFieldCount) { iHeaders = new String[ iFieldCount + 1 ]; int cI = 1; try { for( ; cI <= iFieldCount; cI++ ) { iHeaders[cI] = rsmd.getColumnName( cI ); } } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_rsmd_method, new String[] { "resultset", String.valueOf(iResultSet), "method", "getColumnName", "params", ""+cI, "resultsetmetadata", String.valueOf(rsmd)}, e ); } } else { iSaveHeaders = false; } } // RecordProviderSupport methods /** Handle property settings for saving {@link ResultSet ResultSets}. * <p>You can change these using {@link XmlSpec}.</p> * @see RecordListener#setXmlSpec * @see #PROP_ResultSet_saveHeaders * @see #PROP_ResultSet_resetBeforeFirst */ protected void setXmlSpecImpl( XmlSpec pXmlSpec ) { iSaveHeaders = pXmlSpec.getBooleanProperty( PROP_ResultSet_saveHeaders ); iResetBeforeFirst = pXmlSpec.getBooleanProperty( PROP_ResultSet_resetBeforeFirst ); } /** Start the saving process. */ protected void startProcessImpl() { iFirstRecord = true; } /** Check if there are any more records. */ protected boolean hasNextRecordImpl() { if( iSaveHeaders && iFirstRecord ) { return true; } else { try { return iResultSet.next(); } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_resultset_method, new String[] { "resultset", String.valueOf(iResultSet), "params", "", "method", "next" }, e ); } } } /** Return next data record as a <code>String[]</code> array. */ protected String[] nextRecordImpl() { String[] record = new String[iFieldCount]; if( iSaveHeaders && iFirstRecord ) { for( int fI = 0; fI < iFieldCount; fI++ ) { record[fI] = iHeaders[ fI ]; } } else { int fI = 0; try { for( ; fI < iFieldCount; fI++ ) { record[fI] = String.valueOf( iResultSet.getObject( fI+1 ) ); } } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_resultset_method, new String[] { "resultset", String.valueOf(iResultSet), "method", "getObject", "params", String.valueOf(fI+1) }, e ); } } return record; } /** Reset {@link ResultSet} by calling the {@link ResultSet#beforeFirst beforeFirst} method * if {@link #PROP_ResultSet_resetBeforeFirst} option is <code>true</code> and <code>ResultSet</code> type supports it. */ protected void endProcessImpl() { if( iResetBeforeFirst && ResultSet.TYPE_FORWARD_ONLY != iResultSetType ) { try { iResultSet.beforeFirst(); } catch( Exception e ) { throw new XmlManagerException( XmlManagerException.CODE_resultset_reset, new String[] { "resultset", String.valueOf(iResultSet) }, e ); } } } }