csharp-sdk-upm/Storage/Source/Internal/Operation/AVRelationOperation.cs

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();
}
}
}