Removing Regex usage

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2012-08-09 17:06:00 -07:00
parent 26faadbc64
commit 725f940e4f
5 changed files with 312 additions and 38 deletions

View File

@@ -1,4 +1,5 @@
using NUnit.Framework;
using System;
using NUnit.Framework;
using Orchard.Localization;
using Orchard.Utility.Extensions;
@@ -34,24 +35,24 @@ namespace Orchard.Tests.Utility.Extensions {
[Test]
public void Ellipsize_ShouldTuncateToTheExactNumber() {
const string toEllipsize = "Lorem ipsum";
Assert.That(toEllipsize.Ellipsize(2, ""), Is.StringMatching("Lo"));
Assert.That(toEllipsize.Ellipsize(1, ""), Is.StringMatching("L"));
Assert.That(toEllipsize.Ellipsize(0, ""), Is.StringMatching(""));
Assert.That(toEllipsize.Ellipsize(2, ""), Is.EqualTo("Lo"));
Assert.That(toEllipsize.Ellipsize(1, ""), Is.EqualTo("L"));
Assert.That(toEllipsize.Ellipsize(0, ""), Is.EqualTo(""));
}
[Test]
public void Ellipsize_TruncatedToWordBoundary() {
const string toEllipsize = "Lorem ipsum";
Assert.That(toEllipsize.Ellipsize(8, ""), Is.StringMatching("Lorem"));
Assert.That(toEllipsize.Ellipsize(6, ""), Is.StringMatching("Lorem"));
Assert.That(toEllipsize.Ellipsize(5, ""), Is.StringMatching("Lorem"));
Assert.That(toEllipsize.Ellipsize(4, ""), Is.StringMatching(""));
Assert.That(toEllipsize.Ellipsize(8, ""), Is.EqualTo("Lorem"));
Assert.That(toEllipsize.Ellipsize(6, ""), Is.EqualTo("Lorem"));
Assert.That(toEllipsize.Ellipsize(5, ""), Is.EqualTo("Lorem"));
Assert.That(toEllipsize.Ellipsize(4, ""), Is.EqualTo("Lore"));
}
[Test]
public void Ellipsize_LongStringTruncatedToNearestWord() {
const string toEllipsize = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed purus quis purus orci aliquam.";
Assert.That(toEllipsize.Ellipsize(45), Is.StringMatching("Lorem ipsum dolor sit amet, consectetur …"));
Assert.That(toEllipsize.Ellipsize(46), Is.StringMatching("Lorem ipsum dolor sit amet, consectetur …"));
}
[Test]
@@ -74,6 +75,12 @@ namespace Orchard.Tests.Utility.Extensions {
const string toEllipsize = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed purus quis purus orci aliquam.";
Assert.That(toEllipsize.Ellipsize(45, "........"), Is.StringMatching("Lorem ipsum dolor sit amet, consectetur........"));
}
[Test]
public void Ellipsize_WordBoundary() {
const string toEllipsize = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed purus quis purus orci aliquam.";
Assert.That(toEllipsize.Ellipsize(43, "..."), Is.StringMatching("Lorem ipsum dolor sit amet, consectet..."));
Assert.That(toEllipsize.Ellipsize(43, "...", true), Is.StringMatching("Lorem ipsum dolor sit amet, ..."));
}
[Test]
public void HtmlClassify_ValidReallySimpleClassNameReturnsSame() {
@@ -172,7 +179,7 @@ namespace Orchard.Tests.Utility.Extensions {
[Test]
public void ReplaceNewLinesWith_ReplaceCRLFWithHtmlPsAndCRLF() {
const string lotsOfLineFeeds = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\r\nMaecenas sed purus quis purus orci aliquam.";
Assert.That(lotsOfLineFeeds.ReplaceNewLinesWith(@"</p>$1<p>"), Is.StringMatching("Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>\r\n<p>Maecenas sed purus quis purus orci aliquam."));
Assert.That(lotsOfLineFeeds.ReplaceNewLinesWith(@"</p>{0}<p>"), Is.StringMatching("Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>\r\n<p>Maecenas sed purus quis purus orci aliquam."));
}
[Test]
public void ReplaceNewLinesWith_EmptyStringReturnsEmptyString() {
@@ -184,5 +191,86 @@ namespace Orchard.Tests.Utility.Extensions {
const string lotsOfLineFeeds = null;
Assert.That(lotsOfLineFeeds.ReplaceNewLinesWith("<br />"), Is.StringMatching(""));
}
[Test]
public void StripShouldRemoveStart() {
Assert.That("abc".Strip('a'), Is.StringMatching("bc"));
Assert.That("abc".Strip("ab".ToCharArray()), Is.StringMatching("c"));
}
[Test]
public void StripShouldRemoveInside() {
Assert.That("abc".Strip('b'), Is.StringMatching("ac"));
Assert.That("abc".Strip("abc".ToCharArray()), Is.StringMatching(""));
}
[Test]
public void StripShouldRemoveEnd() {
Assert.That("abc".Strip('c'), Is.StringMatching("ab"));
Assert.That("abc".Strip("bc".ToCharArray()), Is.StringMatching("a"));
}
[Test]
public void StripShouldReturnIfEmpty() {
Assert.That("".Strip('a'), Is.StringMatching(""));
Assert.That("a".Strip("".ToCharArray()), Is.StringMatching("a"));
}
[Test]
public void AnyShouldReturnTrueAtStart() {
Assert.That("abc".Any('a'), Is.True);
Assert.That("abc".Any("ab".ToCharArray()), Is.True);
}
[Test]
public void AnyShouldReturnTrueAtEnd() {
Assert.That("abc".Any('c'), Is.True);
Assert.That("abc".Any("bc".ToCharArray()), Is.True);
}
[Test]
public void AnyShouldReturnTrueAtMiddle() {
Assert.That("abc".Any('b'), Is.True);
Assert.That("abc".Any("abc".ToCharArray()), Is.True);
}
[Test]
public void AnyShouldReturnFalseIfNotPresent() {
Assert.That("abc".Any("".ToCharArray()), Is.False);
Assert.That("abc".Any("d".ToCharArray()), Is.False);
}
[Test]
public void AllShouldReturnTrueIfAllArePresent() {
Assert.That("abc".All("abc".ToCharArray()), Is.True);
Assert.That("abc".All("abcd".ToCharArray()), Is.True);
Assert.That("".All("a".ToCharArray()), Is.True);
Assert.That("abc".All("abcd".ToCharArray()), Is.True);
}
[Test]
public void AllShouldReturnFalseIfAnyIsNotPresent() {
Assert.That("abc".All("".ToCharArray()), Is.False);
Assert.That("abc".All("a".ToCharArray()), Is.False);
}
[Test]
public void TranslateShouldThrowException() {
Assert.Throws<ArgumentNullException>(() => "a".Translate("".ToCharArray(), "a".ToCharArray()));
Assert.Throws<ArgumentNullException>(() => "a".Translate("a".ToCharArray(), "".ToCharArray()));
}
[Test]
public void TranslateShouldReturnSource() {
Assert.That("a".Translate("".ToCharArray(), "".ToCharArray()), Is.StringMatching(""));
Assert.That("".Translate("abc".ToCharArray(), "abc".ToCharArray()), Is.StringMatching(""));
}
[Test]
public void TranslateShouldReplaceChars() {
Assert.That("abc".Translate("a".ToCharArray(), "d".ToCharArray()), Is.StringMatching("dbc"));
Assert.That("abc".Translate("d".ToCharArray(), "d".ToCharArray()), Is.StringMatching("abc"));
Assert.That("abc".Translate("abc".ToCharArray(), "def".ToCharArray()), Is.StringMatching("def"));
}
}
}

View File

@@ -11,7 +11,7 @@
<span class="when">@Display.CommentMetadata(ContentPart: comment)</span>
</h4>
</header>
<p class="text">@(new MvcHtmlString(Html.Encode(comment.Record.CommentText).ReplaceNewLinesWith("<br />$1")))</p>
<p class="text">@(new MvcHtmlString(Html.Encode(comment.Record.CommentText).Replace("\r\n", "<br />\r\n")))</p>
</article>
</li>
}

View File

@@ -9,6 +9,7 @@ using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.VirtualPath;
using Orchard.UI.Resources;
using Orchard.Utility.Extensions;
namespace Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy {
// discovers .css files and turns them into Style__<filename> shapes.
@@ -16,7 +17,7 @@ namespace Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy {
private readonly IExtensionManager _extensionManager;
private readonly ShellDescriptor _shellDescriptor;
private readonly IVirtualPathProvider _virtualPathProvider;
private static readonly Regex _safeName = new Regex(@"[/:?#\[\]@!&'()*+,;=\s\""<>\.\-_]+", RegexOptions.Compiled);
private static readonly char[] unsafeCharList = "/:?#[]@!&'()*+,;=\r\n\t\f\" <>.-_".ToCharArray();
public StylesheetBindingStrategy(IExtensionManager extensionManager, ShellDescriptor shellDescriptor, IVirtualPathProvider virtualPathProvider) {
_extensionManager = extensionManager;
@@ -27,7 +28,8 @@ namespace Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy {
private static string SafeName(string name) {
if (string.IsNullOrWhiteSpace(name))
return String.Empty;
return _safeName.Replace(name, String.Empty).ToLowerInvariant();
return name.Strip(unsafeCharList).ToLowerInvariant();
}
public static string GetAlternateShapeNameFromFileName(string fileName) {

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using Orchard.Caching;
using Orchard.Data;
@@ -83,11 +82,21 @@ namespace Orchard.Localization.Services {
// "<languagecode2>-<country/regioncode2>" or
// "<languagecode2>-<scripttag>-<country/regioncode2>"
public bool IsValidCulture(string cultureName) {
Regex cultureRegex = new Regex(@"\w{2}(-\w{2,})*");
if (cultureRegex.IsMatch(cultureName)) {
return true;
var segments = cultureName.Split('-');
if(segments.Length == 0) {
return false;
}
return false;
if (segments.Length > 3) {
return false;
}
if (segments.Any(s => s.Length < 2)) {
return false;
}
return true;
}
}
}

View File

@@ -1,23 +1,26 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Orchard.Localization;
namespace Orchard.Utility.Extensions {
public static class StringExtensions {
private static readonly Regex humps = new Regex("(?:^[a-zA-Z][^A-Z]*|[A-Z][^A-Z]*)");
private static readonly Regex safe = new Regex(@"[^_\-a-zA-Z\d]+");
public static string CamelFriendly(this string camel) {
if (String.IsNullOrWhiteSpace(camel))
return "";
var matches = humps.Matches(camel).OfType<Match>().Select(m => m.Value).ToArray();
return matches.Any()
? matches.Aggregate((a, b) => a + " " + b).TrimStart(' ')
: camel;
var sb = new StringBuilder(camel);
for (int i = camel.Length-1; i>=0; i--) {
var current = sb[i];
if('A' <= current && current <= 'Z') {
sb.Insert(i, ' ');
}
}
return sb.ToString();
}
public static string Ellipsize(this string text, int characterCount) {
@@ -31,12 +34,23 @@ namespace Orchard.Utility.Extensions {
if (characterCount < 0 || text.Length <= characterCount)
return text;
var trimmed = Regex.Replace(text.Substring(0, characterCount), @"\s+\S*$", "") ;
if(wordBoundary) {
trimmed = Regex.Replace(trimmed + ".", @"\W*\w*$", "");
// search beginning of word
var backup = characterCount;
while (characterCount > 0 && text[characterCount-1].IsLetter()) {
characterCount--;
}
// search previous word
while (characterCount > 0 && text[characterCount - 1].IsSpace()) {
characterCount--;
}
// if it was the last word, recover it, unless boundary is requested
if(characterCount == 0 && !wordBoundary) {
characterCount = backup;
}
var trimmed = text.Substring(0, characterCount);
return trimmed + ellipsis;
}
@@ -45,7 +59,27 @@ namespace Orchard.Utility.Extensions {
return "";
var friendlier = text.CamelFriendly();
return Regex.Replace(friendlier, @"[^a-zA-Z]+", m => m.Index == 0 ? "" : "-").ToLowerInvariant();
var result = new char[friendlier.Length];
var cursor = 0;
var previousIsNotLetter = false;
for (var i = 0; i < friendlier.Length; i++) {
char current = friendlier[i];
if (IsLetter(current)) {
if(previousIsNotLetter && i != 0) {
result[cursor++] = '-';
}
result[cursor++] = Char.ToLowerInvariant(current);
previousIsNotLetter = false;
}
else {
previousIsNotLetter = true;
}
}
return new string(result, 0, cursor);
}
public static LocalizedString OrDefault(this string text, LocalizedString defaultValue) {
@@ -55,16 +89,42 @@ namespace Orchard.Utility.Extensions {
}
public static string RemoveTags(this string html) {
return String.IsNullOrEmpty(html)
? ""
: Regex.Replace(html, "<[^<>]*>", "", RegexOptions.Singleline);
if (String.IsNullOrEmpty(html)) {
return String.Empty;
}
var result = new char[html.Length];
var cursor = 0;
var inside = false;
for (var i = 0; i < html.Length; i++) {
char current = html[i];
switch(current) {
case '<':
inside = true;
continue;
case '>':
inside = false;
continue;
}
if (!inside) {
result[cursor++] = current;
}
}
return new string(result, 0, cursor);
}
// not accounting for only \r (e.g. Apple OS 9 carriage return only new lines)
public static string ReplaceNewLinesWith(this string text, string replacement) {
return String.IsNullOrWhiteSpace(text)
? ""
: Regex.Replace(text, @"(\r?\n)", replacement, RegexOptions.Singleline);
? String.Empty
: text
.Replace("\r\n", "\r\r")
.Replace("\n", String.Format(replacement, "\r\n"))
.Replace("\r\r", String.Format(replacement, "\r\n"));
}
public static string ToHexString(this byte[] bytes) {
@@ -78,6 +138,7 @@ namespace Orchard.Utility.Extensions {
ToArray();
}
private static readonly char[] validSegmentChars = @"/?#[]@""^{}|`<>\t\r\n\f ".ToCharArray();
public static bool IsValidUrlSegment(this string segment) {
// valid isegment from rfc3987 - http://tools.ietf.org/html/rfc3987#page-8
// the relevant bits:
@@ -90,7 +151,7 @@ namespace Orchard.Utility.Extensions {
//
// rough blacklist regex == m/^[^/?#[]@"^{}|\s`<>]+$/ (leaving off % to keep the regex simple)
return Regex.IsMatch(segment, @"^[^/?#[\]@""^{}|`<>\s]+$");
return !segment.Any(validSegmentChars);
}
/// <summary>
@@ -104,7 +165,13 @@ namespace Orchard.Utility.Extensions {
return String.Empty;
name = RemoveDiacritics(name);
name = safe.Replace(name, String.Empty);
name = name.Strip(c =>
c != '_'
&& c != '-'
&& !c.IsLetter()
&& !Char.IsDigit(c)
);
name = name.Trim();
// don't allow non A-Z chars as first letter, as they are not allowed in prefixes
@@ -125,6 +192,9 @@ namespace Orchard.Utility.Extensions {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
public static bool IsSpace(this char c) {
return (c == '\r' || c == '\n' || c == '\t' || c == '\f' || c == ' ');
}
public static string RemoveDiacritics(string name) {
string stFormD = name.Normalize(NormalizationForm.FormD);
@@ -139,5 +209,110 @@ namespace Orchard.Utility.Extensions {
return (sb.ToString().Normalize(NormalizationForm.FormC));
}
public static string Strip(this string subject, params char[] stripped) {
if(stripped == null || stripped.Length == 0 || String.IsNullOrEmpty(subject)) {
return subject;
}
Array.Sort(stripped);
var result = new char[subject.Length];
var cursor = 0;
for (var i = 0; i < subject.Length; i++) {
char current = subject[i];
if (Array.BinarySearch(stripped, current) < 0) {
result[cursor++] = current;
}
}
return new string(result, 0, cursor);
}
public static string Strip(this string subject, Func<char, bool> predicate) {
var result = new char[subject.Length];
var cursor = 0;
for (var i = 0; i < subject.Length; i++) {
char current = subject[i];
if (!predicate(current)) {
result[cursor++] = current;
}
}
return new string(result, 0, cursor);
}
public static bool Any(this string subject, params char[] chars) {
if (string.IsNullOrEmpty(subject) || chars == null || chars.Length == 0) {
return false;
}
Array.Sort(chars);
for (var i = 0; i < subject.Length; i++) {
char current = subject[i];
if (Array.BinarySearch(chars, current) >= 0) {
return true;
}
}
return false;
}
public static bool All(this string subject, params char[] chars) {
if (string.IsNullOrEmpty(subject)) {
return true;
}
if(chars == null || chars.Length == 0) {
return false;
}
Array.Sort(chars);
for (var i = 0; i < subject.Length; i++) {
char current = subject[i];
if (Array.BinarySearch(chars, current) < 0) {
return false;
}
}
return true;
}
public static string Translate(this string subject, char[] from, char[] to) {
if (string.IsNullOrEmpty(subject)) {
return subject;
}
if (from == null || to == null) {
throw new ArgumentNullException();
}
if (from.Length != to.Length) {
throw new ArgumentNullException("from", "Parameters must have the same length");
}
var map = new Dictionary<char, char>(from.Length);
for (var i = 0; i < from.Length; i++) {
map[from[i]] = to[i];
}
var result = new char[subject.Length];
for (var i = 0; i < subject.Length; i++) {
var current = subject[i];
if (map.ContainsKey(current)) {
result[i] = map[current];
}
else {
result[i] = current;
}
}
return new string(result);
}
}
}