Files
PDFPatcher/App/Model/PdfPath/PathCompiler.cs
T
2021-12-24 20:35:03 +08:00

105 lines
2.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PDFPatcher.Model.PdfPath
{
class PathCompiler
{
const char PathSeparator = '/';
const char SelfChar = '.';
const char UniversalName = '*';
const char StartPredicate = '[';
const char EndPredicate = ']';
static readonly char[] __PredicateChars = new char[] { StartPredicate, EndPredicate };
sealed class Context
{
internal bool CanBeRoot { get; set; }
internal PathAxisType Axis { get; set; }
}
public static IEnumerable<IPathExpression> Compile (string path) {
var r = new Queue<IPathExpression> ();
if (String.IsNullOrEmpty (path)) {
return r.ToArray ();
}
var l = path.Length;
var i = 0;
string n;
var ctx = new Context();
while (i < l) {
ctx.Axis = ExtractAxis (path, l, ctx.CanBeRoot, ref i);
if (ctx.Axis == PathAxisType.Root) {
ctx.CanBeRoot = false;
r.Enqueue (new PathExpression (ctx.Axis));
continue;
}
n = ExtractName (path, l, ref i);
r.Enqueue (new PathExpression (ctx.Axis, n));
}
return r.ToArray ();
}
private static PathAxisType ExtractAxis (string path, int length, bool canBeRoot, ref int index) {
char c = path[index];
if (__PredicateChars.Contains (c)) {
throw new FormatException ("“[]”筛选表达式前缺少节点轴及节点名称标识。");
}
if (c == PathSeparator) {
if (MatchNextChar (path, length, index, PathSeparator)) {
++index;
return PathAxisType.Decendants;
}
else if (canBeRoot) {
return PathAxisType.Root;
}
else {
return PathAxisType.Children;
}
}
else if (c == SelfChar) {
if (MatchNextChar (path, length, index, SelfChar)) {
++index;
return PathAxisType.Parent;
}
else {
return PathAxisType.None;
}
}
else {
return PathAxisType.Children;
}
}
private static string ExtractName (string path, int length, ref int index) {
char c = path[index];
if (__PredicateChars.Contains (c)) {
throw new FormatException ("“[]”筛选表达式前缺少节点名称。");
}
if (c == UniversalName) {
return null;
}
var n = new List<char> ();
while (Char.IsLetter (c) || n.Count > 0 && Char.IsLetterOrDigit (c)) {
n.Add (c);
++index;
if (index < length) {
c = path[index];
}
else {
break;
}
}
return n.Count > 0 ? new String (n.ToArray ()) : null;
}
private static bool MatchNextChar (string path, int length, int index, char ch) {
return index + 1 < length && path[index + 1] == ch;
}
}
}