Example #1
0
        internal void ResolveValue(IJsonPlusNode child)
        {
            if (child.Type == JsonPlusType.Empty)
            {
                Remove(child);
            }
            else if (Type == JsonPlusType.Empty)
            {
                Type = child.Type;
            }
            else if (Type != child.Type)
            {
                JsonPlusSubstitution sub = (JsonPlusSubstitution)child;
                throw JsonPlusParserException.Create(sub, sub.Path, string.Format(RS.SubstitutionSiblingTypeMismatch, Type, child.Type));
            }

            if (Parent is JsonPlusArray)
            {
                // #todo
                ((JsonPlusArray)Parent).ResolveValue(this);
                return;
            }

            ((JsonPlusObjectMember)Parent).ResolveValue(this);
        }
Example #2
0
        private JsonPlusValue ResolveSubstitution(JsonPlusSubstitution sub)
        {
            JsonPlusObjectMember subField = sub.ParentMember;

            // first case, this substitution is a direct self-reference
            if (sub.Path == sub.GetMemberPath())
            {
                IJsonPlusNode parent = sub.Parent;
                while (parent is JsonPlusValue)
                {
                    parent = parent.Parent;
                }

                // Fail case
                if (parent is JsonPlusArray)
                {
                    throw new JsonPlusException(RS.SelfRefSubstitutionInArray);
                }

                // try to resolve substitution by looking backward in the field assignment stack
                return(subField.OlderValueThan(sub));
            }

            // need to recursively get full path
            JsonPlusPath fieldPath = subField.GetMemberPath();

            // second case, the substitution references a field child in the past
            if (sub.Path.IsChildPathOf(fieldPath))
            {
                JsonPlusValue olderValue = subField.OlderValueThan(sub);
                if ((olderValue != null) &&
                    (olderValue.Type == JsonPlusType.Object))
                {
                    int          difLength = sub.Path.Count - fieldPath.Count;
                    JsonPlusPath deltaPath = sub.Path.SubPath(sub.Path.Count - difLength, difLength);

                    JsonPlusObject olderObject = olderValue.GetObject();
                    if (olderObject.TryGetValue(deltaPath, out JsonPlusValue innerValue))
                    {
                        return(innerValue.Type == JsonPlusType.Object ? innerValue : null);
                    }
                }
            }

            // Detect invalid parent-referencing substitution
            if (fieldPath.IsChildPathOf(sub.Path))
            {
                throw new JsonPlusException(RS.SubstitutionRefDirectParentError);
            }

            // Detect invalid cyclic reference loop
            if (IsValueCyclic(subField, sub))
            {
                throw new JsonPlusException(RS.CyclicSubstitutionLoop);
            }

            // third case, regular substitution
            _root.GetObject().TryGetValue(sub.Path, out JsonPlusValue field);
            return(field);
        }
Example #3
0
 internal void ResolveValue(JsonPlusSubstitution sub)
 {
     if (sub.Type == JsonPlusType.Empty)
     {
         Remove(sub);
         return;
     }
 }
Example #4
0
        private IJsonPlusNode ParseSelfAssignArray(IJsonPlusNode owner)
        {
            // sanity check
            if (_tokens.Current.Type != TokenType.SelfAssignment)
            {
                throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInArray, TokenType.SelfAssignment, _tokens.Current.Type));
            }

            // consume += operator token
            ConsumeWhitelines();

            JsonPlusArray currentArray = new JsonPlusArray(owner);

            switch (_tokens.Current.Type)
            {
            case TokenType.Include:
            case TokenType.OptionalInclude:
                // #todo
                currentArray.Add(ParseInclude(currentArray));
                break;

            case TokenType.StartOfArray:
                return(ParseArray(owner));

            case TokenType.StartOfObject:
                // #todo
                currentArray.Add(ParseObject(currentArray));
                break;

            case TokenType.LiteralValue:
                if (_tokens.Current.IsNonSignificant())
                {
                    ConsumeWhitelines();
                }
                if (_tokens.Current.Type != TokenType.LiteralValue)
                {
                    break;
                }

                currentArray.Add(ParseValue(currentArray));
                return(currentArray);

            case TokenType.OptionalSubstitution:
            case TokenType.Substitution:
                JsonPlusPath         pointerPath = JsonPlusPath.Parse(_tokens.Current.Value);
                JsonPlusSubstitution sub         = new JsonPlusSubstitution(owner, pointerPath, _tokens.Current,
                                                                            _tokens.Current.Type == TokenType.Substitution);
                _substitutions.Add(sub);
                _tokens.Next();
                return(sub);

            default:
                throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInArray, TokenType.EndOfArray, _tokens.Current.Type));
            }

            return(currentArray);
        }
Example #5
0
        /// <summary>
        /// Retrieves the next value token from the tokenizer and appends it
        /// to the supplied element <paramref name="owner"/>.
        /// </summary>
        /// <param name="owner">The element to append the next token.</param>
        /// <exception cref="Exception">End of file reached while trying to read a value</exception>
        private JsonPlusValue ParseValue(IJsonPlusNode owner)
        {
            JsonPlusValue value   = new JsonPlusValue(owner);
            bool          parsing = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                case TokenType.OptionalInclude:
                    value.Add(ParseInclude(value));
                    break;

                case TokenType.LiteralValue:
                    // Consume leading whitespaces.
                    if (_tokens.Current.IsNonSignificant())
                    {
                        ConsumeWhitespace();
                    }
                    if (_tokens.Current.Type != TokenType.LiteralValue)
                    {
                        break;
                    }

                    while (_tokens.Current.Type == TokenType.LiteralValue)
                    {
                        value.Add(JsonPlusLiteralValue.Create(value, _tokens.Current));
                        _tokens.Next();
                    }
                    break;

                case TokenType.StartOfObject:
                    value.Add(ParseObject(value));
                    break;

                case TokenType.StartOfArray:
                    value.Add(ParseArray(value));
                    break;

                case TokenType.OptionalSubstitution:
                case TokenType.Substitution:
                    JsonPlusPath         pointerPath = JsonPlusPath.Parse(_tokens.Current.Value);
                    JsonPlusSubstitution sub         = new JsonPlusSubstitution(value, pointerPath, _tokens.Current,
                                                                                _tokens.Current.Type == TokenType.Substitution);
                    _substitutions.Add(sub);
                    value.Add(sub);
                    _tokens.Next();
                    break;

                case TokenType.EndOfObject:
                case TokenType.EndOfArray:
                    parsing = false;
                    break;

                // comments automatically stop value parsing.
                case TokenType.Comment:
                    ConsumeWhitelines();
                    parsing = false;
                    break;

                case TokenType.EndOfLine:
                    parsing = false;
                    break;

                case TokenType.EndOfFile:
                case TokenType.ArraySeparator:
                    parsing = false;
                    break;

                case TokenType.SelfAssignment:
                    JsonPlusSubstitution subAssign = new JsonPlusSubstitution(value, new JsonPlusPath(Path), _tokens.Current, false);
                    _substitutions.Add(subAssign);
                    value.Add(subAssign);

                    value.Add(ParseSelfAssignArray(value));
                    parsing = false;
                    break;

                case TokenType.Assignment:
                    ConsumeWhitelines();
                    break;

                default:
                    throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.ErrAtUnexpectedToken, _tokens.Current.Type));
                }
            }

            // trim trailing whitespace if result is a literal
            if (value.Type == JsonPlusType.Literal)
            {
                if (value[value.Count - 1] is WhitespaceValue)
                {
                    value.RemoveAt(value.Count - 1);
                }
            }
            return(value);
        }
Example #6
0
        private bool IsValueCyclic(JsonPlusObjectMember field, JsonPlusSubstitution sub)
        {
            Stack <JsonPlusValue>       pendingValues = new Stack <JsonPlusValue>();
            List <JsonPlusObjectMember> visitedFields = new List <JsonPlusObjectMember> {
                field
            };
            Stack <JsonPlusSubstitution> pendingSubs = new Stack <JsonPlusSubstitution>();

            pendingSubs.Push(sub);

            while (pendingSubs.Count > 0)
            {
                JsonPlusSubstitution currentSub = pendingSubs.Pop();
                if (!_root.GetObject().TryGetMember(currentSub.Path, out var currentField))
                {
                    continue;
                }

                if (visitedFields.Contains(currentField))
                {
                    return(true);
                }

                visitedFields.Add(currentField);
                pendingValues.Push(currentField.Value);
                while (pendingValues.Count > 0)
                {
                    JsonPlusValue currentValue = pendingValues.Pop();

                    foreach (IJsonPlusNode value in currentValue)
                    {
                        switch (value)
                        {
                        case JsonPlusLiteralValue _:
                            break;

                        case JsonPlusObject o:
                            foreach (JsonPlusObjectMember f in o.Values)
                            {
                                if (visitedFields.Contains(f))
                                {
                                    return(true);
                                }

                                visitedFields.Add(f);
                                pendingValues.Push(f.Value);
                            }
                            break;

                        case JsonPlusArray a:
                            foreach (JsonPlusValue item in a.GetArray())
                            {
                                pendingValues.Push(item);
                            }
                            break;

                        case JsonPlusSubstitution s:
                            pendingSubs.Push(s);
                            break;
                        }
                    }
                }
            }
            return(false);
        }