private void ResolveSubstitutions() { foreach (var sub in _substitutions) { // Retrieve value HoconValue res; try { res = ResolveSubstitution(sub); } catch (HoconException e) { throw HoconParserException.Create(sub, sub.Path, $"Invalid substitution declaration. {e.Message}.", e); } if (res != null) { sub.ResolvedValue = res; continue; } // Try to pull value from environment string envValue = null; try { envValue = Environment.GetEnvironmentVariable(sub.Path.Value); } catch (Exception) { // ignored } if (envValue != null) { // undefined value resolved to an environment variable res = new HoconValue(sub.Parent); if (envValue.NeedQuotes()) { res.Add(new HoconQuotedString(sub.Parent, envValue)); } else { res.Add(new HoconUnquotedString(sub.Parent, envValue)); } sub.ResolvedValue = res; continue; } // ${ throws exception if it is not resolved if (sub.Required) { throw HoconParserException.Create(sub, sub.Path, $"Unresolved substitution: {sub.Path}"); } sub.ResolvedValue = new HoconEmptyValue(sub.Parent); } }
public void FallbackMerge(HoconObject other) { foreach (var kvp in other) { var path = kvp.Value.Path; if (!TryGetValue(path, out _)) { var currentObject = this; HoconField newField = null; foreach (var key in path) { newField = currentObject.GetOrCreateKey(key); if (newField.Type == HoconType.Empty) { var emptyValue = new HoconValue(newField); emptyValue.Add(new HoconObject(emptyValue)); newField.SetValue(emptyValue); } currentObject = newField.GetObject(); } newField.SetValue(kvp.Value.Value); } else { if (kvp.Value.Type == HoconType.Object) { FallbackMerge(kvp.Value.GetObject()); } } } }
private static Config CreateEmpty() { var value = new HoconValue(null); value.Add(new HoconObject(value)); return(new Config(value)); }
/// <inheritdoc /> public List <HoconValue> GetArray() { var result = new List <HoconValue>(); foreach (var item in this) { switch (item) { case HoconValue value: result.Add(value); break; case HoconObject obj: var val = new HoconValue(this); val.Add(obj); result.Add(val); break; case HoconSubstitution sub: if (sub.ResolvedValue != null) { result.AddRange(sub.ResolvedValue.GetArray()); } break; default: throw new HoconException($"Unknown type: {item.Type}"); } } return(result); }
public virtual IHoconElement Clone(IHoconElement newParent) { var clone = new HoconValue(newParent); foreach (var value in this) { clone.Add(value.Clone(clone)); } return(clone); }
/// <summary> /// Wraps this <see cref="HoconValue" /> into a new <see cref="HoconObject" /> at the specified key. /// </summary> /// <param name="key">The key designated to be the new root element.</param> /// <returns>A new HOCON root.</returns> /// <remarks> /// Immutable. Performs a deep copy on this <see cref="HoconValue" /> first. /// </remarks> public HoconRoot AtKey(string key) { var value = new HoconValue(null); var obj = new HoconObject(value); var field = new HoconField(key, obj); field.SetValue(Clone(field) as HoconValue); obj.Add(key, field); value.Add(obj); return(new HoconRoot(value)); }
internal void EnsureFieldIsObject() { if (Type == HoconType.Object) { return; } var v = new HoconValue(this); var o = new HoconObject(v); v.Add(o); _internalValues.Add(v); }
private void ParseTokens() { if (_tokens.Current.IsNonSignificant() || _tokens.Current.Type == TokenType.EndOfLine) { _tokens.ToNextSignificantLine(); } // Array Hocon files are handled differently than normal Hocon file. // Array Hocon files HAVE to start with an angle bracket and can only contain ONE array if (_tokens.Current.Type == TokenType.StartOfArray) { _root.Add(ParseArray(_root)); // Sanity check if (_tokens.Current.IsNonSignificant() || _tokens.Current.Type == TokenType.EndOfLine) { _tokens.ToNextSignificantLine(); } if (_tokens.Current.Type != TokenType.EndOfFile) { throw HoconParserException.Create(_tokens.Current, null, $"Illegal token type: {_tokens.Current.Type}. Hocon array file can only contain one array.", null); } return; } if (_tokens.Current.Type != TokenType.StartOfObject) { // This is a "headless" Hocon file, we'll normalize the file // by inserting the proper open and close curly brackets _tokens.Insert(new Token("{", TokenType.StartOfObject, null)); _tokens.Insert(_tokens.Count - 1, new Token("}", TokenType.EndOfObject, null)); } ParseObject(ref _root); }
// The owner in this context can be either an object or an array. private void ParseObject(ref HoconValue owner) { if (owner == null) { throw HoconParserException.Create(_tokens.Current, Path, "ParseObject called with null parent."); } // Sanity check if (_tokens.Current.Type != TokenType.StartOfObject) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.StartOfObject}`, found `{_tokens.Current.Type}` instead."); } // Consume curly bracket _tokens.ToNextSignificant(); var hoconObject = owner.GetObject(); if (hoconObject == null) { hoconObject = new HoconObject(owner); owner.Add(hoconObject); } var valueWasParsed = false; var parsing = true; while (parsing) { switch (_tokens.Current.Type) { case TokenType.Include: if (valueWasParsed) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}`, " + $"found `{_tokens.Current.Type}` instead."); } owner.ReParent(ParseInclude()); valueWasParsed = true; break; case TokenType.LiteralValue: if (_tokens.Current.IsNonSignificant()) { _tokens.ToNextSignificant(); } if (_tokens.Current.Type != TokenType.LiteralValue) { break; } if (valueWasParsed) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.Comma}` or `{TokenType.EndOfLine}`, " + $"found `{_tokens.Current.Type}` instead."); } ParseField(hoconObject); valueWasParsed = true; break; case TokenType.Comment: case TokenType.EndOfLine: valueWasParsed = false; _tokens.ToNextSignificantLine(); break; case TokenType.Comma: if (!valueWasParsed) { throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected `{TokenType.Assign}` or `{TokenType.StartOfObject}`, " + $"found `{_tokens.Current.Type}` instead."); } valueWasParsed = false; _tokens.ToNextSignificant(); break; case TokenType.EndOfObject: valueWasParsed = false; parsing = false; break; case TokenType.EndOfFile: throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Expected {TokenType.EndOfObject} but found {_tokens.Current.Type} instead."); default: throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon object. Unexpected token `{_tokens.Current.Type}`."); } } // Consume the closing curly bracket. _tokens.ToNextSignificant(); }
static Config() { EmptyValue = new HoconValue(null); EmptyValue.Add(new HoconObject(EmptyValue)); }
/// <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="System.Exception">End of file reached while trying to read a value</exception> private HoconValue ParseValue(IHoconElement owner) { var value = new HoconValue(owner); var parsing = true; while (parsing) { switch (_tokens.Current.Type) { case TokenType.Include: 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(HoconLiteral.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.SubstituteOptional: case TokenType.SubstituteRequired: var pointerPath = HoconPath.Parse(_tokens.Current.Value); HoconSubstitution sub = new HoconSubstitution(value, pointerPath, _tokens.Current, _tokens.Current.Type == TokenType.SubstituteRequired); _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.Comma: parsing = false; break; case TokenType.PlusEqualAssign: HoconSubstitution subAssign = new HoconSubstitution(value, new HoconPath(Path), _tokens.Current, false); _substitutions.Add(subAssign); value.Add(subAssign); value.Add(ParsePlusEqualAssignArray(value)); parsing = false; break; case TokenType.Assign: ConsumeWhitelines(); break; default: throw HoconParserException.Create(_tokens.Current, Path, $"Failed to parse Hocon value. Unexpected token: `{_tokens.Current.Type}`"); } } // trim trailing whitespace if result is a literal if (value.Type == HoconType.Literal) { if (value[value.Count - 1] is HoconWhitespace) { value.RemoveAt(value.Count - 1); } } return(value); }
private void ParseTokens() { if (_tokens.Current.IsNonSignificant()) { ConsumeWhitelines(); } while (_tokens.Current.Type != TokenType.EndOfFile) { switch (_tokens.Current.Type) { case TokenType.Include: var parsedInclude = ParseInclude(null); if (_root.Type != HoconType.Object) { _root.Clear(); _root.Add(parsedInclude.GetObject()); } else { _root.Add(parsedInclude.GetObject()); } break; // Hocon config file may contain one array and one array only case TokenType.StartOfArray: _root.Clear(); _root.Add(ParseArray(null)); ConsumeWhitelines(); if (_tokens.Current.Type != TokenType.EndOfFile) { throw HoconParserException.Create(_tokens.Current, Path, "Hocon config can only contain one array or one object."); } return; case TokenType.StartOfObject: { var parsedObject = ParseObject(null); if (_root.Type != HoconType.Object) { _root.Clear(); _root.Add(parsedObject); } else { _root.Add(parsedObject.GetObject()); } break; } case TokenType.LiteralValue: { if (_tokens.Current.IsNonSignificant()) { ConsumeWhitelines(); } if (_tokens.Current.Type != TokenType.LiteralValue) { break; } var parsedObject = ParseObject(null); if (_root.Type != HoconType.Object) { _root.Clear(); _root.Add(parsedObject); } else { _root.Add(parsedObject.GetObject()); } break; } case TokenType.Comment: case TokenType.EndOfLine: case TokenType.EndOfFile: case TokenType.EndOfObject: case TokenType.EndOfArray: _tokens.Next(); break; default: throw HoconParserException.Create(_tokens.Current, null, $"Illegal token type: {_tokens.Current.Type}", null); } } }