356 lines
12 KiB
C#

//
// ExpandedString.cs - provides a way to represent a String that can contain variable or constant references
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// Copyright (c) 2011-2020 Mike Becker's Software
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
using UniversalEditor.ObjectModels.Markup;
namespace UniversalEditor
{
public class ExpandedStringVariableStore
{
private ExpandedStringVariable.ExpandedStringVariableCollection mvarConstants = new ExpandedStringVariable.ExpandedStringVariableCollection();
public ExpandedStringVariable.ExpandedStringVariableCollection Constants { get { return mvarConstants; } }
private ExpandedStringVariable.ExpandedStringVariableCollection mvarVariables = new ExpandedStringVariable.ExpandedStringVariableCollection();
public ExpandedStringVariable.ExpandedStringVariableCollection Variables { get { return mvarVariables; } }
}
public class ExpandedStringVariable
{
private string mvarID = String.Empty;
public string ID { get { return mvarID; } set { mvarID = value; } }
private object mvarValue = null;
public object Value { get { return mvarValue; } set { mvarValue = value; } }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(mvarID);
sb.Append("=");
if (mvarValue == null)
{
sb.Append("(null)");
}
else
{
sb.Append("\"");
sb.Append(mvarValue.ToString());
sb.Append("\"");
}
return sb.ToString();
}
public class ExpandedStringVariableCollection
{
private Dictionary<ExpandedStringSegmentVariableScope, Dictionary<string, ExpandedStringVariable>> variables = new Dictionary<ExpandedStringSegmentVariableScope, Dictionary<string, ExpandedStringVariable>>();
public ExpandedStringVariable this[ExpandedStringSegmentVariableScope scope, string id]
{
get
{
if (!variables.ContainsKey(scope)) variables.Add(scope, new Dictionary<string, ExpandedStringVariable>());
if (!variables[scope].ContainsKey(id)) return null;
return variables[scope][id];
}
set
{
if (!variables.ContainsKey(scope)) variables.Add(scope, new Dictionary<string, ExpandedStringVariable>());
variables[scope][id] = value;
}
}
}
}
/// <summary>
/// Provides a way to represent a String that can contain variable or constant references.
/// </summary>
public class ExpandedString
{
public static readonly ExpandedString Empty = new ExpandedString();
public ExpandedString()
{
}
public ExpandedString(params ExpandedStringSegment[] segments)
{
foreach (ExpandedStringSegment segment in segments)
{
mvarSegments.Add(segment);
}
}
private ExpandedStringSegment.ExpandedStringSegmentCollection mvarSegments = new ExpandedStringSegment.ExpandedStringSegmentCollection();
public ExpandedStringSegment.ExpandedStringSegmentCollection Segments { get { return mvarSegments; } }
public override string ToString()
{
return mvarSegments.Count.ToString() + " segments";
}
public string ToString(ExpandedStringVariableStore variables)
{
StringBuilder sb = new StringBuilder();
foreach (ExpandedStringSegment segment in mvarSegments)
{
sb.Append(segment.ToString(variables));
}
return sb.ToString();
}
public static ExpandedString FromMarkup(MarkupTagElement markup)
{
ExpandedString value = new ExpandedString();
foreach (MarkupElement el in markup.Elements)
{
MarkupTagElement tag = (el as MarkupTagElement);
if (tag == null) continue;
switch (tag.FullName)
{
case "VariableReference":
case "ConstantReference":
{
MarkupAttribute attScope = tag.Attributes["Scope"];
ExpandedStringSegmentVariableScope scope = ExpandedStringSegmentVariableScope.None;
if (attScope != null)
{
switch (attScope.Value.ToLower())
{
case "file": scope = ExpandedStringSegmentVariableScope.File; break;
case "project": scope = ExpandedStringSegmentVariableScope.Project; break;
case "solution": scope = ExpandedStringSegmentVariableScope.Solution; break;
case "global": scope = ExpandedStringSegmentVariableScope.Global; break;
}
}
MarkupAttribute attID = tag.Attributes["ID"];
if (attID == null) continue;
string separator = ";";
MarkupAttribute attSeparator = tag.Attributes["Separator"];
if (attSeparator != null)
{
separator = attSeparator.Value;
}
if (tag.FullName == "VariableReference")
{
value.Segments.Add(new ExpandedStringSegmentVariable(attID.Value, scope, separator));
}
else if (tag.FullName == "ConstantReference")
{
value.Segments.Add(new ExpandedStringSegmentConstant(attID.Value, scope, separator));
}
break;
}
case "Literal":
{
MarkupAttribute attValue = tag.Attributes["Value"];
if (attValue == null) continue;
value.Segments.Add(new ExpandedStringSegmentLiteral(attValue.Value));
break;
}
}
}
return value;
}
}
public abstract class ExpandedStringSegment
{
public class ExpandedStringSegmentCollection
: System.Collections.ObjectModel.Collection<ExpandedStringSegment>
{
}
private static readonly ExpandedStringVariableStore _empty = new ExpandedStringVariableStore();
public abstract string ToString(ExpandedStringVariableStore variables);
public override string ToString()
{
return ToString(_empty);
}
}
public class ExpandedStringSegmentLiteral : ExpandedStringSegment
{
public ExpandedStringSegmentLiteral()
{
mvarValue = String.Empty;
}
public ExpandedStringSegmentLiteral(string value)
{
mvarValue = value;
}
private string mvarValue = String.Empty;
public string Value { get { return mvarValue; } set { mvarValue = value; } }
public override string ToString(ExpandedStringVariableStore variables)
{
return mvarValue;
}
}
/// <summary>
/// The scope of an <see cref="ExpandedStringSegmentVariable" />.
/// </summary>
public enum ExpandedStringSegmentVariableScope
{
/// <summary>
/// No scope is defined for this variable. Automatically searches for the variable in this scope order: first
/// file scope, then project scope, then solution scope, and finally global scope.
/// </summary>
None = 0,
/// <summary>
/// The variable is defined at the file level.
/// </summary>
File = 1,
/// <summary>
/// The variable is defined at the project level.
/// </summary>
Project = 2,
/// <summary>
/// The variable is defined at the solution level.
/// </summary>
Solution = 3,
/// <summary>
/// The variable is defined at the global level.
/// </summary>
Global = 4
}
/// <summary>
/// Represents a string segment whose value is populated by a variable.
/// </summary>
public class ExpandedStringSegmentVariable : ExpandedStringSegment
{
private string mvarVariableName = String.Empty;
/// <summary>
/// The name of the variable in the variables dictionary from which to retrieve the value for this <see cref="ExpandedStringSegment" />.
/// </summary>
public string VariableName { get { return mvarVariableName; } set { mvarVariableName = value; } }
private ExpandedStringSegmentVariableScope mvarScope = ExpandedStringSegmentVariableScope.None;
/// <summary>
/// The scope of the variable to search for, or <see cref="ExpandedStringSegmentVariableScope.None" /> to
/// search all scopes in the order of file, project, solution, global.
/// </summary>
public ExpandedStringSegmentVariableScope Scope { get { return mvarScope; } set { mvarScope = value; } }
private string mvarSeparator = String.Empty;
/// <summary>
/// The array separator to use when the value of the variable is an array.
/// </summary>
public string Separator { get { return mvarSeparator; } set { mvarSeparator = value; } }
/// <summary>
/// Creates a new instance of <see cref="ExpandedStringSegmentVariable" /> with the given variable name.
/// </summary>
/// <param name="variableName">
/// The name of the variable in the variables dictionary from which to retrieve the value for this
/// <see cref="ExpandedStringSegment" />.
/// </param>
/// <param name="scope">
/// The scope of the variable to search for, or <see cref="ExpandedStringSegmentVariableScope.None" /> to
/// search all scopes in the order of file, project, solution, global.
/// </param>
/// <param name="separator">The array separator to use when the value of the variable is an array.</param>
public ExpandedStringSegmentVariable(string variableName, ExpandedStringSegmentVariableScope scope = ExpandedStringSegmentVariableScope.None, string separator = null)
{
mvarVariableName = variableName;
mvarScope = scope;
mvarSeparator = separator;
}
/// <summary>
/// Returns the value of this <see cref="ExpandedStringSegment" /> with the given variables.
/// </summary>
/// <param name="variables">The variables to pass into the <see cref="ExpandedStringSegment" />.</param>
/// <returns>A <see cref="String" /> that contains the value of this <see cref="ExpandedStringSegment" />.</returns>
public override string ToString(ExpandedStringVariableStore variables)
{
ExpandedStringVariable varr = variables.Variables[mvarScope, mvarVariableName];
if (varr != null && varr.Value != null) return varr.Value.ToString();
return String.Empty;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("Variable: \"");
sb.Append(mvarVariableName);
sb.Append("\" (");
sb.Append(mvarScope.ToString());
sb.Append(" scope)");
if (!String.IsNullOrEmpty(mvarSeparator))
{
sb.Append(" (Array; separator=\"");
sb.Append(mvarSeparator);
sb.Append("\")");
}
return sb.ToString();
}
}
public class ExpandedStringSegmentConstant : ExpandedStringSegmentVariable
{
/// <summary>
/// Creates a new instance of <see cref="ExpandedStringSegmentVariable" /> with the given variable name.
/// </summary>
/// <param name="variableName">
/// The name of the variable in the variables dictionary from which to retrieve the value for this
/// <see cref="ExpandedStringSegment" />.
/// </param>
/// <param name="scope">
/// The scope of the variable to search for, or <see cref="ExpandedStringSegmentVariableScope.None" /> to
/// search all scopes in the order of file, project, solution, global.
/// </param>
/// <param name="separator">The array separator to use when the value of the variable is an array.</param>
public ExpandedStringSegmentConstant(string variableName, ExpandedStringSegmentVariableScope scope = ExpandedStringSegmentVariableScope.None, string separator = null)
: base(variableName, scope, separator)
{
}
public override string ToString(ExpandedStringVariableStore variables)
{
ExpandedStringVariable varr = variables.Constants[Scope, VariableName];
if (varr != null && varr.Value != null) return varr.Value.ToString();
return String.Empty;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("Constant: \"");
sb.Append(VariableName);
sb.Append("\" (");
sb.Append(Scope.ToString());
sb.Append(" scope)");
if (!String.IsNullOrEmpty(Separator))
{
sb.Append(" (Array; separator=\"");
sb.Append(Separator);
sb.Append("\")");
}
return sb.ToString();
}
}
}