コード例 #1
0
        /// <summary>
        /// Validates the path for the operation.
        /// </summary>
        protected static void ValidatePath(JsonPatchOperation operation)
        {
            // Path cannot be null, but it can be empty.
            if (operation.Path == null)
            {
                throw new VssPropertyValidationException("Path", PatchResources.PathCannotBeNull());
            }

            // If it is not empty and does not start with /, this is an error per RFC.
            if (!operation.Path.StartsWith(PathSeparator) && !string.IsNullOrEmpty(operation.Path))
            {
                throw new VssPropertyValidationException("Path", PatchResources.PathInvalidStartValue());
            }

            // Ending in / is not valid..
            if (operation.Path.EndsWith(PathSeparator))
            {
                throw new VssPropertyValidationException("Path", PatchResources.PathInvalidEndValue());
            }

            // Only add operations allow insert.
            if (operation.Operation != Operation.Add)
            {
                if (operation.Path.EndsWith(EndOfIndex))
                {
                    throw new VssPropertyValidationException("Path", PatchResources.InsertNotSupported(operation.Operation));
                }
            }
        }
コード例 #2
0
        public static PatchOperation <TModel> CreateFromJson(JsonPatchOperation operation)
        {
            if (operation != null)
            {
                switch (operation.Operation)
                {
                case Operation.Add:
                    return(AddPatchOperation <TModel> .CreateFromJson(operation));

                case Operation.Replace:
                    return(ReplacePatchOperation <TModel> .CreateFromJson(operation));

                case Operation.Test:
                    return(TestPatchOperation <TModel> .CreateFromJson(operation));

                case Operation.Remove:
                    return(RemovePatchOperation <TModel> .CreateFromJson(operation));

                default:
                    throw new PatchOperationFailedException(PatchResources.MoveCopyNotImplemented());
                }
            }

            throw new VssPropertyValidationException("Operation", PatchResources.InvalidOperation());
        }
コード例 #3
0
 /// <summary>
 /// Applies the Remove patch operation to the target
 /// </summary>
 /// <param name="target">The object to apply the operation to.</param>
 public override void Apply(TModel target)
 {
     this.Apply(
         target,
         (type, parent, current) =>
     {
         if (type.IsList())
         {
             var list = (IList)parent;
             int index;
             if (int.TryParse(current, out index) &&
                 list.Count > index)
             {
                 list.RemoveAt(index);
             }
             else
             {
                 // We can't remove outside the rangeof the list.
                 throw new PatchOperationFailedException(PatchResources.IndexOutOfRange(this.Path));
             }
         }
         else if (type.IsDictionary())
         {
             ((IDictionary)parent).Remove(current);
         }
         else
         {
             type.SetMemberValue(current, parent, this.Value);
         }
     });
 }
コード例 #4
0
        /// <summary>
        /// Validates and returns the type for the operation.
        /// </summary>
        /// <param name="operation"></param>
        /// <returns></returns>
        protected static Type ValidateAndGetType(JsonPatchOperation operation)
        {
            var type = GetType(typeof(TModel), operation.Path);

            if (type == null)
            {
                throw new VssPropertyValidationException("Path", PatchResources.UnableToEvaluatePath(operation.Path));
            }

            return(type);
        }
コード例 #5
0
        public static new PatchOperation <TModel> CreateFromJson(JsonPatchOperation operation)
        {
            ValidatePath(operation);
            ValidateType(operation);

            if (operation.Value != null)
            {
                throw new VssPropertyValidationException("Value", PatchResources.ValueNotNull());
            }

            return(new RemovePatchOperation <TModel>(operation.Path));
        }
コード例 #6
0
        public static new PatchOperation <TModel> CreateFromJson(JsonPatchOperation operation)
        {
            ValidatePath(operation);

            var value = ValidateAndGetValue(operation);

            if (value == null)
            {
                throw new VssPropertyValidationException("Value", PatchResources.ValueCannotBeNull());
            }

            return(new AddPatchOperation <TModel>(operation.Path, value));
        }
コード例 #7
0
 /// <summary>
 /// Applies the Add patch operation to the target
 /// </summary>
 /// <param name="target">The object to apply the operation to.</param>
 public override void Apply(TModel target)
 {
     this.Apply(
         target,
         (type, parent, current) =>
     {
         // Empty current means replace the whole object.
         if (string.IsNullOrEmpty(current))
         {
             parent = this.Value;
         }
         else if (type.IsList())
         {
             var list = (IList)parent;
             if (current == EndOfIndex)
             {
                 list.Add(this.Value);
             }
             else
             {
                 int index;
                 // When index == list.Count it's the same
                 // as doing an index append to the end.
                 if (int.TryParse(current, out index) &&
                     list.Count >= index)
                 {
                     list.Insert(index, this.Value);
                 }
                 else
                 {
                     // We can't insert beyond the length of the list.
                     throw new PatchOperationFailedException(PatchResources.IndexOutOfRange(this.Path));
                 }
             }
         }
         else if (type.IsDictionary())
         {
             ((IDictionary)parent)[current] = this.Value;
         }
         else
         {
             type.SetMemberValue(current, parent, this.Value);
         }
     });
 }
コード例 #8
0
        /// <summary>
        /// Applies the Replace patch operation to the target
        /// </summary>
        /// <param name="target">The object to apply the operation to.</param>
        public override void Apply(TModel target)
        {
            this.Apply(
                target,
                (type, parent, current) =>
            {
                if (type.IsList())
                {
                    var list = (IList)parent;
                    int index;
                    if (int.TryParse(current, out index) &&
                        list.Count > index)
                    {
                        list[index] = this.Value;
                    }
                    else
                    {
                        throw new PatchOperationFailedException(PatchResources.CannotReplaceNonExistantValue(this.Path));
                    }
                }
                else if (type.IsDictionary())
                {
                    var dictionary = (IDictionary)parent;
                    if (!dictionary.Contains(current))
                    {
                        throw new InvalidPatchFieldNameException(PatchResources.InvalidFieldName(current));
                    }

                    dictionary[current] = this.Value;
                }
                else
                {
                    if (type.GetMemberValue(current, parent) == null)
                    {
                        throw new PatchOperationFailedException(PatchResources.CannotReplaceNonExistantValue(this.Path));
                    }

                    type.SetMemberValue(current, parent, this.Value);
                }
            });
        }
コード例 #9
0
        /// <summary>
        /// Deserializes the json value.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="jsonValue">The json formatted value.</param>
        /// <returns>The strongly typed (best effort) value.</returns>
        private static object DeserializeValue(Type type, object jsonValue)
        {
            object value = null;

            if (jsonValue is JToken)
            {
                try
                {
                    value = ((JToken)jsonValue).ToObject(type, serializer);
                }
                catch (JsonException ex)
                {
                    throw new VssPropertyValidationException("Value", PatchResources.InvalidValue(jsonValue, type), ex);
                }
            }
            else
            {
                // Not a JToken, so it must be a primitive type.  Will
                // attempt to convert to the requested type.
                if (type.IsAssignableOrConvertibleFrom(jsonValue))
                {
                    value = ConvertUtility.ChangeType(jsonValue, type);
                }
                else
                {
                    Guid guidValue;
                    if (Guid.TryParse((string)jsonValue, out guidValue))
                    {
                        value = guidValue;
                    }
                    else
                    {
                        throw new VssPropertyValidationException("Value", PatchResources.InvalidValue(jsonValue, type));
                    }
                }
            }

            return(value);
        }
コード例 #10
0
        /// <summary>
        /// Applies the Test patch operation to the target
        /// </summary>
        /// <param name="target">The object to apply the operation to.</param>
        public override void Apply(TModel target)
        {
            this.Apply(
                target,
                (type, parent, current) =>
            {
                object memberValue = null;
                if (type.IsList())
                {
                    var list = (IList)parent;
                    int index;
                    if (int.TryParse(current, out index) &&
                        list.Count > index)
                    {
                        memberValue = list[index];
                    }
                    else
                    {
                        // We can't insert beyond the length of the list.
                        throw new PatchOperationFailedException(PatchResources.IndexOutOfRange(this.Path));
                    }
                }
                else if (type.IsDictionary())
                {
                    var fieldDictionary = ((IDictionary)parent);

                    if (!fieldDictionary.Contains(current))
                    {
                        throw new InvalidPatchFieldNameException(PatchResources.InvalidFieldName(current));
                    }
                    memberValue = fieldDictionary[current];
                }
                else
                {
                    memberValue = type.GetMemberValue(current, parent);
                }

                var success = false;
                if (memberValue != null)
                {
                    if (memberValue is IList)
                    {
                        // TODO: Implement
                        throw new PatchOperationFailedException(PatchResources.TestNotImplementedForList());
                    }
                    else if (memberValue is IDictionary)
                    {
                        // TODO: Implement
                        throw new PatchOperationFailedException(PatchResources.TestNotImplementedForDictionary());
                    }
                    else if (memberValue.GetType().IsAssignableOrConvertibleFrom(this.Value))
                    {
                        // We convert the objects since we need the values unboxed.
                        var convertedMemberValue = ConvertUtility.ChangeType(memberValue, memberValue.GetType());
                        var convertedValue       = ConvertUtility.ChangeType(this.Value, memberValue.GetType());

                        success = convertedMemberValue.Equals(convertedValue);
                    }
                    else
                    {
                        success = memberValue.Equals(this.Value);
                    }
                }
                else
                {
                    success = object.Equals(memberValue, this.Value);
                }

                if (!success)
                {
                    throw new TestPatchOperationFailedException(PatchResources.TestFailed(this.Path, memberValue, this.Value));
                }
            });
        }
コード例 #11
0
        /// <summary>
        /// Evaluates the path on the target and applies an action to the result.
        /// </summary>
        /// <param name="target">The target object to apply the operation to.</param>
        /// <param name="path">The path to evaluate.</param>
        /// <param name="actionToApply">The action to apply to the result of the evaluation.</param>
        private void Apply(object target, IEnumerable <string> path, Action <Type, object, string> actionToApply)
        {
            var current = path.First();
            var type    = target.GetType();

            // We're at the end, time to apply the action.
            if (path.Count() == 1)
            {
                if (PatchOperationApplying != null)
                {
                    PatchOperationApplying(this, new PatchOperationApplyingEventArgs(this.EvaluatedPath, this.Operation));
                }

                actionToApply(type, target, current);

                if (PatchOperationApplied != null)
                {
                    PatchOperationApplied(this, new PatchOperationAppliedEventArgs(this.EvaluatedPath, this.Operation));
                }
            }
            else
            {
                object newTarget = null;

                // The start of the path should always be an empty string after splitting.
                // We just assign target to new target and move down the path.
                if (string.IsNullOrEmpty(current))
                {
                    newTarget = target;
                }
                // If the next level is a dictionary, we want to get object at the key.
                else if (type.IsDictionary())
                {
                    var dictionary = ((IDictionary)target);
                    if (dictionary.Contains(current))
                    {
                        newTarget = dictionary[current];
                    }
                }
                else if (type.IsList())
                {
                    var list = (IList)target;
                    int index;
                    if (int.TryParse(current, out index) &&
                        list.Count > index)
                    {
                        newTarget = ((IList)target)[index];
                    }
                }
                else
                {
                    newTarget = type.GetMemberValue(current, target);
                }

                if (newTarget == null)
                {
                    // An extra layer of protection, since this should never happen because the earlier call to GetType would have failed.
                    throw new PatchOperationFailedException(PatchResources.TargetCannotBeNull());
                }

                this.Apply(newTarget, path.Skip(1), actionToApply);
            }
        }