/* 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 System.IO;
using System.Linq;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Serializers;
namespace MongoDB.Bson
{
///
/// Represents a BSON document that is not materialized until you start using it.
///
[Serializable]
public abstract class MaterializedOnDemandBsonDocument : BsonDocument, IDisposable
{
// private fields
private bool _disposed;
private bool _isMaterialized;
// constructors
///
/// Initializes a new instance of the class.
///
protected MaterializedOnDemandBsonDocument()
{
}
// public properties
///
/// Gets the number of elements.
///
public override int ElementCount
{
get
{
EnsureIsMaterialized();
return base.ElementCount;
}
}
///
/// Gets the elements.
///
public override IEnumerable Elements
{
get
{
EnsureIsMaterialized();
return base.Elements;
}
}
///
/// Gets a value indicating whether this instance is disposed.
///
///
/// true if this instance is disposed; otherwise, false .
///
public bool IsDisposed
{
get { return _disposed; }
}
///
/// Gets a value indicating whether this instance is materialized.
///
///
/// true if this instance is materialized; otherwise, false .
///
public bool IsMaterialized
{
get { return _isMaterialized; }
}
///
/// Gets the element names.
///
public override IEnumerable Names
{
get
{
EnsureIsMaterialized();
return base.Names;
}
}
///
/// Gets the raw values (see BsonValue.RawValue).
///
[Obsolete("Use Values instead.")]
public override IEnumerable RawValues
{
get
{
EnsureIsMaterialized();
return base.RawValues;
}
}
///
/// Gets the values.
///
public override IEnumerable Values
{
get
{
EnsureIsMaterialized();
return base.Values;
}
}
// public indexers
///
/// Gets or sets a value by position.
///
/// The position.
/// The value.
public override BsonValue this[int index]
{
get
{
EnsureIsMaterialized();
return base[index];
}
set
{
EnsureIsMaterialized();
base[index] = value;
}
}
///
/// Gets the value of an element or a default value if the element is not found.
///
/// The name of the element.
/// The default value to return if the element is not found.
/// Teh value of the element or a default value if the element is not found.
[Obsolete("Use GetValue(string name, BsonValue defaultValue) instead.")]
public override BsonValue this[string name, BsonValue defaultValue]
{
get
{
EnsureIsMaterialized();
return base[name, defaultValue];
}
}
///
/// Gets or sets a value by name.
///
/// The name.
/// The value.
public override BsonValue this[string name]
{
get
{
EnsureIsMaterialized();
return base[name];
}
set
{
EnsureIsMaterialized();
base[name] = value;
}
}
// public methods
///
/// Adds an element to the document.
///
/// The element to add.
///
/// The document (so method calls can be chained).
///
public override BsonDocument Add(BsonElement element)
{
EnsureIsMaterialized();
return base.Add(element);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange instead.")]
public override BsonDocument Add(Dictionary dictionary)
{
EnsureIsMaterialized();
return base.Add(dictionary);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
/// Which keys of the hash table to add.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange(IEnumerable elements) instead.")]
public override BsonDocument Add(Dictionary dictionary, IEnumerable keys)
{
EnsureIsMaterialized();
return base.Add(dictionary, keys);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange instead.")]
public override BsonDocument Add(IDictionary dictionary)
{
EnsureIsMaterialized();
return base.Add(dictionary);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
/// Which keys of the hash table to add.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange(IEnumerable elements) instead.")]
public override BsonDocument Add(IDictionary dictionary, IEnumerable keys)
{
EnsureIsMaterialized();
return base.Add(dictionary, keys);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange instead.")]
public override BsonDocument Add(IDictionary dictionary)
{
EnsureIsMaterialized();
return base.Add(dictionary);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
/// Which keys of the hash table to add.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange(IEnumerable elements) instead.")]
public override BsonDocument Add(IDictionary dictionary, IEnumerable keys)
{
EnsureIsMaterialized();
return base.Add(dictionary, keys);
}
///
/// Adds a list of elements to the document.
///
/// The list of elements.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange instead.")]
public override BsonDocument Add(IEnumerable elements)
{
EnsureIsMaterialized();
return base.Add(elements);
}
///
/// Adds a list of elements to the document.
///
/// The list of elements.
/// The document (so method calls can be chained).
[Obsolete("Use AddRange(IEnumerable elements) instead.")]
public override BsonDocument Add(params BsonElement[] elements)
{
EnsureIsMaterialized();
return base.Add(elements);
}
///
/// Creates and adds an element to the document.
///
/// The name of the element.
/// The value of the element.
///
/// The document (so method calls can be chained).
///
public override BsonDocument Add(string name, BsonValue value)
{
EnsureIsMaterialized();
return base.Add(name, value);
}
///
/// Creates and adds an element to the document, but only if the condition is true.
///
/// The name of the element.
/// The value of the element.
/// Whether to add the element to the document.
/// The document (so method calls can be chained).
public override BsonDocument Add(string name, BsonValue value, bool condition)
{
EnsureIsMaterialized();
return base.Add(name, value, condition);
}
///
/// Creates and adds an element to the document, but only if the condition is true.
/// If the condition is false the value factory is not called at all.
///
/// The name of the element.
/// A delegate called to compute the value of the element if condition is true.
/// Whether to add the element to the document.
/// The document (so method calls can be chained).
public override BsonDocument Add(string name, Func valueFactory, bool condition)
{
EnsureIsMaterialized();
return base.Add(name, valueFactory, condition);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
///
/// The document (so method calls can be chained).
///
public override BsonDocument AddRange(Dictionary dictionary)
{
EnsureIsMaterialized();
return base.AddRange(dictionary);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
///
/// The document (so method calls can be chained).
///
public override BsonDocument AddRange(IDictionary dictionary)
{
EnsureIsMaterialized();
return base.AddRange(dictionary);
}
///
/// Adds a list of elements to the document.
///
/// The list of elements.
///
/// The document (so method calls can be chained).
///
public override BsonDocument AddRange(IEnumerable elements)
{
EnsureIsMaterialized();
return base.AddRange(elements);
}
///
/// Adds elements to the document from a dictionary of key/value pairs.
///
/// The dictionary.
///
/// The document (so method calls can be chained).
///
public override BsonDocument AddRange(IEnumerable> dictionary)
{
EnsureIsMaterialized();
return base.AddRange(dictionary);
}
///
/// Clears the document (removes all elements).
///
public override void Clear()
{
EnsureIsMaterialized();
base.Clear();
}
///
/// Creates a shallow clone of the document (see also DeepClone).
///
///
/// A shallow clone of the document.
///
public override BsonValue Clone()
{
EnsureIsMaterialized();
return base.Clone();
}
///
/// Compares this document to another document.
///
/// The other document.
///
/// A 32-bit signed integer that indicates whether this document is less than, equal to, or greather than the other.
///
public override int CompareTo(BsonDocument other)
{
EnsureIsMaterialized();
return base.CompareTo(other);
}
///
/// Compares the BsonDocument to another BsonValue.
///
/// The other BsonValue.
/// A 32-bit signed integer that indicates whether this BsonDocument is less than, equal to, or greather than the other BsonValue.
public override int CompareTo(BsonValue other)
{
EnsureIsMaterialized();
return base.CompareTo(other);
}
///
/// Tests whether the document contains an element with the specified name.
///
/// The name of the element to look for.
///
/// True if the document contains an element with the specified name.
///
public override bool Contains(string name)
{
EnsureIsMaterialized();
return base.Contains(name);
}
///
/// Tests whether the document contains an element with the specified value.
///
/// The value of the element to look for.
///
/// True if the document contains an element with the specified value.
///
public override bool ContainsValue(BsonValue value)
{
EnsureIsMaterialized();
return base.ContainsValue(value);
}
///
/// Creates a deep clone of the document (see also Clone).
///
///
/// A deep clone of the document.
///
public override BsonValue DeepClone()
{
EnsureIsMaterialized();
return base.DeepClone();
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Determines whether the specified , is equal to this instance.
///
/// The to compare with this instance.
///
/// true if the specified is equal to this instance; otherwise, false .
///
public override bool Equals(object obj)
{
EnsureIsMaterialized();
return base.Equals(obj);
}
///
/// Gets the Id of the document.
///
/// The Id of the document (the RawValue if it has one, otherwise the element Value).
/// The nominal type of the Id.
/// The IdGenerator for the Id (or null).
/// True (a BsonDocument either has an Id member or one can be added).
[Obsolete("GetDocumentId was intended to be private and will become private in a future release. Use document[\"_id\"] or document.GetValue(\"_id\") instead.")]
public override bool GetDocumentId(out object id, out Type idNominalType, out IIdGenerator idGenerator)
{
EnsureIsMaterialized();
return base.GetDocumentId(out id, out idNominalType, out idGenerator);
}
///
/// Gets an element of this document.
///
/// The zero based index of the element.
///
/// The element.
///
public override BsonElement GetElement(int index)
{
EnsureIsMaterialized();
return base.GetElement(index);
}
///
/// Gets an element of this document.
///
/// The name of the element.
///
/// A BsonElement.
///
public override BsonElement GetElement(string name)
{
EnsureIsMaterialized();
return base.GetElement(name);
}
///
/// Gets an enumerator that can be used to enumerate the elements of this document.
///
///
/// An enumerator.
///
public override IEnumerator GetEnumerator()
{
EnsureIsMaterialized();
return base.GetEnumerator();
}
///
/// Returns a hash code for this instance.
///
///
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
///
public override int GetHashCode()
{
EnsureIsMaterialized();
return base.GetHashCode();
}
///
/// Gets the value of an element.
///
/// The zero based index of the element.
///
/// The value of the element.
///
public override BsonValue GetValue(int index)
{
EnsureIsMaterialized();
return base.GetValue(index);
}
///
/// Gets the value of an element.
///
/// The name of the element.
///
/// The value of the element.
///
public override BsonValue GetValue(string name)
{
EnsureIsMaterialized();
return base.GetValue(name);
}
///
/// Gets the value of an element or a default value if the element is not found.
///
/// The name of the element.
/// The default value returned if the element is not found.
///
/// The value of the element or the default value if the element is not found.
///
public override BsonValue GetValue(string name, BsonValue defaultValue)
{
EnsureIsMaterialized();
return base.GetValue(name, defaultValue);
}
///
/// Inserts a new element at a specified position.
///
/// The position of the new element.
/// The element.
public override void InsertAt(int index, BsonElement element)
{
EnsureIsMaterialized();
base.InsertAt(index, element);
}
///
/// Merges another document into this one. Existing elements are not overwritten.
///
/// The other document.
///
/// The document (so method calls can be chained).
///
public override BsonDocument Merge(BsonDocument document)
{
EnsureIsMaterialized();
return base.Merge(document);
}
///
/// Merges another document into this one, specifying whether existing elements are overwritten.
///
/// The other document.
/// Whether to overwrite existing elements.
///
/// The document (so method calls can be chained).
///
public override BsonDocument Merge(BsonDocument document, bool overwriteExistingElements)
{
EnsureIsMaterialized();
return base.Merge(document, overwriteExistingElements);
}
///
/// Removes an element from this document (if duplicate element names are allowed
/// then all elements with this name will be removed).
///
/// The name of the element to remove.
public override void Remove(string name)
{
EnsureIsMaterialized();
base.Remove(name);
}
///
/// Removes an element from this document.
///
/// The zero based index of the element to remove.
public override void RemoveAt(int index)
{
EnsureIsMaterialized();
base.RemoveAt(index);
}
///
/// Removes an element from this document.
///
/// The element to remove.
public override void RemoveElement(BsonElement element)
{
EnsureIsMaterialized();
base.RemoveElement(element);
}
///
/// Sets the value of an element.
///
/// The zero based index of the element whose value is to be set.
/// The new value.
///
/// The document (so method calls can be chained).
///
public override BsonDocument Set(int index, BsonValue value)
{
EnsureIsMaterialized();
return base.Set(index, value);
}
///
/// Sets the value of an element (an element will be added if no element with this name is found).
///
/// The name of the element whose value is to be set.
/// The new value.
///
/// The document (so method calls can be chained).
///
public override BsonDocument Set(string name, BsonValue value)
{
EnsureIsMaterialized();
return base.Set(name, value);
}
///
/// Sets the document Id.
///
/// The value of the Id.
[Obsolete("SetDocumentId was intended to be private and will become private in a future release. Use document[\"_id\"] = value or document.Set(\"_id\", value) instead.")]
public override void SetDocumentId(object id)
{
EnsureIsMaterialized();
base.SetDocumentId(id);
}
///
/// Sets an element of the document (replaces any existing element with the same name or adds a new element if an element with the same name is not found).
///
/// The new element.
///
/// The document.
///
public override BsonDocument SetElement(BsonElement element)
{
EnsureIsMaterialized();
return base.SetElement(element);
}
///
/// Sets an element of the document (replacing the existing element at that position).
///
/// The zero based index of the element to replace.
/// The new element.
///
/// The document.
///
public override BsonDocument SetElement(int index, BsonElement element)
{
EnsureIsMaterialized();
return base.SetElement(index, element);
}
///
/// Tries to get an element of this document.
///
/// The name of the element.
/// The element.
///
/// True if an element with that name was found.
///
public override bool TryGetElement(string name, out BsonElement value)
{
EnsureIsMaterialized();
return base.TryGetElement(name, out value);
}
///
/// Tries to get the value of an element of this document.
///
/// The name of the element.
/// The value of the element.
///
/// True if an element with that name was found.
///
public override bool TryGetValue(string name, out BsonValue value)
{
EnsureIsMaterialized();
return base.TryGetValue(name, out value);
}
// protected methods
///
/// Releases unmanaged and - optionally - managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
{
_disposed = true;
}
///
/// Materializes the BsonDocument.
///
/// The materialized elements.
protected abstract IEnumerable Materialize();
///
/// Informs subclasses that the Materialize process completed so they can free any resources related to the unmaterialized state.
///
protected abstract void MaterializeCompleted();
///
/// Throws if disposed.
///
///
protected void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
// private methods
private void EnsureIsMaterialized()
{
ThrowIfDisposed();
if (!_isMaterialized)
{
var elements = Materialize();
try
{
_isMaterialized = true;
base.AddRange(elements);
MaterializeCompleted();
}
catch
{
base.Clear();
_isMaterialized = false;
throw;
}
}
}
}
}