예제 #1
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);
        }
예제 #2
0
        /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
        public IJsonPlusNode Clone(IJsonPlusNode newParent)
        {
            JsonPlusObjectMember newField = new JsonPlusObjectMember(Path, (JsonPlusObject)newParent);

            newField._internalValues.AddRange(_internalValues);
            return(newField);
        }
예제 #3
0
        public static bool IsSubstitution(this IJsonPlusNode value)
        {
            switch (value)
            {
            case JsonPlusValue v:
                foreach (IJsonPlusNode val in v)
                {
                    if (val is JsonPlusSubstitution)
                    {
                        return(true);
                    }
                }
                return(false);

            case JsonPlusObjectMember f:
                foreach (IJsonPlusNode v in f.Value)
                {
                    if (v is JsonPlusSubstitution)
                    {
                        return(true);
                    }
                }
                return(false);

            case JsonPlusSubstitution _:
                return(true);

            default:
                return(false);
            }
        }
예제 #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="JsonPlusSubstitution"/> class.
 /// </summary>
 /// <param name="parent">The <see cref="JsonPlusValue"/> parent of this substitution.</param>
 /// <param name="path">The <see cref="JsonPlusPath"/> that this substitution is pointing to.</param>
 /// <param name="required">Indicates whether this is a lazy substitution. Lazy substitutions uses the `${?` notation.</param>
 /// <param name="location">The location of this substitution token in the source code, used for exception generation purposes.</param>
 internal JsonPlusSubstitution(IJsonPlusNode parent, JsonPlusPath path, ISourceLocation location, bool required)
 {
     Parent   = parent ?? throw new ArgumentNullException(nameof(parent), RS.CannotSubstitutionRootNode);
     Column   = location.Column;
     Line     = location.Line;
     Required = required;
     Path     = path;
 }
예제 #5
0
        private static void Flatten(IJsonPlusNode node)
        {
            if (!(node is JsonPlusValue v))
            {
                return;
            }

            switch (v.Type)
            {
            case JsonPlusType.Object:
                JsonPlusObject o = v.GetObject();
                v.Clear();
                v.Add(o);
                foreach (JsonPlusObjectMember item in o.Values)
                {
                    Flatten(item);
                }
                break;

            case JsonPlusType.Array:
                List <IJsonPlusNode> a = v.GetArray();
                v.Clear();
                JsonPlusArray newArray = new JsonPlusArray(v);
                foreach (IJsonPlusNode item in a)
                {
                    Flatten(item);
                    newArray.Add(item);
                }
                v.Add(newArray);
                break;

            case JsonPlusType.Literal:
                if (v.Count == 1)
                {
                    return;
                }

                string value = v.GetString();
                v.Clear();
                if (value == null)
                {
                    v.Add(new NullValue(v));
                }
                else if (value.NeedTripleQuotes())
                {
                    v.Add(new TripleQuotedStringValue(v, value));
                }
                else if (value.NeedQuotes())
                {
                    v.Add(new QuotedStringValue(v, value));
                }
                else
                {
                    v.Add(new UnquotedStringValue(v, value));
                }
                break;
            }
        }
예제 #6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="JsonPlusMergedObject"/> class.
 /// </summary>
 /// <param name="parent">The parent container.</param>
 /// <param name="objects">All <see cref="JsonPlusObject"/> items that are merged.</param>
 public JsonPlusMergedObject(IJsonPlusNode parent, List <JsonPlusObject> objects)
     : base(parent)
 {
     Objects = objects;
     foreach (JsonPlusObject obj in objects)
     {
         base.Merge(obj);
     }
 }
예제 #7
0
        /// <see cref="JsonPlusValue.Equals(IJsonPlusNode)"/>
        public override bool Equals(IJsonPlusNode other)
        {
            if (other is null)
            {
                return(false);
            }

            return(other.Type == JsonPlusType.Empty);
        }
예제 #8
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);
        }
예제 #9
0
        private object[] PopulateJPlusArray(List <IJsonPlusNode> jv, out ErrorRecord error)
        {
            if (jv == null)
            {
                throw new ArgumentNullException(nameof(jv));
            }

            error = null;

            // do not recurse over the max
            if (_maxRecurseLevel > -1)
            {
                _recurseLevel += 1;
                if (_recurseLevel > _maxRecurseLevel)
                {
                    return(null);
                }
            }

            List <object> results = new List <object>();

            for (int i = 0; i < jv.Count; i++)
            {
                IJsonPlusNode current = jv[i];

                if (current.Type == JsonPlusType.Empty)
                {
                    results.Add(null);
                }
                else if (current.Type == JsonPlusType.Literal)
                {
                    results.Add(PopulateJPlusLeaf(current.GetValue(), out error));
                }
                else if (current.Type == JsonPlusType.Object)
                {
                    results.Add(PopulateJPlusObject(current.GetObject(), out error));
                    _recurseLevel -= 1;
                }
                else if (current.Type == JsonPlusType.Array)
                {
                    results.Add(PopulateJPlusArray(current.GetArray(), out error));
                    _recurseLevel -= 1;
                }
                else
                {
                    error = new ErrorRecord(new JsonPlusException(string.Format(RS.JsonPlusAmbiguousTypeInArray, i)), "UnsupportedJsonPlusDataType", ErrorCategory.ParserError, null);
                }

                // terminate immediately if there's an error
                if (error != null)
                {
                    return(null);
                }
            }

            return(results.ToArray());
        }
예제 #10
0
        /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
        public IJsonPlusNode Clone(IJsonPlusNode newParent)
        {
            JsonPlusObject clone = new JsonPlusObject(newParent);

            foreach (KeyValuePair <string, JsonPlusObjectMember> kvp in this)
            {
                clone[kvp.Key] = (JsonPlusObjectMember)kvp.Value.Clone(clone);
            }
            return(clone);
        }
예제 #11
0
        /// <see cref="JsonPlusValue.Equals(IJsonPlusNode)"/>
        public bool Equals(IJsonPlusNode other)
        {
            if (other is null)
            {
                return(false);
            }

            if (ReferenceEquals(this, other))
            {
                return(true);
            }

            return(Type == other.Type && string.Equals(Value, other.GetString()));
        }
예제 #12
0
        /// <see cref="JsonPlusValue.Equals(IJsonPlusNode)"/>
        public bool Equals(IJsonPlusNode other)
        {
            if (other is null)
            {
                return(false);
            }
            if (ReferenceEquals(this, other))
            {
                return(true);
            }

            return(other is JsonPlusObjectMember field &&
                   Path.Equals(field.Path) &&
                   Value.Equals(other));
        }
예제 #13
0
        internal JsonPlusValue OlderValueThan(IJsonPlusNode marker)
        {
            List <JsonPlusObject> objectList = new List <JsonPlusObject>();
            int index = 0;

            while (index < _internalValues.Count)
            {
                JsonPlusValue value = _internalValues[index];
                if (value.Any(v => ReferenceEquals(v, marker)))
                {
                    break;
                }

                switch (value.Type)
                {
                case JsonPlusType.Object:
                    objectList.Add(value.GetObject());
                    break;

                case JsonPlusType.Literal:
                case JsonPlusType.Array:
                    objectList.Clear();
                    break;
                }

                index++;
            }

            if (objectList.Count == 0)
            {
                return(index == 0
                    ? null
                    : _internalValues[index - 1]);
            }

            JsonPlusValue  result = new JsonPlusValue(null);
            JsonPlusObject o      = new JsonPlusObject(result);

            result.Add(o);

            foreach (JsonPlusObject obj in objectList)
            {
                o.Merge(obj);
            }

            return(result);
        }
예제 #14
0
        /// <see cref="JsonPlusValue.Equals(IJsonPlusNode)"/>
        public bool Equals(IJsonPlusNode other)
        {
            if (other is null)
            {
                return(false);
            }
            if (ReferenceEquals(this, other))
            {
                return(true);
            }

            if (other is JsonPlusSubstitution sub)
            {
                return((Path == sub.Path) && (GetMemberPath() == sub.GetMemberPath()));
            }

            return(!(_resolvedValue is null) && _resolvedValue.Equals(other));
        }
예제 #15
0
        /// <see cref="JsonPlusValue.Equals(IJsonPlusNode)"/>
        public bool Equals(IJsonPlusNode other)
        {
            if (other is null)
            {
                return(false);
            }

            if (ReferenceEquals(this, other))
            {
                return(true);
            }

            if (other.Type != JsonPlusType.Object)
            {
                return(false);
            }

            return(this.AsEnumerable().SequenceEqual(other.GetObject().AsEnumerable()));
        }
예제 #16
0
        internal static JsonPlusLiteralValue Create(IJsonPlusNode owner, Token token)
        {
            switch (token.LiteralType)
            {
            case LiteralTokenType.Null:
                return(new NullValue(owner));

            case LiteralTokenType.Boolean:
                return(new BooleanValue(owner, token.Value));

            case LiteralTokenType.Whitespace:
                return(new WhitespaceValue(owner, token.Value));

            case LiteralTokenType.UnquotedLiteralValue:
                return(new UnquotedStringValue(owner, token.Value));

            case LiteralTokenType.QuotedLiteralValue:
                return(new QuotedStringValue(owner, token.Value));

            case LiteralTokenType.TripleQuotedLiteralValue:
                return(new TripleQuotedStringValue(owner, token.Value));

            case LiteralTokenType.Integer:
                return(new IntegerValue(owner, token.Value));

            case LiteralTokenType.Decimal:
                return(new DecimalValue(owner, token.Value));

            case LiteralTokenType.Hexadecimal:
                return(new HexadecimalValue(owner, token.Value));

            case LiteralTokenType.Octet:
                return(new OctetValue(owner, token.Value));

            default:
                throw new JsonPlusException(string.Format(RS.UnknownLiteralToken, token.Value));
            }
        }
예제 #17
0
        /// <summary>
        /// Returns the <see cref="JsonPlusObjectMember"/> associated with the key specified.
        /// </summary>
        /// <param name="key">The key associated with the member to return.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="key"/> specified is `null`.</exception>
        /// <exception cref="KeyNotFoundException">The <paramref name="key"/> does not exist in the <see cref="JsonPlusObject"/> instance.</exception>
        /// <returns>The <see cref="JsonPlusObjectMember"/> associated with <paramref name="key"/>.</returns>
        public JsonPlusObjectMember GetMember(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (!TryGetValue(key, out JsonPlusObjectMember item))
            {
                IJsonPlusNode p = Parent;
                while (p != null && !(p is JsonPlusObjectMember))
                {
                    p = p.Parent;
                }

                string currentPath = (p != null)
                    ? ((JsonPlusObjectMember)p).Path.Value + "." + key
                    : key;
                string errorMessage = string.Format(RS.ObjectMemberNotFoundByPath, key, currentPath);
                throw new KeyNotFoundException(errorMessage);
            }

            return(item);
        }
예제 #18
0
 /// <summary>
 /// Initializes a new instance of the <see cref="OctetValue"/> class.
 /// </summary>
 /// <param name="parent">The container object.</param>
 /// <param name="value">The underlying <see cref="string"/> of this value.</param>
 public OctetValue(IJsonPlusNode parent, string value)
     : base(parent, value)
 {
 }
예제 #19
0
        // The owner in this context can be either an object or an array.
        private JsonPlusObject ParseObject(IJsonPlusNode owner)
        {
            JsonPlusObject jpObject = new JsonPlusObject(owner);

            if (_tokens.Current.Type != TokenType.StartOfObject &&
                _tokens.Current.Type != TokenType.LiteralValue &&
                _tokens.Current.Type != TokenType.Include)
            {
                throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                          TokenType.StartOfObject, TokenType.LiteralValue,
                                                                                          _tokens.Current.Type));
            }

            bool headless = true;

            if (_tokens.Current.Type == TokenType.StartOfObject)
            {
                headless = false;
                ConsumeWhitelines();
            }

            IJsonPlusNode lastValue = null;
            bool          parsing   = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                case TokenType.OptionalInclude:
                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                                  TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                                                  _tokens.Current.Type));
                    }

                    lastValue = ParseInclude(jpObject);
                    break;

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

                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                                  TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                                                  _tokens.Current.Type));
                    }

                    lastValue = ParseObjectMember(jpObject);
                    break;

                // TODO: can an object be declared floating without being assigned to a field?
                //case TokenType.StartOfObject:
                case TokenType.Comment:
                case TokenType.EndOfLine:
                    switch (lastValue)
                    {
                    case null:
                        ConsumeWhitelines();
                        break;

                    case JsonPlusObjectMember _:
                        break;

                    default:
                        ((JsonPlusValue)jpObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.ArraySeparator:
                    switch (lastValue)
                    {
                    case null:
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                                  TokenType.Assignment, TokenType.StartOfObject,
                                                                                                  _tokens.Current.Type));

                    case JsonPlusObjectMember _:
                        break;

                    default:
                        ((JsonPlusValue)jpObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.EndOfObject:
                case TokenType.EndOfFile:
                    if (headless && _tokens.Current.Type != TokenType.EndOfFile)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInObject, TokenType.EndOfFile, _tokens.Current.Type));
                    }

                    if (!headless && _tokens.Current.Type != TokenType.EndOfObject)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInObject, TokenType.EndOfFile, _tokens.Current.Type));
                    }

                    switch (lastValue)
                    {
                    case null:
                        break;

                    case JsonPlusObjectMember _:
                        break;

                    default:
                        ((JsonPlusValue)jpObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    parsing   = false;
                    break;

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

            if (_tokens.Current.Type == TokenType.EndOfObject)
            {
                _tokens.Next();
            }

            return(jpObject);
        }
예제 #20
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TripleQuotedStringValue"/> class.
 /// </summary>
 /// <param name="parent">The container object.</param>
 /// <param name="value">The underlying <see cref="string"/> of this value.</param>
 public TripleQuotedStringValue(IJsonPlusNode parent, string value)
     : base(parent, value)
 {
 }
예제 #21
0
 /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
 public override IJsonPlusNode Clone(IJsonPlusNode newParent)
 {
     return(new OctetValue(newParent, Value));
 }
예제 #22
0
 /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
 public override IJsonPlusNode Clone(IJsonPlusNode newParent)
 {
     return(new EmptyValue(newParent));
 }
예제 #23
0
 /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
 public override IJsonPlusNode Clone(IJsonPlusNode newParent)
 {
     return(new TripleQuotedStringValue(newParent, Value));
 }
예제 #24
0
 /// <see cref="JsonPlusValue.Add(IJsonPlusNode)"/>
 public override void Add(IJsonPlusNode value)
 {
     throw new JsonPlusException(string.Format(RS.ErrAddToNode, nameof(EmptyValue)));
 }
예제 #25
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WhitespaceValue"/> class.
 /// </summary>
 /// <param name="parent">The container object.</param>
 /// <param name="value">The underlying <see cref="string"/> of this value.</param>
 public WhitespaceValue(IJsonPlusNode parent, string value)
     : base(parent, value)
 {
 }
예제 #26
0
 /// <summary>
 /// Initializes a new instance of the <see cref="EmptyValue"/> class.
 /// </summary>
 /// <param name="parent">The parent container.</param>
 public EmptyValue(IJsonPlusNode parent)
     : base(parent)
 {
 }
예제 #27
0
 /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
 public override IJsonPlusNode Clone(IJsonPlusNode newParent)
 {
     return(new WhitespaceValue(newParent, Value));
 }
예제 #28
0
        private IJsonPlusNode ParseInclude(IJsonPlusNode owner)
        {
            // sanity check
            if (_tokens.Current.Type != TokenType.Include &&
                _tokens.Current.Type != TokenType.OptionalInclude)
            {
                throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.InvalidTokenOnParseInclude, _tokens.Current.Type));
            }

            bool   required     = _tokens.Current.Type == TokenType.Include;
            string fileName     = null;
            Token  includeToken = _tokens.Current;

            List <TokenType> expectedTokens = new List <TokenType>(new[]
            {
                TokenType.LiteralValue,
                TokenType.CloseBracket,
                TokenType.EndOfLine
            });

            bool parsing = true;

            while (parsing)
            {
                if (!_tokens.GetNextSignificant(expectedTokens.ToArray()))
                {
                    throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.InvalidTokenInInclude, _tokens.Current.Type), null);
                }

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

                    if (_tokens.Current.LiteralType != LiteralTokenType.QuotedLiteralValue)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.BadIncludeFileName, LiteralTokenType.QuotedLiteralValue, _tokens.Current.LiteralType));
                    }

                    fileName = _tokens.Current.Value;
                    expectedTokens.Remove(TokenType.LiteralValue);

                    parsing = false;
                    break;

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

            if (fileName == null)
            {
                throw JsonPlusParserException.Create(_tokens.Current, Path, RS.FileNameMissingInInclude);
            }

            // Consume the last token
            _tokens.Next();

            string includeSrc = _includeCallback(fileName).ConfigureAwait(false).GetAwaiter().GetResult();

            if (string.IsNullOrWhiteSpace(includeSrc))
            {
                if (required)
                {
                    throw JsonPlusParserException.Create(includeToken, Path, RS.IncludeReturnEmptyError);
                }

                return(new EmptyValue(owner));
            }

            JsonPlusRoot includeRoot = new JsonPlusParser().ParseSource(includeSrc, false, false, _includeCallback);

            if (owner != null &&
                owner.Type != JsonPlusType.Empty &&
                owner.Type != includeRoot.Value.Type)
            {
                throw JsonPlusParserException.Create(includeToken, Path, string.Format(RS.IncludeMergeTypeMismatch, owner.Type, includeRoot.Value.Type));
            }

            // fixup the substitution, add the current path as a prefix to the substitution path
            foreach (JsonPlusSubstitution substitution in includeRoot.Substitutions)
            {
                substitution.Path.InsertRange(0, Path);
            }
            _substitutions.AddRange(includeRoot.Substitutions);

            // reparent the value returned by the callback to the owner of the include declaration
            return(includeRoot.Value.Clone(owner));
        }
예제 #29
0
        /// <summary>
        /// Retrieves the next array token from the tokenizer.
        /// </summary>
        /// <returns>An array of elements retrieved from the token.</returns>
        private JsonPlusArray ParseArray(IJsonPlusNode owner)
        {
            JsonPlusArray currentArray = new JsonPlusArray(owner);

            // consume start of array token
            ConsumeWhitelines();

            IJsonPlusNode lastValue = null;
            bool          parsing   = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                case TokenType.OptionalInclude:
                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path,
                                                             string.Format(RS.UnexpectedTokenWith2AltInArray,
                                                                           TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                           _tokens.Current.Type));
                    }

                    lastValue = ParseInclude(currentArray);
                    break;

                case TokenType.StartOfArray:
                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path,
                                                             string.Format(RS.UnexpectedTokenWith2AltInArray,
                                                                           TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                           _tokens.Current.Type));
                    }

                    // Array inside of arrays are parsed as values because it can be value concatenated with another array.
                    lastValue = ParseValue(currentArray);
                    break;

                case TokenType.StartOfObject:
                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path,
                                                             string.Format(RS.UnexpectedTokenWith2AltInArray,
                                                                           TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                           _tokens.Current.Type));
                    }

                    lastValue = ParseObject(currentArray);
                    break;

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

                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path,
                                                             string.Format(RS.UnexpectedTokenWith2AltInArray,
                                                                           TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                           _tokens.Current.Type));
                    }

                    lastValue = ParseValue(currentArray);
                    break;

                case TokenType.OptionalSubstitution:
                case TokenType.Substitution:
                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path,
                                                             string.Format(RS.UnexpectedTokenWith2AltInArray,
                                                                           TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                           _tokens.Current.Type));
                    }

                    lastValue = ParseValue(currentArray);

                    break;

                case TokenType.Comment:
                case TokenType.EndOfLine:
                    if (lastValue == null)
                    {
                        ConsumeWhitelines();
                        break;
                    }

                    currentArray.Add(lastValue);
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.ArraySeparator:
                    if (lastValue == null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.BadArrayType, _tokens.Current.Type));
                    }

                    currentArray.Add(lastValue);
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.EndOfArray:
                    if (lastValue != null)
                    {
                        currentArray.Add(lastValue);
                        lastValue = null;
                    }
                    parsing = false;
                    break;

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

            // Consume end of array token
            _tokens.Next();
            return(currentArray);
        }
예제 #30
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);
        }