using LeanCloud.Storage.Internal;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace LeanCloud
{
///
/// A common base class for AVRelations.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class AVRelationBase : IJsonConvertible
{
private AVObject parent;
private string key;
private string targetClassName;
internal AVRelationBase(AVObject parent, string key)
{
EnsureParentAndKey(parent, key);
}
internal AVRelationBase(AVObject parent, string key, string targetClassName)
: this(parent, key)
{
this.targetClassName = targetClassName;
}
internal static IObjectSubclassingController SubclassingController
{
get
{
return AVPlugins.Instance.SubclassingController;
}
}
internal void EnsureParentAndKey(AVObject parent, string key)
{
this.parent = this.parent ?? parent;
this.key = this.key ?? key;
Debug.Assert(this.parent == parent, "Relation retrieved from two different objects");
Debug.Assert(this.key == key, "Relation retrieved from two different keys");
}
internal void Add(AVObject obj)
{
var change = new AVRelationOperation(new[] { obj }, null);
parent.PerformOperation(key, change);
targetClassName = change.TargetClassName;
}
internal void Remove(AVObject obj)
{
var change = new AVRelationOperation(null, new[] { obj });
parent.PerformOperation(key, change);
targetClassName = change.TargetClassName;
}
IDictionary IJsonConvertible.ToJSON()
{
return new Dictionary {
{ "__type", "Relation"},
{ "className", targetClassName}
};
}
internal AVQuery GetQuery() where T : AVObject
{
if (targetClassName != null)
{
return new AVQuery(targetClassName)
.WhereRelatedTo(parent, key);
}
return new AVQuery(parent.ClassName)
.RedirectClassName(key)
.WhereRelatedTo(parent, key);
}
internal AVQuery GetReverseQuery(T target) where T : AVObject
{
if (target.ObjectId == null)
{
throw new ArgumentNullException("target.ObjectId", "can not query a relation without target ObjectId.");
}
return new AVQuery(parent.ClassName).WhereEqualTo(key, target);
}
internal string TargetClassName
{
get
{
return targetClassName;
}
set
{
targetClassName = value;
}
}
///
/// Produces the proper AVRelation<T> instance for the given classname.
///
internal static AVRelationBase CreateRelation(AVObject parent,
string key,
string targetClassName)
{
var targetType = SubclassingController.GetType(targetClassName) ?? typeof(AVObject);
Expression>> createRelationExpr =
() => CreateRelation(parent, key, targetClassName);
var createRelationMethod =
((MethodCallExpression)createRelationExpr.Body)
.Method
.GetGenericMethodDefinition()
.MakeGenericMethod(targetType);
return (AVRelationBase)createRelationMethod.Invoke(null, new object[] { parent, key, targetClassName });
}
private static AVRelation CreateRelation(AVObject parent, string key, string targetClassName)
where T : AVObject
{
return new AVRelation(parent, key, targetClassName);
}
}
///
/// Provides access to all of the children of a many-to-many relationship. Each instance of
/// AVRelation is associated with a particular parent and key.
///
/// The type of the child objects.
public sealed class AVRelation : AVRelationBase where T : AVObject
{
internal AVRelation(AVObject parent, string key) : base(parent, key) { }
internal AVRelation(AVObject parent, string key, string targetClassName)
: base(parent, key, targetClassName) { }
///
/// Adds an object to this relation. The object must already have been saved.
///
/// The object to add.
public void Add(T obj)
{
base.Add(obj);
}
///
/// Removes an object from this relation. The object must already have been saved.
///
/// The object to remove.
public void Remove(T obj)
{
base.Remove(obj);
}
///
/// Gets a query that can be used to query the objects in this relation.
///
public AVQuery Query
{
get
{
return base.GetQuery();
}
}
}
}