// This file is part of YamlDotNet - A .NET library for YAML. // Copyright (c) Antoine Aubry and contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using YamlDotNet.Core; using YamlDotNet.Core.Events; namespace YamlDotNet.RepresentationModel { /// /// Represents a single node in the YAML document. /// public abstract class YamlNode { private const int MaximumRecursionLevel = 1000; internal const string MaximumRecursionLevelReachedToStringValue = "WARNING! INFINITE RECURSION!"; /// /// Gets or sets the anchor of the node. /// /// The anchor. public AnchorName Anchor { get; set; } /// /// Gets or sets the tag of the node. /// /// The tag. public TagName Tag { get; set; } /// /// Gets the position in the input stream where the event that originated the node starts. /// public Mark Start { get; private set; } = Mark.Empty; /// /// Gets the position in the input stream where the event that originated the node ends. /// public Mark End { get; private set; } = Mark.Empty; /// /// Loads the specified event. /// /// The event. /// The state of the document. internal void Load(NodeEvent yamlEvent, DocumentLoadingState state) { Tag = yamlEvent.Tag; if (!yamlEvent.Anchor.IsEmpty) { Anchor = yamlEvent.Anchor; state.AddAnchor(this); } Start = yamlEvent.Start; End = yamlEvent.End; } /// /// Parses the node represented by the next event in . /// /// Returns the node that has been parsed. internal static YamlNode ParseNode(IParser parser, DocumentLoadingState state) { if (parser.Accept(out var _)) { return new YamlScalarNode(parser, state); } if (parser.Accept(out var _)) { return new YamlSequenceNode(parser, state); } if (parser.Accept(out var _)) { return new YamlMappingNode(parser, state); } if (parser.TryConsume(out var alias)) { return state.TryGetNode(alias.Value, out var node) ? node : new YamlAliasNode(alias.Value); } throw new ArgumentException("The current event is of an unsupported type.", nameof(parser)); } /// /// Resolves the aliases that could not be resolved when the node was created. /// /// The state of the document. internal abstract void ResolveAliases(DocumentLoadingState state); /// /// Saves the current node to the specified emitter. /// /// The emitter where the node is to be saved. /// The state. internal void Save(IEmitter emitter, EmitterState state) { if (!Anchor.IsEmpty && !state.EmittedAnchors.Add(Anchor)) { emitter.Emit(new AnchorAlias(Anchor)); } else { Emit(emitter, state); } } /// /// Saves the current node to the specified emitter. /// /// The emitter where the node is to be saved. /// The state. internal abstract void Emit(IEmitter emitter, EmitterState state); /// /// Accepts the specified visitor by calling the appropriate Visit method on it. /// /// /// A . /// public abstract void Accept(IYamlVisitor visitor); public override string ToString() { var level = new RecursionLevel(MaximumRecursionLevel); return ToString(level); } internal abstract string ToString(RecursionLevel level); /// /// Gets all nodes from the document, starting on the current node. /// is thrown if an infinite recursion is detected. /// public IEnumerable AllNodes { get { var level = new RecursionLevel(MaximumRecursionLevel); return SafeAllNodes(level); } } /// /// When implemented, recursively enumerates all the nodes from the document, starting on the current node. /// If is reached, a is thrown /// instead of continuing and crashing with a . /// internal abstract IEnumerable SafeAllNodes(RecursionLevel level); /// /// Gets the type of node. /// public abstract YamlNodeType NodeType { get; } /// /// Performs an implicit conversion from to . /// /// The value. /// The result of the conversion. public static implicit operator YamlNode(string value) { return new YamlScalarNode(value); } /// /// Performs an implicit conversion from string[] to . /// /// The value. /// The result of the conversion. public static implicit operator YamlNode(string[] sequence) { return new YamlSequenceNode(sequence.Select(i => (YamlNode)i)); } /// /// Converts a to a string by returning its value. /// public static explicit operator string?(YamlNode node) { return node is YamlScalarNode scalar ? scalar.Value : throw new ArgumentException($"Attempted to convert a '{node.NodeType}' to string. This conversion is valid only for Scalars."); } /// /// Gets the nth element in a . /// public YamlNode this[int index] { get { return this is YamlSequenceNode sequence ? sequence.Children[index] : throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {index}. Only Sequences can be indexed by number."); } } /// /// Gets the value associated with a key in a . /// public YamlNode this[YamlNode key] { get { return this is YamlMappingNode mapping ? mapping.Children[key] : throw new ArgumentException($"Accessed '{NodeType}' with an invalid index: {key}. Only Mappings can be indexed by key."); } } } }