// This file is part of the Ubik project
// Copyright (c) 2006 Nicholas Blumhardt <nicholas.blumhardt@gmail.com>
//
// This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
using System;
using System.Collections.Generic;
using System.Text;
namespace Ubik.Engine.Client{
/// <summary>
/// Member of an <see cref="Individual"/> subclass representing a reference to another Individual.
/// Null references are represented by a standard .NET null in the Value property.
/// Whether or not the value is nullable is the responsibility of the parent.
/// </summary>
public class TrackedReference<T> : TrackedProperty<T> where T : Individual
{
private Guid _identity = Guid.Empty;
private Guid _oldIdentity = Guid.Empty;
private bool _inTransaction = false;
/// <summary>
/// Create a TrackedReference belonging to <see cref="Individual"/> <paramref name="parent"/> with
/// the serialisation/query name <paramref name="name"/>.
/// </summary>
/// <param name="parent">Owner of the property.</param>
/// <param name="name">Serialisation/query name of the property, normally the same as the
/// parent's interface member that exposes the property.</param>
public TrackedReference(Individual parent, string name)
: base(parent, name)
{
}
/// <summary>
/// See <see cref="IUkmlSerialisable"/>.
/// </summary>
/// <param name="writer"></param>
protected override void WriteUkmlImpl(System.Xml.XmlWriter writer)
{
writer.WriteStartElement(((ITrackedMember)this).Name);
{
if (_value != null)
{
writer.WriteValue(_value.Identity.ToString());
}
else if (_identity != Guid.Empty)
{
writer.WriteValue(_identity.ToString());
}
else
{
WriteIsNullAttribute(writer, true);
}
}
writer.WriteEndElement();
}
/// <summary>
/// See <see cref="IUkmlSerialisable"/>.
/// </summary>
/// <param name="reader"></param>
/// <param name="session"></param>
protected override void ReadUkmlImpl(System.Xml.XmlReader reader, Session session)
{
if (reader.Name != ((ITrackedMember)this).Name)
throw new ArgumentException(TrackedPropertyResources.IncorrectName);
if (ReadIsNullAttribute(reader))
{
_identity = Guid.Empty;
}
else
{
_identity = new Guid(reader.ReadElementContentAsString());
}
_value = null;
}
/// <summary>
/// Set the internal value without triggering an update registration.
/// The internal transactional mechanism WILL NOT BE TRIGGERED so this method
/// absolutely cannot be called except during the parent's construction.
/// </summary>
/// <param name="value">The new value to store.</param>
public override void Reset(T value)
{
_identity = Guid.Empty;
base.Reset(value);
}
/// <summary>
/// The tracked reference value.
/// </summary>
public override T Value
{
get
{
if (_value == null && _identity != Guid.Empty)
{
_value = (T) Parent.Session.SelectOne(typeof(T), _identity);
}
return base.Value;
}
set // Attempt strong exception guarantee
{
bool wasInTransaction = _inTransaction;
if (!_inTransaction)
{
_inTransaction = true;
_oldIdentity = _identity;
}
_identity = Guid.Empty;
try
{
base.Value = value;
}
catch (Exception)
{
if (!wasInTransaction)
{
_inTransaction = false;
_identity = _oldIdentity;
_oldIdentity = Guid.Empty;
}
throw;
}
}
}
/// <summary>
/// See <see cref="ISimpleCommitAbort"/>.
/// </summary>
protected override void CommitImpl()
{
base.CommitImpl();
_inTransaction = false;
_oldIdentity = Guid.Empty;
}
/// <summary>
/// See <see cref="ISimpleCommitAbort"/>.
/// </summary>
protected override void AbortImpl()
{
if (_inTransaction)
{
_inTransaction = false;
_identity = _oldIdentity;
_oldIdentity = Guid.Empty;
}
base.AbortImpl();
}
/// <summary>
/// See <see cref="TrackedProperty<T>"/>.
/// </summary>
/// <param name="individual"></param>
/// <returns></returns>
protected override bool ReferencesImpl(Individual individual)
{
if (_value == null && _identity != Guid.Empty)
return individual.Identity == _identity;
else
return _value == individual;
}
}
}
|