/* Copyright 2010-2014 MongoDB Inc.
*
* 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Serializers;
namespace MongoDB.Bson
{
///
/// Represents a BSON array that is deserialized lazily.
///
[Serializable]
[BsonSerializer(typeof(LazyBsonArraySerializer))]
public class LazyBsonArray : MaterializedOnDemandBsonArray
{
// private fields
private IByteBuffer _slice;
private List _disposableItems = new List();
private BsonBinaryReaderSettings _readerSettings = BsonBinaryReaderSettings.Defaults;
// constructors
///
/// Initializes a new instance of the class.
///
/// The slice.
/// slice
/// LazyBsonArray cannot be used with an IByteBuffer that needs disposing.
public LazyBsonArray(IByteBuffer slice)
{
if (slice == null)
{
throw new ArgumentNullException("slice");
}
_slice = slice;
}
// public properties
///
/// Gets the slice.
///
///
/// The slice.
///
public IByteBuffer Slice
{
get { return _slice; }
}
///
/// Creates a shallow clone of the array (see also DeepClone).
///
/// A shallow clone of the array.
public override BsonValue Clone()
{
if (_slice != null)
{
return new LazyBsonArray(CloneSlice());
}
else
{
return base.Clone();
}
}
///
/// Creates a deep clone of the array (see also Clone).
///
/// A deep clone of the array.
public override BsonValue DeepClone()
{
if (_slice != null)
{
return new LazyBsonArray(CloneSlice());
}
else
{
return base.Clone();
}
}
// protected methods
///
/// Releases unmanaged and - optionally - managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
if (_slice != null)
{
_slice.Dispose();
_slice = null;
}
if (_disposableItems != null)
{
_disposableItems.ForEach(x => x.Dispose());
_disposableItems = null;
}
}
}
base.Dispose(disposing);
}
///
/// Materializes the BsonArray.
///
///
/// The materialized values.
///
protected override IEnumerable Materialize()
{
return MaterializeThisLevel();
}
///
/// Informs subclasses that the Materialize process completed so they can free any resources related to the unmaterialized state.
///
protected override void MaterializeCompleted()
{
var slice = _slice;
_slice = null;
slice.Dispose();
}
// private methods
private IByteBuffer CloneSlice()
{
return _slice.GetSlice(0, _slice.Length);
}
private LazyBsonArray DeserializeLazyBsonArray(BsonBinaryReader bsonReader)
{
var slice = bsonReader.ReadRawBsonArray();
var nestedArray = new LazyBsonArray(slice);
_disposableItems.Add(nestedArray);
return nestedArray;
}
private LazyBsonDocument DeserializeLazyBsonDocument(BsonBinaryReader bsonReader)
{
var slice = bsonReader.ReadRawBsonDocument();
var nestedDocument = new LazyBsonDocument(slice);
_disposableItems.Add(nestedDocument);
return nestedDocument;
}
private IEnumerable MaterializeThisLevel()
{
var values = new List();
var readerSettings = _readerSettings.Clone();
readerSettings.MaxDocumentSize = _slice.Length;
using (var bsonReader = new BsonBinaryReader(new BsonBuffer(CloneSlice(), true), true, readerSettings))
{
bsonReader.ReadStartDocument();
BsonType bsonType;
while ((bsonType = bsonReader.ReadBsonType()) != BsonType.EndOfDocument)
{
bsonReader.SkipName();
BsonValue value;
switch (bsonType)
{
case BsonType.Array: value = DeserializeLazyBsonArray(bsonReader); break;
case BsonType.Document: value = DeserializeLazyBsonDocument(bsonReader); break;
default: value = (BsonValue)BsonValueSerializer.Instance.Deserialize(bsonReader, typeof(BsonValue), null); break;
}
values.Add(value);
}
bsonReader.ReadEndDocument();
}
return values;
}
}
}