using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using UnityEngine.Localization.SmartFormat;
using UnityEngine.Pool;
namespace UnityEngine.Localization.Pseudo
{
///
/// A message fragment represents part of a .
/// A Message can be broken into multiple and fragments.
///
public abstract class MessageFragment
{
///
/// The original string being parsed.
///
protected string m_OriginalString;
///
/// The start index for this fragment from .
///
protected int m_StartIndex;
///
/// The end index for this fragment from .
///
protected int m_EndIndex;
string m_CachedToString;
///
/// Total length of the fragment.
///
public int Length => m_StartIndex == -1 ? m_OriginalString.Length : m_EndIndex - m_StartIndex;
///
/// The message the fragment is part of.
///
public Message @Message { get; private set; }
internal void Initialize(Message parent, string original, int start, int end)
{
@Message = parent;
m_OriginalString = original;
m_StartIndex = start;
m_EndIndex = end;
m_CachedToString = null;
}
internal void Initialize(Message parent, string text)
{
@Message = parent;
m_OriginalString = text;
m_StartIndex = -1;
m_EndIndex = -1;
m_CachedToString = null;
}
///
public WritableMessageFragment CreateTextFragment(int start, int end)
{
var frag = WritableMessageFragment.Pool.Get();
var startIndex = m_StartIndex == -1 ? start : m_StartIndex + start;
var endIndex = m_StartIndex == -1 ? end : m_StartIndex + end;
frag.Initialize(@Message, m_OriginalString, startIndex, endIndex);
return frag;
}
///
public ReadOnlyMessageFragment CreateReadonlyTextFragment(int start, int end)
{
var frag = ReadOnlyMessageFragment.Pool.Get();
var startIndex = m_StartIndex == -1 ? start : m_StartIndex + start;
var endIndex = m_StartIndex == -1 ? end : m_StartIndex + end;
frag.Initialize(@Message, m_OriginalString, startIndex, endIndex);
return frag;
}
public override string ToString()
{
if (m_CachedToString == null)
m_CachedToString = m_StartIndex == -1 ? m_OriginalString : m_OriginalString.Substring(m_StartIndex, m_EndIndex - m_StartIndex);
return m_CachedToString;
}
internal void BuildString(StringBuilder builder)
{
if (m_StartIndex == -1)
builder.Append(m_OriginalString);
else
builder.Append(m_OriginalString, m_StartIndex, m_EndIndex - m_StartIndex);
}
///
/// Returns the char at the specified index.
///
/// The index of the char to return from .
///
public char this[int index]
{
get
{
var startIdx = m_StartIndex == -1 ? 0 : m_StartIndex;
return m_OriginalString[startIdx + index];
}
}
}
///
/// Represents a message fragment that can be modified.
///
[DebuggerDisplay("Writable: {Text}")]
public class WritableMessageFragment : MessageFragment
{
internal static readonly ObjectPool Pool = new ObjectPool(
() => new WritableMessageFragment(), collectionCheck: false);
///
/// The text contained in this fragment.
///
public string Text
{
get => ToString();
set => Initialize(Message, value);
}
}
///
/// Represents a message fragment that should be preserved and mot modified.
///
[DebuggerDisplay("ReadOnly: {Text}")]
public class ReadOnlyMessageFragment : MessageFragment
{
internal static readonly ObjectPool Pool = new ObjectPool(
() => new ReadOnlyMessageFragment(), collectionCheck: false);
///
/// The text contained in this fragment.
///
public string Text => ToString();
}
///
/// A message is a piece of text that can be broken down into multiple sub fragments. A fragment can be writable or read only.
/// A read only fragment indicates that the sub string should be preserved and not modified by ant pseudo methods.
///
public class Message
{
internal static readonly ObjectPool Pool = new ObjectPool(
() => new Message(), collectionCheck: false);
///
/// The original text before it was broken into fragments.
///
public string Original { get; private set; }
///
/// A message is comprised of writable and readonly fragments. Readonly fragments are those that should be preserved such as xml/rich text tags.
///
public List Fragments { get; private set; } = new List();
///
/// Total length of the Message including all Fragments.
///
public int Length
{
get
{
int l = 0;
foreach (var f in Fragments)
{
l += f.Length;
}
return l;
}
}
///
/// Creates a new which represents a sub string of the original.
/// Fragments are created using an ObjectPool so they can be reused. Use to return the fragment.
///
/// Original string
/// Sub string start
/// Sub string end
/// A new fragment.
public WritableMessageFragment CreateTextFragment(string original, int start, int end)
{
var frag = WritableMessageFragment.Pool.Get();
frag.Initialize(this, original, start, end);
return frag;
}
///
/// Creates a new which represents a string.
/// Fragments are created using an ObjectPool so they can be reused. Use to
/// return the fragment or allow the Message to handle returning the fragment if it is part of .
///
/// The source string.
/// A new fragment.
public WritableMessageFragment CreateTextFragment(string original)
{
var frag = WritableMessageFragment.Pool.Get();
frag.Initialize(this, original);
return frag;
}
///
/// Creates a which represents a sub string of the original that should
/// be preserved and not modified by any other pseudo methods.
/// Fragments are created using an ObjectPool so they can be reused. Use to
/// return the fragment or allow the Message to handle returning the fragment if it is part of .
///
/// Original string
/// Sub string start
/// Sub string end
/// A new fragment.
public ReadOnlyMessageFragment CreateReadonlyTextFragment(string original, int start, int end)
{
var frag = ReadOnlyMessageFragment.Pool.Get();
frag.Initialize(this, original, start, end);
return frag;
}
///
/// Creates a which represents string that should be preserved and not modified by any other pseudo methods.
/// Fragments are created using an ObjectPool so they can be reused. Use to
/// return the fragment or allow the Message to handle returning the fragment if it is part of .
///
/// The source string.
/// A new fragment.
public ReadOnlyMessageFragment CreateReadonlyTextFragment(string original)
{
var frag = ReadOnlyMessageFragment.Pool.Get();
frag.Initialize(this, original);
return frag;
}
///
/// Replaces the Fragments in and returns the previous fragment back to the ObjectPool so it can be reused.
///
/// Fragment to replace.
/// Replacement Fragment.
public void ReplaceFragment(MessageFragment original, MessageFragment replacement)
{
var index = Fragments.IndexOf(original);
if (index == -1)
throw new Exception($"Can not replace Fragment {original.ToString()} that is not part of the message.");
Fragments[index] = replacement;
ReleaseFragment(original);
}
///
/// Returns a Fragment back to its ObjectPool so it can be used again.
///
///
public void ReleaseFragment(MessageFragment fragment)
{
if (fragment is WritableMessageFragment wmf)
WritableMessageFragment.Pool.Release(wmf);
else if (fragment is ReadOnlyMessageFragment romf)
ReadOnlyMessageFragment.Pool.Release(romf);
}
///
/// Creates a new message to represent a piece of text.
///
/// The source text.
/// A new Message instance.
internal static Message CreateMessage(string text)
{
var message = Pool.Get();
message.Fragments.Add(message.CreateTextFragment(text));
message.Original = text;
return message;
}
internal void Release()
{
foreach (var f in Fragments)
{
ReleaseFragment(f);
}
Fragments.Clear();
Pool.Release(this);
}
public override string ToString()
{
using (StringBuilderPool.Get(out var stringBuilder))
{
foreach (var f in Fragments)
{
f.BuildString(stringBuilder);
}
return stringBuilder.ToString();
}
}
}
}