Exemplo n.º 1
0
        /// <summary>
        ///  The "copy" operation copies the value at a specified location to the
        ///  target location.
        ///
        ///  The operation object MUST contain a "from" member, which is a string
        ///  containing a JSON Pointer value that references the location in the
        ///  target document to copy the value from.
        ///
        ///  The "from" location MUST exist for the operation to be successful.
        ///
        ///  For example:
        ///
        ///  { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" }
        ///
        ///  This operation is functionally identical to an "add" operation at the
        ///  target location using the value specified in the "from" member.
        ///
        ///  Note: even though it's the same functionally, we do not call add with
        ///  the value specified in from for performance reasons (multiple checks of same requirements).
        /// </summary>
        /// <param name="operation">The copy operation</param>
        /// <param name="objectApplyTo">Object to apply the operation to</param>
        public void Copy(Operation <T> operation, T objectToApplyTo)
        {
            object valueAtFromLocation = null;

            // get path result
            var pathResult = PropertyHelpers.GetActualPropertyPath(
                operation.from,
                objectToApplyTo,
                operation,
                true);

            var positionAsInteger        = pathResult.NumericEnd;
            var actualPathToFromProperty = pathResult.PathToProperty;

            PropertyInfo fromProperty = PropertyHelpers
                                        .FindProperty(objectToApplyTo, actualPathToFromProperty);

            // does property at from exist?
            if (fromProperty == null)
            {
                throw new JsonPatchException(
                          new JsonPatchError(
                              objectToApplyTo,
                              operation,
                              string.Format("Patch failed: property at location from: {0} does not exist", operation.from))
                          , 422);
            }

            // get the property path

            // is the path an array (but not a string (= char[]))?  In this case,
            // the path must end with "/position" or "/-", which we already determined before.

            if (positionAsInteger > -1)
            {
                if (fromProperty.PropertyType.IsNonStringList())
                {
                    // now, get the generic type of the enumerable
                    var genericTypeOfArray = PropertyHelpers.GetEnumerableType(fromProperty.PropertyType);

                    if (!fromProperty.CanRead)
                    {
                        // cannot get the property
                        throw new JsonPatchException(
                                  new JsonPatchError(
                                      objectToApplyTo,
                                      operation,
                                      string.Format("Patch failed: cannot get property value at location from: {0}.  Possible cause: the property doesn't have an accessible getter.", operation.from)),
                                  422);
                    }

                    // get value (it can be cast, we just checked that)
                    var array = PropertyHelpers.GetValue(fromProperty, objectToApplyTo, actualPathToFromProperty) as IList;

                    if (array.Count <= positionAsInteger)
                    {
                        throw new JsonPatchException(
                                  new JsonPatchError(
                                      objectToApplyTo,
                                      operation,
                                      string.Format("Patch failed: provided from path is invalid for array property type at location from: {0}: invalid position",
                                                    operation.from))
                                  , 422);
                    }

                    valueAtFromLocation = array[positionAsInteger];
                }
                else
                {
                    throw new JsonPatchException(
                              new JsonPatchError(
                                  objectToApplyTo,
                                  operation,
                                  string.Format("Patch failed: provided from path is invalid for array property type at location from: {0}: expected array",
                                                operation.from))
                              , 422);
                }
            }
            else
            {
                if (!fromProperty.CanRead)
                {
                    // cannot get the property
                    throw new JsonPatchException(
                              new JsonPatchError(
                                  objectToApplyTo,
                                  operation,
                                  string.Format("Patch failed: cannot get property value at location from: {0}.  Possible cause: the property doesn't have an accessible getter.", operation.from)),
                              422);
                }

                // no list, just get the value
                valueAtFromLocation = PropertyHelpers.GetValue(fromProperty, objectToApplyTo, actualPathToFromProperty);
            }

            // add operation to target location with that value.
            Add(operation.path, valueAtFromLocation, objectToApplyTo, operation);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Add is used by various operations (eg: add, copy, ...), yet through different operations;
        /// This method allows code reuse yet reporting the correct operation on error
        /// </summary>
        private void Add(string path, object value, T objectToApplyTo, Operation <T> operationToReport)
        {
            // add, in this implementation, does not just "add" properties - that's
            // technically impossible;  It can however be used to add items to arrays,
            // or to replace values.

            // first up: if the path ends in a numeric value, we're inserting in a list and
            // that value represents the position; if the path ends in "-", we're appending
            // to the list.

            // get path result
            var pathResult = PropertyHelpers.GetActualPropertyPath(
                path,
                objectToApplyTo,
                operationToReport,
                true);

            var appendList           = pathResult.ExecuteAtEnd;
            var positionAsInteger    = pathResult.NumericEnd;
            var actualPathToProperty = pathResult.PathToProperty;

            var pathProperty = PropertyHelpers
                               .FindProperty(objectToApplyTo, actualPathToProperty);

            // does property at path exist?
            if (pathProperty == null)
            {
                throw new JsonPatchException(
                          new JsonPatchError(
                              objectToApplyTo,
                              operationToReport,
                              string.Format("Patch failed: property at location path: {0} does not exist", path))
                          , 422);
            }

            // it exists.  If it' an array, add to that array.  If it's not, we replace.

            // is the path an array (but not a string (= char[]))?  In this case,
            // the path must end with "/position" or "/-", which we already determined before.

            if (appendList || positionAsInteger > -1)
            {
                // what if it's an array but there's no position??
                if (pathProperty.PropertyType.IsNonStringList())
                {
                    // now, get the generic type of the enumerable
                    var genericTypeOfArray = PropertyHelpers.GetEnumerableType(pathProperty.PropertyType);

                    var conversionResult = PropertyHelpers.ConvertToActualType(genericTypeOfArray, value);

                    if (!conversionResult.CanBeConverted)
                    {
                        throw new JsonPatchException(
                                  new JsonPatchError(
                                      objectToApplyTo,
                                      operationToReport,
                                      string.Format("Patch failed: provided value is invalid for array property type at location path: {0}", path))
                                  , 422);
                    }

                    if (!pathProperty.CanRead)
                    {
                        // cannot get the property
                        throw new JsonPatchException(
                                  new JsonPatchError(
                                      objectToApplyTo,
                                      operationToReport,
                                      string.Format("Patch failed: cannot get property value at location from: {0}.  Possible cause: the property doesn't have an accessible getter.", path)),
                                  422);
                    }

                    // get value (it can be cast, we just checked that)
                    var array = PropertyHelpers.GetValue(pathProperty, objectToApplyTo, actualPathToProperty) as IList;

                    if (appendList)
                    {
                        array.Add(conversionResult.ConvertedInstance);
                    }
                    else
                    {
                        // specified index must not be greater than the amount of items in the
                        // array
                        if (positionAsInteger <= array.Count)
                        {
                            array.Insert(positionAsInteger, conversionResult.ConvertedInstance);
                        }
                        else
                        {
                            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);
                        }
                    }
                }
                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
            {
                var conversionResultTuple = PropertyHelpers.ConvertToActualType(pathProperty.PropertyType, value);

                // conversion successful
                if (conversionResultTuple.CanBeConverted)
                {
                    if (!pathProperty.CanWrite)
                    {
                        // cannot set the property
                        throw new JsonPatchException(
                                  new JsonPatchError(
                                      objectToApplyTo,
                                      operationToReport,
                                      string.Format("Patch failed: cannot set property value at path {0}.  Possible cause: the property doesn't have an accessible setter.", path)),
                                  422);
                    }

                    PropertyHelpers.SetValue(pathProperty, objectToApplyTo, actualPathToProperty,
                                             conversionResultTuple.ConvertedInstance);
                }
                else
                {
                    throw new JsonPatchException(
                              new JsonPatchError(
                                  objectToApplyTo,
                                  operationToReport,
                                  string.Format("Patch failed: provided value is invalid for property type at location path: {0}", path))
                              , 422);
                }
            }
        }
Exemplo n.º 3
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 void 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 pathProperty = PropertyHelpers
                               .FindProperty(objectToApplyTo, actualPathToProperty);

            // does the target location exist?
            if (pathProperty == null)
            {
                throw new JsonPatchException(
                          new JsonPatchError(
                              objectToApplyTo,
                              operationToReport,
                              string.Format("Patch failed: property at location path: {0} does not exist", path))
                          , 422);
            }

            // get the property, and remove it - in this case, for DTO's, that means setting
            // it to null or its default value; in case of an array, remove at provided index
            // or at the end.
            if (removeFromList || positionAsInteger > -1)
            {
                // what if it's an array but there's no position??
                if (pathProperty.PropertyType.IsNonStringList())
                {
                    // now, get the generic type of the enumerable
                    var genericTypeOfArray = PropertyHelpers.GetEnumerableType(pathProperty.PropertyType);

                    if (!pathProperty.CanRead)
                    {
                        // cannot get the property
                        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);
                    }

                    // get value (it can be cast, we just checked that)
                    var array = PropertyHelpers.GetValue(pathProperty, objectToApplyTo, actualPathToProperty) as IList;

                    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);
                        }
                        array.RemoveAt(array.Count - 1);
                    }
                    else
                    {
                        if (positionAsInteger < array.Count)
                        {
                            array.RemoveAt(positionAsInteger);
                        }
                        else
                        {
                            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);
                        }
                    }
                }
                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 (!pathProperty.CanWrite)
                {
                    // cannot set the property
                    throw new JsonPatchException(
                              new JsonPatchError(
                                  objectToApplyTo,
                                  operationToReport,
                                  string.Format("Patch failed: cannot set property value at path {0}.  Possible cause: the property doesn't have an accessible setter.", path)),
                              422);
                }

                // setting the value to "null" will use the default value in case of value types, and
                // null in case of reference types
                PropertyHelpers.SetValue(pathProperty, objectToApplyTo, actualPathToProperty, null);
            }
        }