Files
crtr/Assets/YamlDotNet/RepresentationModel/YamlDocument.cs
2022-12-12 22:23:49 +08:00

212 lines
7.1 KiB
C#

// 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.Diagnostics;
using System.Globalization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
namespace YamlDotNet.RepresentationModel
{
/// <summary>
/// Represents an YAML document.
/// </summary>
public class YamlDocument
{
/// <summary>
/// Gets or sets the root node.
/// </summary>
/// <value>The root node.</value>
public YamlNode RootNode { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="YamlDocument"/> class.
/// </summary>
public YamlDocument(YamlNode rootNode)
{
RootNode = rootNode;
}
/// <summary>
/// Initializes a new instance of the <see cref="YamlDocument"/> class with a single scalar node.
/// </summary>
public YamlDocument(string rootNode)
{
RootNode = new YamlScalarNode(rootNode);
}
/// <summary>
/// Initializes a new instance of the <see cref="YamlDocument"/> class.
/// </summary>
internal YamlDocument(IParser parser)
{
var state = new DocumentLoadingState();
parser.Consume<DocumentStart>();
while (!parser.TryConsume<DocumentEnd>(out var _))
{
Debug.Assert(RootNode == null);
RootNode = YamlNode.ParseNode(parser, state);
if (RootNode is YamlAliasNode)
{
throw new YamlException("A document cannot contain only an alias");
}
}
state.ResolveAliases();
// Throw should not happen unless the parser has a bug
RootNode = RootNode ?? throw new ArgumentException("Atempted to parse an empty document");
}
/// <summary>
/// Visitor that assigns anchors to nodes that are referenced more than once.
/// Existing anchors are preserved as much as possible.
/// </summary>
private class AnchorAssigningVisitor : YamlVisitorBase
{
private readonly HashSet<AnchorName> existingAnchors = new HashSet<AnchorName>();
/// <summary>
/// Key: Node, Value: IsDuplicate
/// </summary>
private readonly Dictionary<YamlNode, bool> visitedNodes = new Dictionary<YamlNode, bool>();
public void AssignAnchors(YamlDocument document)
{
existingAnchors.Clear();
visitedNodes.Clear();
document.Accept(this);
var random = new Random();
foreach (var visitedNode in visitedNodes)
{
if (visitedNode.Value)
{
AnchorName anchor;
// If the existing anchor is not already used, we can have it
if (!visitedNode.Key.Anchor.IsEmpty && !existingAnchors.Contains(visitedNode.Key.Anchor))
{
anchor = visitedNode.Key.Anchor;
}
else
{
do
{
anchor = new AnchorName(random.Next().ToString(CultureInfo.InvariantCulture));
} while (existingAnchors.Contains(anchor));
}
existingAnchors.Add(anchor);
visitedNode.Key.Anchor = anchor;
}
}
}
/// <summary>
/// Returns whether the visited node is a duplicate.
/// </summary>
private bool VisitNodeAndFindDuplicates(YamlNode node)
{
if (visitedNodes.TryGetValue(node, out var isDuplicate))
{
if (!isDuplicate)
{
visitedNodes[node] = true;
}
return !isDuplicate;
}
else
{
visitedNodes.Add(node, false);
return false;
}
}
public override void Visit(YamlScalarNode scalar)
{
VisitNodeAndFindDuplicates(scalar);
}
public override void Visit(YamlMappingNode mapping)
{
if (!VisitNodeAndFindDuplicates(mapping))
{
base.Visit(mapping);
}
}
public override void Visit(YamlSequenceNode sequence)
{
if (!VisitNodeAndFindDuplicates(sequence))
{
base.Visit(sequence);
}
}
}
private void AssignAnchors()
{
var visitor = new AnchorAssigningVisitor();
visitor.AssignAnchors(this);
}
internal void Save(IEmitter emitter, bool assignAnchors = true)
{
if (assignAnchors)
{
AssignAnchors();
}
emitter.Emit(new DocumentStart());
RootNode.Save(emitter, new EmitterState());
emitter.Emit(new DocumentEnd(false));
}
/// <summary>
/// Accepts the specified visitor by calling the appropriate Visit method on it.
/// </summary>
/// <param name="visitor">
/// A <see cref="IYamlVisitor"/>.
/// </param>
public void Accept(IYamlVisitor visitor)
{
visitor.Visit(this);
}
/// <summary>
/// Gets all nodes from the document.
/// <see cref="MaximumRecursionLevelReachedException"/> is thrown if an infinite recursion is detected.
/// </summary>
public IEnumerable<YamlNode> AllNodes
{
get
{
return RootNode.AllNodes;
}
}
}
}