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;
|
2020-01-05 06:39:13 +08:00
|
|
|
|
using Core;
|
2018-04-13 05:34:38 +08:00
|
|
|
|
using Parser.Parts;
|
2018-11-17 04:00:12 +08:00
|
|
|
|
using Tokenization.Scanner;
|
2017-11-14 07:48:25 +08:00
|
|
|
|
using Util.JetBrains.Annotations;
|
2017-11-13 01:06:19 +08:00
|
|
|
|
|
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>
|
|
|
|
|
public class DictionaryToken : IDataToken<IReadOnlyDictionary<string, IToken>>
|
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>
|
2017-11-14 07:48:25 +08:00
|
|
|
|
[NotNull]
|
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>
|
2018-12-08 22:38:27 +08:00
|
|
|
|
public DictionaryToken([NotNull]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
|
|
|
|
internal T Get<T>(NameToken name, IPdfTokenScanner scanner) where T : IToken
|
2018-04-13 05:34:38 +08:00
|
|
|
|
{
|
|
|
|
|
if (!TryGet(name, out var token) || !(token is T typedToken))
|
|
|
|
|
{
|
|
|
|
|
if (!(token is IndirectReferenceToken indirectReference))
|
|
|
|
|
{
|
2018-11-25 21:06:54 +08:00
|
|
|
|
throw new PdfDocumentFormatException($"Dictionary does not contain token with name {name} of type {typeof(T).Name}.");
|
2018-04-13 05:34:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedToken = DirectObjectFinder.Get<T>(indirectReference, scanner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return typedToken;
|
|
|
|
|
}
|
2018-12-08 22:38:27 +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
|
|
|
|
|
2019-06-23 20:52:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Try and get the entry with a given name and type or look-up the object if it's an indirect reference.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal bool TryGet<T>(NameToken name, IPdfTokenScanner tokenScanner, out T token) where T : IToken
|
|
|
|
|
{
|
|
|
|
|
token = default(T);
|
|
|
|
|
if (!TryGet(name, out var t) || !(t is T typedToken))
|
|
|
|
|
{
|
|
|
|
|
if (t is IndirectReferenceToken reference)
|
|
|
|
|
{
|
|
|
|
|
return DirectObjectFinder.TryGet(reference, tokenScanner, out token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = typedToken;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2017-11-13 01:06:19 +08:00
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return string.Join(", ", Data.Select(x => $"<{x.Key}, {x.Value}>"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|