// 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.");
}
}
}
}