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); }
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); }
internal void ResolveValue(JsonPlusSubstitution sub) { if (sub.Type == JsonPlusType.Empty) { Remove(sub); return; } }
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); }
/// <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); }
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); }