119 lines
4.5 KiB
C#
119 lines
4.5 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Collections.ObjectModel;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
namespace LeanCloud.Storage.Internal {
|
|||
|
public class AVRelationOperation : IAVFieldOperation {
|
|||
|
private readonly IList<string> adds;
|
|||
|
private readonly IList<string> removes;
|
|||
|
private readonly string targetClassName;
|
|||
|
|
|||
|
private AVRelationOperation(IEnumerable<string> adds,
|
|||
|
IEnumerable<string> removes,
|
|||
|
string targetClassName) {
|
|||
|
this.targetClassName = targetClassName;
|
|||
|
this.adds = new ReadOnlyCollection<string>(adds.ToList());
|
|||
|
this.removes = new ReadOnlyCollection<string>(removes.ToList());
|
|||
|
}
|
|||
|
|
|||
|
public AVRelationOperation(IEnumerable<AVObject> adds,
|
|||
|
IEnumerable<AVObject> removes) {
|
|||
|
adds = adds ?? new AVObject[0];
|
|||
|
removes = removes ?? new AVObject[0];
|
|||
|
this.targetClassName = adds.Concat(removes).Select(o => o.ClassName).FirstOrDefault();
|
|||
|
this.adds = new ReadOnlyCollection<string>(IdsFromObjects(adds).ToList());
|
|||
|
this.removes = new ReadOnlyCollection<string>(IdsFromObjects(removes).ToList());
|
|||
|
}
|
|||
|
|
|||
|
public object Encode() {
|
|||
|
var adds = this.adds
|
|||
|
.Select(id => PointerOrLocalIdEncoder.Instance.Encode(
|
|||
|
AVObject.CreateWithoutData(targetClassName, id)))
|
|||
|
.ToList();
|
|||
|
var removes = this.removes
|
|||
|
.Select(id => PointerOrLocalIdEncoder.Instance.Encode(
|
|||
|
AVObject.CreateWithoutData(targetClassName, id)))
|
|||
|
.ToList();
|
|||
|
var addDict = adds.Count == 0 ? null : new Dictionary<string, object> {
|
|||
|
{"__op", "AddRelation"},
|
|||
|
{"objects", adds}
|
|||
|
};
|
|||
|
var removeDict = removes.Count == 0 ? null : new Dictionary<string, object> {
|
|||
|
{"__op", "RemoveRelation"},
|
|||
|
{"objects", removes}
|
|||
|
};
|
|||
|
|
|||
|
if (addDict != null && removeDict != null) {
|
|||
|
return new Dictionary<string, object> {
|
|||
|
{"__op", "Batch"},
|
|||
|
{"ops", new[] {addDict, removeDict}}
|
|||
|
};
|
|||
|
}
|
|||
|
return addDict ?? removeDict;
|
|||
|
}
|
|||
|
|
|||
|
public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) {
|
|||
|
if (previous == null) {
|
|||
|
return this;
|
|||
|
}
|
|||
|
if (previous is AVDeleteOperation) {
|
|||
|
throw new InvalidOperationException("You can't modify a relation after deleting it.");
|
|||
|
}
|
|||
|
var other = previous as AVRelationOperation;
|
|||
|
if (other != null) {
|
|||
|
if (other.TargetClassName != TargetClassName) {
|
|||
|
throw new InvalidOperationException(
|
|||
|
string.Format("Related object must be of class {0}, but {1} was passed in.",
|
|||
|
other.TargetClassName,
|
|||
|
TargetClassName));
|
|||
|
}
|
|||
|
var newAdd = adds.Union(other.adds.Except(removes)).ToList();
|
|||
|
var newRemove = removes.Union(other.removes.Except(adds)).ToList();
|
|||
|
return new AVRelationOperation(newAdd, newRemove, TargetClassName);
|
|||
|
}
|
|||
|
throw new InvalidOperationException("Operation is invalid after previous operation.");
|
|||
|
}
|
|||
|
|
|||
|
public object Apply(object oldValue, string key) {
|
|||
|
if (adds.Count == 0 && removes.Count == 0) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
if (oldValue == null) {
|
|||
|
return AVRelationBase.CreateRelation(null, key, targetClassName);
|
|||
|
}
|
|||
|
if (oldValue is AVRelationBase) {
|
|||
|
var oldRelation = (AVRelationBase)oldValue;
|
|||
|
var oldClassName = oldRelation.TargetClassName;
|
|||
|
if (oldClassName != null && oldClassName != targetClassName) {
|
|||
|
throw new InvalidOperationException("Related object must be a " + oldClassName
|
|||
|
+ ", but a " + targetClassName + " was passed in.");
|
|||
|
}
|
|||
|
oldRelation.TargetClassName = targetClassName;
|
|||
|
return oldRelation;
|
|||
|
}
|
|||
|
throw new InvalidOperationException("Operation is invalid after previous operation.");
|
|||
|
}
|
|||
|
|
|||
|
public string TargetClassName { get { return targetClassName; } }
|
|||
|
|
|||
|
private IEnumerable<string> IdsFromObjects(IEnumerable<AVObject> objects) {
|
|||
|
foreach (var obj in objects) {
|
|||
|
if (obj.ObjectId == null) {
|
|||
|
throw new ArgumentException(
|
|||
|
"You can't add an unsaved AVObject to a relation.");
|
|||
|
}
|
|||
|
if (obj.ClassName != targetClassName) {
|
|||
|
throw new ArgumentException(string.Format(
|
|||
|
"Tried to create a AVRelation with 2 different types: {0} and {1}",
|
|||
|
targetClassName,
|
|||
|
obj.ClassName));
|
|||
|
}
|
|||
|
}
|
|||
|
return objects.Select(o => o.ObjectId).Distinct();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|