/* 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; } } }