#region Apache Notice
/*****************************************************************************
* $Revision: 374175 $
* $LastChangedDate$
* $LastChangedBy$
*
* iBATIS.NET Data Mapper
* Copyright (C) 2008/2005 - The Apache Software Foundation
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
********************************************************************************/
#endregion
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using Apache.Ibatis.Common;
using Apache.Ibatis.DataMapper.Exceptions;
namespace Apache.Ibatis.DataMapper.Data
{
///
/// An implementation of that will copy the contents
/// of the an open to an in-memory if the
/// session doesn't allow multiple open with
/// the same .
///
public class InMemoryDataReader : IDataReader
{
private int _currentRowIndex = 0;
private int _currentResultIndex = 0;
private bool _isClosed = false;
private InMemoryResultSet[ ] _results = null;
///
/// Creates an InMemoryDataReader from a
///
/// The which holds the records from the Database.
public InMemoryDataReader(IDataReader reader)
{
ArrayList resultList = new ArrayList();
try
{
_currentResultIndex = 0;
_currentRowIndex = 0;
resultList.Add( new InMemoryResultSet( reader, true ) );
while( reader.NextResult() )
{
resultList.Add( new InMemoryResultSet( reader, false ) );
}
_results = ( InMemoryResultSet[ ] ) resultList.ToArray( typeof( InMemoryResultSet ) );
}
catch( Exception e )
{
throw new DataMapperException( "There was a problem converting an IDataReader to an InMemoryDataReader", e );
}
finally
{
reader.Close();
reader.Dispose();
}
}
#region IDataReader Members
///
/// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
///
public int RecordsAffected
{
get { throw new NotImplementedException( "InMemoryDataReader only used for select IList statements !" ); }
}
///
/// Gets a value indicating whether the data reader is closed.
///
public bool IsClosed
{
get { return _isClosed; }
}
///
/// Advances the data reader to the next result, when reading the results of batch SQL statements.
///
///
public bool NextResult()
{
_currentResultIndex++;
if( _currentResultIndex >= _results.Length )
{
_currentResultIndex--;
return false;
}
return true;
}
///
/// Closes the IDataReader 0bject.
///
public void Close()
{
_isClosed = true;
}
///
/// Advances the IDataReader to the next record.
///
/// true if there are more rows; otherwise, false.
public bool Read()
{
_currentRowIndex++;
if( _currentRowIndex >= _results[ _currentResultIndex ].RecordCount )
{
_currentRowIndex--;
return false;
}
return true;
}
///
/// Gets a value indicating the depth of nesting for the current row.
///
public int Depth
{
get { return _currentResultIndex; }
}
///
/// Returns a DataTable that describes the column metadata of the IDataReader.
///
///
public DataTable GetSchemaTable()
{
throw new NotImplementedException( "GetSchemaTable() is not implemented, cause not use." );
}
#endregion
#region IDisposable Members
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
_isClosed = true;
_results = null;
}
#endregion
#region IDataRecord Members
///
/// Gets the 32-bit signed integer value of the specified field.
///
/// The zero-based column ordinal.
/// The value of the column.
public int GetInt32(int fieldIndex)
{
return (int) GetValue(fieldIndex);
}
///
/// Gets the column with the specified name.
///
public object this[string name]
{
get { return this [GetOrdinal(name)]; }
}
///
/// Gets the column located at the specified index.
///
public object this[int fieldIndex]
{
get { return GetValue( fieldIndex ); }
}
///
/// Return the value of the specified field.
///
/// The index of the field to find.
/// The object which will contain the field value upon return.
public object GetValue(int fieldIndex)
{
return this.CurrentResultSet.GetValue( _currentRowIndex, fieldIndex );
}
///
/// Return whether the specified field is set to null.
///
/// The zero-based column ordinal.
/// The value of the column.
public bool IsDBNull(int fieldIndex)
{
return (GetValue(fieldIndex) == DBNull.Value);
}
///
/// Reads a stream of bytes from the specified column offset into the buffer as an array,
/// starting at the given buffer offset.
///
/// The zero-based column ordinal.
/// The index within the field from which to begin the read operation.
/// The buffer into which to read the stream of bytes.
/// The index for buffer to begin the read operation.
/// The number of bytes to read.
/// The actual number of bytes read.
public long GetBytes(int fieldIndex, long dataIndex, byte[] buffer, int bufferIndex, int length)
{
object value = GetValue(fieldIndex);
if (!(value is byte []))
{
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
if ( buffer == null )
{
// Return length of data
return ((byte []) value).Length;
}
else
{
// Copy data into buffer
int availableLength = (int) ( ( (byte []) value).Length - dataIndex);
if (availableLength < length)
{
length = availableLength;
}
Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
return length;
}
}
///
/// Gets the 8-bit unsigned integer value of the specified column.
///
/// The zero-based column ordinal.
/// The value of the column.
public byte GetByte(int fieldIndex)
{
return (byte) GetValue(fieldIndex);
}
///
/// Gets the Type information corresponding to the type of Object that would be returned from GetValue.
///
/// The zero-based column ordinal.
/// The value of the column.
public Type GetFieldType(int fieldIndex)
{
return this.CurrentResultSet.GetFieldType(fieldIndex);
}
///
/// Gets the fixed-position numeric value of the specified field.
///
/// The zero-based column ordinal.
/// The value of the column.
public decimal GetDecimal(int fieldIndex)
{
return (decimal) GetValue(fieldIndex);
}
///
/// Gets all the attribute fields in the collection for the current record.
///
///
///
public int GetValues(object[] values)
{
return this.CurrentResultSet.GetValues( _currentRowIndex, values );
}
///
/// Gets the name for the field to find.
///
/// The zero-based column ordinal.
/// The value of the column.
public string GetName(int fieldIndex)
{
return this.CurrentResultSet.GetName( fieldIndex );
}
///
/// Indicates the number of fields within the current record. This property is read-only.
///
public int FieldCount
{
get { return this.CurrentResultSet.FieldCount; }
}
///
///
///
/// The zero-based column ordinal.
/// The value of the column.
public long GetInt64(int fieldIndex)
{
return (long) GetValue(fieldIndex);
}
///
///
///
/// The zero-based column ordinal.
/// The value of the column.
public double GetDouble(int fieldIndex)
{
return (double) GetValue(fieldIndex);
}
///
/// Gets the value of the specified column as a Boolean.
///
/// The zero-based column ordinal.
/// The value of the column.
public bool GetBoolean(int fieldIndex)
{
return (bool) GetValue(fieldIndex);
}
///
/// Returns the GUID value of the specified field.
///
/// The zero-based column ordinal.
/// The value of the column.
public Guid GetGuid(int fieldIndex)
{
return (Guid) GetValue(fieldIndex);
}
///
/// Returns the value of the specified column as a DateTime object.
///
/// The zero-based column ordinal.
/// The value of the column.
public DateTime GetDateTime(int fieldIndex)
{
return (DateTime) GetValue(fieldIndex);
}
///
/// Returns the column ordinal, given the name of the column.
///
/// The name of the column.
/// The value of the column.
public int GetOrdinal(string colName)
{
return this.CurrentResultSet.GetOrdinal(colName);
}
///
/// Gets the database type information for the specified field.
///
/// The index of the field to find.
/// The database type information for the specified field.
public string GetDataTypeName(int fieldIndex)
{
return this.CurrentResultSet.GetDataTypeName(fieldIndex);
}
///
/// Returns the value of the specified column as a single-precision floating point number.
///
/// The zero-based column ordinal.
/// The value of the column.
public float GetFloat(int fieldIndex)
{
return (float) GetValue(fieldIndex);
}
///
/// Gets an IDataReader to be used when the field points to more remote structured data.
///
/// The zero-based column ordinal.
/// The value of the column.
public IDataReader GetData(int fieldIndex)
{
throw new NotImplementedException( "GetData(int) is not implemented, cause not use." );
}
///
/// Reads a stream of characters from the specified column offset into the buffer as an array,
/// starting at the given buffer offset.
///
/// The zero-based column ordinal.
/// The index within the row from which to begin the read operation.
/// The buffer into which to read the stream of bytes.
/// The index for buffer to begin the read operation.
/// The number of bytes to read.
/// The actual number of characters read.
public long GetChars(int fieldIndex, long dataIndex, char[] buffer, int bufferIndex, int length)
{
object value = GetValue(fieldIndex);
char [] valueBuffer = null;
if (value is char[])
{
valueBuffer = (char[])value;
}
else if (value is string)
{
valueBuffer = ((string)value).ToCharArray();
}
else
{
throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
}
if ( buffer == null )
{
// Return length of data
return valueBuffer.Length;
}
else
{
// Copy data into buffer
Array.Copy (valueBuffer, (int) dataIndex, buffer, bufferIndex, length);
return valueBuffer.Length - dataIndex;
}
}
///
/// Gets the string value of the specified field.
///
/// The zero-based column ordinal.
/// The value of the column.
public string GetString(int fieldIndex)
{
return (string) GetValue(fieldIndex);
}
///
/// Gets the character value of the specified column.
///
/// The zero-based column ordinal.
/// The value of the column.
public char GetChar(int fieldIndex)
{
return (char) GetValue(fieldIndex);
}
///
/// Gets the 16-bit signed integer value of the specified field.
///
/// The zero-based column ordinal.
/// The value of the column.
public short GetInt16(int fieldIndex)
{
return (short)GetValue (fieldIndex);
}
#endregion
///
/// Gets the current result set.
///
/// The current result set.
private InMemoryResultSet CurrentResultSet
{
get {return _results[ _currentResultIndex ];}
}
///
/// Represent an in-memory result set
///
private class InMemoryResultSet
{
// [row][column]
private readonly object[ ][ ] _records = null;
private int _fieldCount = 0;
private string[] _fieldsName = null;
private Type[] _fieldsType = null;
StringDictionary _fieldsNameLookup = new StringDictionary();
private string[] _dataTypeName = null;
///
/// Creates an in-memory ResultSet from a
///
///
/// true if the is already positioned on the record
/// to start reading from.
///
/// The which holds the records from the Database.
public InMemoryResultSet(IDataReader reader, bool isMidstream )
{
// [record index][ columns values=object[ ] ]
ArrayList recordsList = new ArrayList();
_fieldCount = reader.FieldCount;
_fieldsName = new string[_fieldCount];
_fieldsType = new Type[_fieldCount];
_dataTypeName = new string[_fieldCount];
bool firstRow = true;
// if we are in the middle of processing the reader then don't bother
// to move to the next record - just use the current one.
// Copy the records in memory
while( isMidstream || reader.Read())
{
if( firstRow )
{
for( int fieldIndex = 0; fieldIndex < reader.FieldCount; fieldIndex++ )
{
string fieldName = reader.GetName( fieldIndex );
_fieldsName[ fieldIndex ] = fieldName;
if (!_fieldsNameLookup.ContainsKey(fieldName))
{
_fieldsNameLookup.Add(fieldName, fieldIndex.ToString() );
}
_fieldsType[fieldIndex] = reader.GetFieldType( fieldIndex ) ;
_dataTypeName[ fieldIndex ] = reader.GetDataTypeName( fieldIndex );
}
}
firstRow = false;
object[ ] columnsValues = new object[_fieldCount];
reader.GetValues( columnsValues );
recordsList.Add( columnsValues );
isMidstream = false;
}
_records = ( object[ ][ ] ) recordsList.ToArray( typeof( object[ ] ) );
}
///
/// Gets the number of columns in the current row.
///
/// The number of columns in the current row.
public int FieldCount
{
get {return _fieldCount;}
}
///
/// Get a column value in a row
///
/// The row index
/// The column index
/// The column value
public object GetValue( int rowIndex, int colIndex )
{
return _records[ rowIndex ][ colIndex ];
}
///
/// The number of record contained in the ResultSet.
///
public int RecordCount
{
get { return _records.Length; }
}
///
/// Gets the type of the field.
///
/// Index of the col.
/// The type of the field.
public Type GetFieldType( int colIndex )
{
return _fieldsType[ colIndex ];
}
///
/// Gets the name of the field.
///
/// Index of the col.
/// The name of the field.
public string GetName( int colIndex )
{
return _fieldsName[ colIndex ];
}
///
/// Gets the ordinal.
///
/// Name of the column.
/// The ordinal of the column
public int GetOrdinal( string colName )
{
if( _fieldsNameLookup.ContainsKey(colName) )
{
return Convert.ToInt32(_fieldsNameLookup[ colName ]);
}
else
{
throw new IndexOutOfRangeException( String.Format( "No column with the specified name was found: {0}.", colName ) );
}
}
///
/// Gets the name of the database type.
///
/// Index of the col.
/// The name of the database type
public string GetDataTypeName( int colIndex )
{
return _dataTypeName[ colIndex ];
}
///
/// Gets the values.
///
/// Index of the row.
/// The values.
///
public int GetValues( int rowIndex, object[ ] values )
{
Array.Copy( _records[ rowIndex ], 0, values, 0, _fieldCount );
return _fieldCount;
}
}
}
}