예제 #1
0
        /// <summary>
        /// Remove is used by various operations (eg: remove, move, ...), yet through different operations;
        /// This method allows code reuse yet reporting the correct operation on error
        /// </summary>
        private RemovedPropertyTypeResult Remove(string path, T objectToApplyTo, Operation <T> operationToReport)
        {
            // get path result
            var pathResult = PropertyHelpers.GetActualPropertyPath(
                path,
                objectToApplyTo,
                operationToReport,
                false);

            var removeFromList       = pathResult.ExecuteAtEnd;
            var positionAsInteger    = pathResult.NumericEnd;
            var actualPathToProperty = pathResult.PathToProperty;
            var expressionToUse      = pathResult.ExpressionEnd;
            var result = new ObjectTreeAnalysisResult(objectToApplyTo, actualPathToProperty,
                                                      ContractResolver);

            if (!result.IsValidPathForRemove)
            {
                throw new JsonPatchException(
                          new JsonPatchError(
                              objectToApplyTo,
                              operationToReport,
                              string.Format("Patch failed: the provided path is invalid: {0}.", path)),
                          422);
            }

            var patchProperty = result.JsonPatchProperty;

            if (removeFromList || positionAsInteger > -1)
            {
                if (expressionToUse != null)
                {
                    // we have an expression.
                    var    elementType = PropertyHelpers.GetCollectionType(patchProperty.Property.PropertyType);
                    object coll        = patchProperty.Property.ValueProvider
                                         .GetValue(patchProperty.Parent);
                    var remMethod   = typeof(ICollection <>).MakeGenericType(elementType).GetMethod("Remove");
                    var objToRemove = ExpressionHelpers.GetElementAtFromObjectExpression(coll, expressionToUse);
                    var remResult   = remMethod.Invoke(coll, new object[] { objToRemove });
                    if ((bool)remResult != true)
                    {
                        throw new Exception("D'oh");
                    }
                    return(new RemovedPropertyTypeResult(elementType, false));
                }
                else if (PropertyHelpers.IsNonStringList(patchProperty.Property.PropertyType))
                {
                    // now, get the generic type of the enumerable
                    var genericTypeOfArray = PropertyHelpers.GetEnumerableType(patchProperty.Property.PropertyType);

                    if (patchProperty.Property.Readable)
                    {
                        var array = (IList)patchProperty.Property.ValueProvider
                                    .GetValue(patchProperty.Parent);

                        if (removeFromList)
                        {
                            if (array.Count == 0)
                            {
                                // if the array is empty, we should throw an error
                                throw new JsonPatchException(
                                          new JsonPatchError(
                                              objectToApplyTo,
                                              operationToReport,
                                              string.Format("Patch failed: provided path is invalid for array property type at location path: {0}: position larger than array size", path)),
                                          422);
                            }
                            SetValueEventArgs eArg = new SetValueEventArgs(operationToReport)
                            {
                                Cancel = false,
                                //NewValue = conversionResultTuple.ConvertedInstance,
                                ParentObject = patchProperty.Parent,
                                Property     = patchProperty.Property,
                                Index        = array.Count - 1,
                            };
                            InvokeWithBeforeSetValue(eArg, () => array.RemoveAt(eArg.Index.Value));

                            // return the type of the value that has been removed
                            return(new RemovedPropertyTypeResult(genericTypeOfArray, false));
                        }
                        else
                        {
                            if (positionAsInteger >= array.Count)
                            {
                                throw new JsonPatchException(
                                          new JsonPatchError(
                                              objectToApplyTo,
                                              operationToReport,
                                              string.Format("Patch failed: provided path is invalid for array property type at location path: {0}: position larger than array size", path)),
                                          422);
                            }
                            SetValueEventArgs eArg = new SetValueEventArgs(operationToReport)
                            {
                                Cancel = false,
                                //NewValue = conversionResultTuple.ConvertedInstance,
                                ParentObject = patchProperty.Parent,
                                Property     = patchProperty.Property,
                                Index        = positionAsInteger
                            };
                            InvokeWithBeforeSetValue(eArg, () => array.RemoveAt(eArg.Index.Value));

                            // return the type of the value that has been removed
                            return(new RemovedPropertyTypeResult(genericTypeOfArray, false));
                        }
                    }
                    else
                    {
                        throw new JsonPatchException(
                                  new JsonPatchError(
                                      objectToApplyTo,
                                      operationToReport,
                                      string.Format("Patch failed: cannot get property value at path {0}.  Possible cause: the property doesn't have an accessible getter.", path)),
                                  422);
                    }
                }
                else
                {
                    throw new JsonPatchException(
                              new JsonPatchError(
                                  objectToApplyTo,
                                  operationToReport,
                                  string.Format("Patch failed: provided path is invalid for array property type at location path: {0}: expected array.", path)),
                              422);
                }
            }
            else
            {
                if (!patchProperty.Property.Writable)
                {
                    throw new JsonPatchException(
                              new JsonPatchError(
                                  objectToApplyTo,
                                  operationToReport,
                                  string.Format("Patch failed: property at path location cannot be set: {0}.  Possible causes: the property may not have an accessible setter, or the property may be part of an anonymous object (and thus cannot be changed after initialization).", path)),
                              422);
                }

                // set value to null, or for non-nullable value types, to its default value.
                object value = null;

                if (patchProperty.Property.PropertyType.GetType().IsValueType &&
                    Nullable.GetUnderlyingType(patchProperty.Property.PropertyType) == null)
                {
                    value = Activator.CreateInstance(patchProperty.Property.PropertyType);
                }

                // check if it can be converted.
                var conversionResultTuple = PropertyHelpers.ConvertToActualType(
                    patchProperty.Property.PropertyType,
                    value);
                SetValueEventArgs eArg = new SetValueEventArgs(operationToReport)
                {
                    Cancel = false,
                    //NewValue = conversionResultTuple.ConvertedInstance,
                    ParentObject          = patchProperty.Parent,
                    Property              = patchProperty.Property,
                    IsRemoveStepOfReplace = operationToReport.OperationType == OperationType.Replace
                };

                if (!conversionResultTuple.CanBeConverted)
                {
                    // conversion failed, so use reflection (somewhat slower) to
                    // create a new default instance of the property type to set as value

                    eArg.NewValue = Activator.CreateInstance(patchProperty.Property.PropertyType);
                    //patchProperty.Property.ValueProvider.SetValue(patchProperty.Parent,
                    //    Activator.CreateInstance(patchProperty.Property.PropertyType));

                    //return new RemovedPropertyTypeResult(patchProperty.Property.PropertyType, false);
                }
                else
                {
                    eArg.NewValue = conversionResultTuple.ConvertedInstance;
                }
                InvokeWithBeforeSetValue(eArg, () => patchProperty.Property.ValueProvider.SetValue(eArg.ParentObject,
                                                                                                   eArg.NewValue));

                return(new RemovedPropertyTypeResult(patchProperty.Property.PropertyType, false));
            }
        }