2018-11-17 04:00:12 +08:00
|
|
|
|
namespace UglyToad.PdfPig.Tokens
|
2017-11-13 01:06:19 +08:00
|
|
|
|
{
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A dictionary object is an associative table containing pairs of objects, known as the dictionary's entries.
|
|
|
|
|
/// The key must be a <see cref="NameToken"/> and the value may be an kind of <see cref="IToken"/>.
|
|
|
|
|
/// </summary>
|
2021-05-10 17:58:42 +08:00
|
|
|
|
public class DictionaryToken : IDataToken<IReadOnlyDictionary<string, IToken>>, IEquatable<DictionaryToken>
|
2017-11-13 01:06:19 +08:00
|
|
|
|
{
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The key value pairs in this dictionary.
|
|
|
|
|
/// </summary>
|
2018-01-14 18:53:01 +08:00
|
|
|
|
public IReadOnlyDictionary<string, IToken> Data { get; }
|
2017-11-13 01:06:19 +08:00
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create a new <see cref="DictionaryToken"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">The data this dictionary will contain.</param>
|
2020-01-05 18:07:01 +08:00
|
|
|
|
public DictionaryToken(IReadOnlyDictionary<NameToken, IToken> data)
|
2017-11-13 01:06:19 +08:00
|
|
|
|
{
|
2018-01-14 18:53:01 +08:00
|
|
|
|
if (data == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(data));
|
|
|
|
|
}
|
2018-12-08 22:38:27 +08:00
|
|
|
|
|
2018-01-14 18:53:01 +08:00
|
|
|
|
var result = new Dictionary<string, IToken>(data.Count);
|
|
|
|
|
|
|
|
|
|
foreach (var keyValuePair in data)
|
|
|
|
|
{
|
2018-12-08 22:38:27 +08:00
|
|
|
|
result[keyValuePair.Key.Data] = keyValuePair.Value;
|
2018-01-14 18:53:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Data = result;
|
2017-11-13 01:06:19 +08:00
|
|
|
|
}
|
2018-01-19 08:35:04 +08:00
|
|
|
|
|
|
|
|
|
private DictionaryToken(IReadOnlyDictionary<string, IToken> data)
|
|
|
|
|
{
|
|
|
|
|
Data = data;
|
|
|
|
|
}
|
2018-04-13 05:34:38 +08:00
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Try and get the entry with a given name.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name of the entry to retrieve.</param>
|
|
|
|
|
/// <param name="token">The token, if it is found.</param>
|
|
|
|
|
/// <returns><see langword="true"/> if the token is found, <see langword="false"/> otherwise.</returns>
|
2018-01-19 08:35:04 +08:00
|
|
|
|
public bool TryGet(NameToken name, out IToken token)
|
2017-11-14 07:48:25 +08:00
|
|
|
|
{
|
|
|
|
|
if (name == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(name));
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 08:35:04 +08:00
|
|
|
|
return Data.TryGetValue(name.Data, out token);
|
2017-11-14 07:48:25 +08:00
|
|
|
|
}
|
2017-11-13 01:06:19 +08:00
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Try and get the entry with a given name and a specific data type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T">The expected data type of the dictionary value.</typeparam>
|
|
|
|
|
/// <param name="name">The name of the entry to retrieve.</param>
|
|
|
|
|
/// <param name="token">The token, if it is found.</param>
|
|
|
|
|
/// <returns><see langword="true"/> if the token is found with this type, <see langword="false"/> otherwise.</returns>
|
2018-04-28 20:00:43 +08:00
|
|
|
|
public bool TryGet<T>(NameToken name, out T token) where T : IToken
|
|
|
|
|
{
|
|
|
|
|
token = default(T);
|
|
|
|
|
if (!TryGet(name, out var t) || !(t is T typedToken))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = typedToken;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Whether the dictionary contains an entry with this name.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name">The name to check.</param>
|
|
|
|
|
/// <returns><see langword="true"/> if the token is found, <see langword="false"/> otherwise.</returns>
|
2018-01-19 08:35:04 +08:00
|
|
|
|
public bool ContainsKey(NameToken name)
|
|
|
|
|
{
|
|
|
|
|
return Data.ContainsKey(name.Data);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create a copy of this dictionary with the additional entry (or override the value of the existing entry).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The key of the entry to create or override.</param>
|
|
|
|
|
/// <param name="value">The value of the entry to create or override.</param>
|
|
|
|
|
/// <returns>A new <see cref="DictionaryToken"/> with the entry created or modified.</returns>
|
2018-01-19 08:35:04 +08:00
|
|
|
|
public DictionaryToken With(NameToken key, IToken value) => With(key.Data, value);
|
2018-11-25 03:02:06 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create a copy of this dictionary with the additional entry (or override the value of the existing entry).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The key of the entry to create or override.</param>
|
|
|
|
|
/// <param name="value">The value of the entry to create or override.</param>
|
|
|
|
|
/// <returns>A new <see cref="DictionaryToken"/> with the entry created or modified.</returns>
|
2018-01-19 08:35:04 +08:00
|
|
|
|
public DictionaryToken With(string key, IToken value)
|
|
|
|
|
{
|
2021-05-27 18:54:11 +08:00
|
|
|
|
if (key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(value));
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 08:35:04 +08:00
|
|
|
|
var result = new Dictionary<string, IToken>(Data.Count + 1);
|
|
|
|
|
|
|
|
|
|
foreach (var keyValuePair in Data)
|
|
|
|
|
{
|
|
|
|
|
result[keyValuePair.Key] = keyValuePair.Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result[key] = value;
|
|
|
|
|
|
|
|
|
|
return new DictionaryToken(result);
|
|
|
|
|
}
|
2018-11-25 03:02:06 +08:00
|
|
|
|
|
2021-05-27 18:54:11 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a copy of this dictionary with the entry with the specified key removed (if it exists).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The key of the entry to remove.</param>
|
|
|
|
|
/// <returns>A new <see cref="DictionaryToken"/> with the entry removed.</returns>
|
|
|
|
|
public DictionaryToken Without(NameToken key) => Without(key.Data);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a copy of this dictionary with the entry with the specified key removed (if it exists).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="key">The key of the entry to remove.</param>
|
|
|
|
|
/// <returns>A new <see cref="DictionaryToken"/> with the entry removed.</returns>
|
|
|
|
|
public DictionaryToken Without(string key)
|
|
|
|
|
{
|
|
|
|
|
if (key == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = new Dictionary<string, IToken>(Data.ContainsKey(key) ? Data.Count - 1 : Data.Count);
|
|
|
|
|
|
|
|
|
|
foreach (var keyValuePair in Data.Where(x => !x.Key.Equals(key)))
|
|
|
|
|
{
|
|
|
|
|
result[keyValuePair.Key] = keyValuePair.Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new DictionaryToken(result);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-05 08:31:37 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Create a new <see cref="DictionaryToken"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="data">The data this dictionary will contain.</param>
|
|
|
|
|
public static DictionaryToken With(IReadOnlyDictionary<string, IToken> data)
|
|
|
|
|
{
|
|
|
|
|
return new DictionaryToken(data ?? throw new ArgumentNullException(nameof(data)));
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-07 23:25:56 +08:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public bool Equals(IToken obj)
|
|
|
|
|
{
|
2021-05-10 17:58:42 +08:00
|
|
|
|
return Equals(obj as DictionaryToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public bool Equals(DictionaryToken other)
|
|
|
|
|
{
|
|
|
|
|
if (other == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ReferenceEquals(this, other))
|
2020-03-14 21:35:49 +08:00
|
|
|
|
{
|
2020-03-07 23:25:56 +08:00
|
|
|
|
return true;
|
2021-05-10 17:58:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-07 23:25:56 +08:00
|
|
|
|
if (Data.Count != other.Data.Count)
|
2020-03-14 21:35:49 +08:00
|
|
|
|
{
|
2020-03-07 23:25:56 +08:00
|
|
|
|
return false;
|
2020-03-14 21:35:49 +08:00
|
|
|
|
}
|
2020-03-07 23:25:56 +08:00
|
|
|
|
|
2020-03-14 21:35:49 +08:00
|
|
|
|
foreach (var kvp in other.Data)
|
|
|
|
|
{
|
|
|
|
|
if (!Data.TryGetValue(kvp.Key, out var val) || !val.Equals(kvp.Value))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-10 17:58:42 +08:00
|
|
|
|
return true;
|
2020-03-07 23:25:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-25 03:02:06 +08:00
|
|
|
|
/// <inheritdoc />
|
2017-11-13 01:06:19 +08:00
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return string.Join(", ", Data.Select(x => $"<{x.Key}, {x.Value}>"));
|
2021-05-10 17:58:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-13 01:06:19 +08:00
|
|
|
|
}
|
|
|
|
|
}
|