384 lines
13 KiB
C#
384 lines
13 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright company='Microsoft Corporation'>
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
// The RegexReplacement class represents a substitution string for
|
|
// use when using regexs to search/replace, etc. It's logically
|
|
// a sequence intermixed (1) constant strings and (2) group numbers.
|
|
//
|
|
|
|
namespace System.Text.RegularExpressions {
|
|
|
|
using System.Collections;
|
|
|
|
internal sealed class RegexReplacement {
|
|
// Since RegexReplacement shares the same parser as Regex,
|
|
// the constructor takes a RegexNode which is a concatenation
|
|
// of constant strings and backreferences.
|
|
internal RegexReplacement(String rep, RegexNode concat, Hashtable _caps) {
|
|
StringBuilder sb;
|
|
ArrayList strings;
|
|
ArrayList rules;
|
|
int slot;
|
|
|
|
_rep = rep;
|
|
|
|
if (concat.Type() != RegexNode.Concatenate)
|
|
throw new ArgumentException("Replacement Error");
|
|
//XXX: throw new ArgumentException(SR.GetString(SR.ReplacementError));
|
|
|
|
sb = new StringBuilder();
|
|
strings = new ArrayList();
|
|
rules = new ArrayList();
|
|
|
|
for (int i = 0; i < concat.ChildCount(); i++) {
|
|
RegexNode child = concat.Child(i);
|
|
|
|
switch (child.Type()) {
|
|
case RegexNode.Multi:
|
|
sb.Append(child._str);
|
|
break;
|
|
case RegexNode.One:
|
|
sb.Append(child._ch);
|
|
break;
|
|
case RegexNode.Ref:
|
|
if (sb.Length > 0) {
|
|
rules.Add(strings.Count);
|
|
strings.Add(sb.ToString());
|
|
sb.Length = 0;
|
|
}
|
|
slot = child._m;
|
|
|
|
if (_caps != null && slot >= 0)
|
|
slot = (int)_caps[slot];
|
|
|
|
rules.Add(-Specials - 1 - slot);
|
|
break;
|
|
default:
|
|
throw new ArgumentException("Replacement Error");//XXX: SR.GetString(SR.ReplacementError));
|
|
}
|
|
}
|
|
|
|
if (sb.Length > 0) {
|
|
rules.Add(strings.Count);
|
|
strings.Add(sb.ToString());
|
|
}
|
|
|
|
_strings = strings;
|
|
_rules = rules;
|
|
}
|
|
|
|
internal String _rep;
|
|
internal ArrayList _strings; // table of string constants
|
|
internal ArrayList _rules; // negative -> group #, positive -> string #
|
|
|
|
// constants for special insertion patterns
|
|
|
|
internal const int Specials = 4;
|
|
internal const int LeftPortion = -1;
|
|
internal const int RightPortion = -2;
|
|
internal const int LastGroup = -3;
|
|
internal const int WholeString = -4;
|
|
|
|
|
|
// Given a Match, emits into the StringBuilder the evaluated
|
|
// substitution pattern.
|
|
private void ReplacementImpl(StringBuilder sb, Match match) {
|
|
for (int i = 0; i < _rules.Count; i++) {
|
|
int r = (int) _rules[i];
|
|
if (r >= 0)
|
|
sb.Append((string) _strings[r]);
|
|
else if (r < -Specials)
|
|
sb.Append(match.GroupToStringImpl(-Specials - 1 - r));
|
|
else {
|
|
switch (-Specials - 1 - r) {
|
|
case LeftPortion:
|
|
sb.Append(match.GetLeftSubstring());
|
|
break;
|
|
case RightPortion:
|
|
sb.Append(match.GetRightSubstring());
|
|
break;
|
|
case LastGroup:
|
|
sb.Append(match.LastGroupToStringImpl());
|
|
break;
|
|
case WholeString:
|
|
sb.Append(match.GetOriginalString());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The original pattern string
|
|
internal String Pattern {
|
|
get {
|
|
return _rep;
|
|
}
|
|
}
|
|
|
|
// Returns the replacement result for a single match
|
|
internal String Replacement(Match match) {
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
ReplacementImpl(sb, match);
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
// Three very similar algorithms appear below: replace (pattern),
|
|
// replace (evaluator), and split.
|
|
|
|
|
|
// Replaces all occurrences of the regex in the string with the
|
|
// replacement pattern.
|
|
//
|
|
// Note that the special case of no matches is handled on its own:
|
|
// with no matches, the input string is returned unchanged.
|
|
// The right-to-left case is split out because StringBuilder
|
|
// doesn't handle right-to-left string building directly very well.
|
|
internal String Replace(Regex regex, String input, int count, int startat) {
|
|
Match match;
|
|
|
|
if (count < -1)
|
|
throw new ArgumentOutOfRangeException("count", "Count too Small");//XXX: SR.GetString(SR.CountTooSmall));
|
|
if (startat < 0 || startat > input.Length)
|
|
throw new ArgumentOutOfRangeException("startat", "Begin Index Not Negative");//SR.GetString(SR.BeginIndexNotNegative));
|
|
|
|
if (count == 0)
|
|
return input;
|
|
|
|
match = regex.Match(input, startat);
|
|
if (!match.Success) {
|
|
return input;
|
|
}
|
|
else {
|
|
StringBuilder sb;
|
|
|
|
if (!regex.RightToLeft) {
|
|
sb = new StringBuilder();
|
|
int prevat = 0;
|
|
|
|
do {
|
|
if (match.Index != prevat)
|
|
sb.Append(input, prevat, match.Index - prevat);
|
|
|
|
prevat = match.Index + match.Length;
|
|
ReplacementImpl(sb, match);
|
|
if (--count == 0)
|
|
break;
|
|
|
|
match = match.NextMatch();
|
|
} while (match.Success);
|
|
|
|
if (prevat < input.Length)
|
|
sb.Append(input, prevat, input.Length - prevat);
|
|
}
|
|
else {
|
|
ArrayList al = new ArrayList();
|
|
int prevat = input.Length;
|
|
|
|
do {
|
|
if (match.Index + match.Length != prevat)
|
|
al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
|
|
|
|
prevat = match.Index;
|
|
|
|
for (int i = _rules.Count - 1; i >= 0; i--) {
|
|
int r = (int) _rules[i];
|
|
if (r >= 0)
|
|
al.Add((string) _strings[r]);
|
|
else
|
|
al.Add(match.GroupToStringImpl(-Specials - 1 - r));
|
|
}
|
|
|
|
if (--count == 0)
|
|
break;
|
|
|
|
match = match.NextMatch();
|
|
} while (match.Success);
|
|
|
|
sb = new StringBuilder();
|
|
|
|
if (prevat > 0)
|
|
sb.Append(input, 0, prevat);
|
|
|
|
for (int i = al.Count - 1; i >= 0; i--) {
|
|
sb.Append((String)al[i]);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
|
|
// Replaces all occurrences of the regex in the string with the
|
|
// replacement evaluator.
|
|
//
|
|
// Note that the special case of no matches is handled on its own:
|
|
// with no matches, the input string is returned unchanged.
|
|
// The right-to-left case is split out because StringBuilder
|
|
// doesn't handle right-to-left string building directly very well.
|
|
internal static String Replace(MatchEvaluator evaluator, Regex regex,
|
|
String input, int count, int startat) {
|
|
Match match;
|
|
|
|
if (evaluator == null)
|
|
throw new ArgumentNullException("evaluator");
|
|
if (count < -1)
|
|
throw new ArgumentOutOfRangeException("count", ""); //XXX: SR.GetString(SR.CountTooSmall));
|
|
if (startat < 0 || startat > input.Length)
|
|
throw new ArgumentOutOfRangeException("startat", ""); //XXX: SR.GetString(SR.BeginIndexNotNegative));
|
|
|
|
if (count == 0)
|
|
return input;
|
|
|
|
match = regex.Match(input, startat);
|
|
|
|
if (!match.Success) {
|
|
return input;
|
|
}
|
|
else {
|
|
StringBuilder sb;
|
|
|
|
if (!regex.RightToLeft) {
|
|
sb = new StringBuilder();
|
|
int prevat = 0;
|
|
|
|
do {
|
|
if (match.Index != prevat)
|
|
sb.Append(input, prevat, match.Index - prevat);
|
|
|
|
prevat = match.Index + match.Length;
|
|
|
|
sb.Append(evaluator(match));
|
|
|
|
if (--count == 0)
|
|
break;
|
|
|
|
match = match.NextMatch();
|
|
} while (match.Success);
|
|
|
|
if (prevat < input.Length)
|
|
sb.Append(input, prevat, input.Length - prevat);
|
|
}
|
|
else {
|
|
ArrayList al = new ArrayList();
|
|
int prevat = input.Length;
|
|
|
|
do {
|
|
if (match.Index + match.Length != prevat)
|
|
al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
|
|
|
|
prevat = match.Index;
|
|
|
|
al.Add(evaluator(match));
|
|
|
|
if (--count == 0)
|
|
break;
|
|
|
|
match = match.NextMatch();
|
|
} while (match.Success);
|
|
|
|
sb = new StringBuilder();
|
|
|
|
if (prevat > 0)
|
|
sb.Append(input, 0, prevat);
|
|
|
|
for (int i = al.Count - 1; i >= 0; i--) {
|
|
sb.Append((String)al[i]);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
|
|
// Does a split. In the right-to-left case we reorder the
|
|
// array to be forwards.
|
|
internal static String[] Split(Regex regex, String input, int count, int startat) {
|
|
Match match;
|
|
String[] result;
|
|
|
|
if (count < 0)
|
|
throw new ArgumentOutOfRangeException("count", ""); // XXX: SR.GetString(SR.CountTooSmall));
|
|
|
|
if (startat < 0 || startat > input.Length)
|
|
throw new ArgumentOutOfRangeException("startat", ""); // XXX: SR.GetString(SR.BeginIndexNotNegative));
|
|
|
|
if (count == 1) {
|
|
result = new String[1];
|
|
result[0] = input;
|
|
return result;
|
|
}
|
|
|
|
count -= 1;
|
|
|
|
match = regex.Match(input, startat);
|
|
|
|
if (!match.Success) {
|
|
result = new String[1];
|
|
result[0] = input;
|
|
return result;
|
|
}
|
|
else {
|
|
ArrayList al = new ArrayList();
|
|
|
|
if (!regex.RightToLeft) {
|
|
int prevat = 0;
|
|
|
|
for (;;) {
|
|
al.Add(input.Substring(prevat, match.Index - prevat));
|
|
|
|
prevat = match.Index + match.Length;
|
|
|
|
// add all matched capture groups to the list.
|
|
int i = 1;
|
|
while (match.IsMatched(i)) {
|
|
al.Add(match.Groups[i].ToString());
|
|
i++;
|
|
}
|
|
|
|
if (--count == 0)
|
|
break;
|
|
|
|
match = match.NextMatch();
|
|
|
|
if (!match.Success)
|
|
break;
|
|
}
|
|
|
|
al.Add(input.Substring(prevat, input.Length - prevat));
|
|
}
|
|
else {
|
|
int prevat = input.Length;
|
|
|
|
for (;;) {
|
|
al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
|
|
|
|
prevat = match.Index;
|
|
|
|
if (--count == 0)
|
|
break;
|
|
|
|
match = match.NextMatch();
|
|
|
|
if (!match.Success)
|
|
break;
|
|
}
|
|
|
|
al.Add(input.Substring(0, prevat));
|
|
|
|
al.Reverse(0, al.Count);
|
|
}
|
|
|
|
return(String[])al.ToArray(typeof(String));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|