Browse Source

Optimized StringParser.

Included PieceTableTextBufferStrategy from David McCloskey, but it's currently commented out.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@121 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 21 years ago
parent
commit
a17fed9eda
  1. 1
      src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj
  2. 321
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextBufferStrategy/PieceTableTextBufferStrategy.cs
  3. 3
      src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs
  4. 2
      src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs
  5. 59
      src/Main/Core/Project/Src/Services/StringParser/StringParser.cs
  6. 1
      src/Main/Core/Test/ICSharpCode.Core.Tests.csproj
  7. 82
      src/Main/Core/Test/StringParserTests.cs

1
src/Libraries/ICSharpCode.TextEditor/Project/ICSharpCode.TextEditor.csproj

@ -177,6 +177,7 @@ @@ -177,6 +177,7 @@
<EmbeddedResource Include="Resources\Tex-Mode.xshd" />
<EmbeddedResource Include="Resources\VBNET-Mode.xshd" />
<EmbeddedResource Include="Resources\XML-Mode.xshd" />
<Compile Include="Src\Document\TextBufferStrategy\PieceTableTextBufferStrategy.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project>

321
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextBufferStrategy/PieceTableTextBufferStrategy.cs

@ -0,0 +1,321 @@ @@ -0,0 +1,321 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David McCloskey" email="dave_a_mccloskey@hotmail.com"/>
// <version value="$version"/>
// </file>
using System;
using System.Collections;
namespace ICSharpCode.TextEditor.Document
{
// Currently not in use and doesn't compile because SetContent is not implemented.
// If anyone wants to use this, it should be easy to fix it.
/*
public class PieceTableTextBufferStrategy : ITextBufferStrategy
{
protected struct PieceTableDescriptor
{
public bool Buffer; // False for original, True for modified
public int Offset;
public int Length;
}
protected string OriginalBuffer = "";
protected string ModifiedBuffer = ""; // Use StringBuilder
// Use List<PieceTableDescriptor>
protected ArrayList Descriptors = new ArrayList();
public int Length
{
get
{
int length = 0;
for(int i = 0; i < Descriptors.Count; i++)
{
length += ((PieceTableDescriptor)Descriptors[i]).Length;
}
return length;
}
}
public void Insert(int offset, string text)
{
int Len = 0;
int CurrentDesc = 0;
while(true)
{
PieceTableDescriptor desc = (PieceTableDescriptor)Descriptors[CurrentDesc];
// Is the offset in this descriptor
if((Len + desc.Length) >= offset)
{
int gap = offset - Len;
int newoffset = ModifiedBuffer.Length;
// Add the text to the end of the buffer
ModifiedBuffer += text;
// Set up descriptor for the new text
PieceTableDescriptor newtext = new PieceTableDescriptor();
newtext.Offset = newoffset;
newtext.Length = text.Length;
newtext.Buffer = true;
// Is the offset in the middle of the descriptor
if(gap != 0 && gap != desc.Length)
{
int end = desc.Offset + desc.Length;
desc.Length = gap;
// Set up descriptor for the end of the current descriptor
PieceTableDescriptor newdesc = new PieceTableDescriptor();
newdesc.Offset = desc.Offset + desc.Length;
newdesc.Length = end - newdesc.Offset;
Descriptors[CurrentDesc] = desc;
Descriptors.Insert(CurrentDesc + 1, newtext);
Descriptors.Insert(CurrentDesc + 2, newdesc);
}
else if(gap == desc.Length) // Is it at the end
{
Descriptors.Insert(CurrentDesc + 1, newtext);
}
else // Is it at the beginning
{
Descriptors.Insert(CurrentDesc, newtext);
}
break;
}
else
{
CurrentDesc++;
Len += desc.Length;
if(CurrentDesc == Descriptors.Count)
break;
}
}
}
public void Remove(int offset, int length)
{
int Len = 0;
int CurrentDesc = 0;
while(true)
{
// Does the descriptor contain the offset
if((Len + ((PieceTableDescriptor)Descriptors[CurrentDesc]).Length) >= offset)
{
// Remove the text from the descriptor
RemoveInternal(CurrentDesc, Len, offset, length);
break;
}
else
{
CurrentDesc++;
Len += ((PieceTableDescriptor)Descriptors[CurrentDesc]).Length;
if(CurrentDesc == Descriptors.Count)
break;
}
}
}
protected void RemoveInternal(int descriptor, int lentodesc, int offset, int length)
{
PieceTableDescriptor desc = (PieceTableDescriptor)Descriptors[descriptor];
int gap = offset - lentodesc;
// Is all the text we want to remove span over multiple descriptors
if((offset + length) > (lentodesc + desc.Length))
{
lentodesc += desc.Length;
length -= lentodesc - offset;
offset = lentodesc;
desc.Length = gap;
// Does the text we want to remove encompass all of this descriptor
if(gap != 0)
{
Descriptors[descriptor] = desc;
RemoveInternal(descriptor + 1, lentodesc, offset, length);
}
else // It does encompass all of this descriptor so remove it
{
Descriptors.RemoveAt(descriptor);
RemoveInternal(descriptor, lentodesc, offset, length);
}
}
else
{
// Set up new descriptor to reflect removed text
PieceTableDescriptor newdesc = new PieceTableDescriptor();
newdesc.Buffer = desc.Buffer;
newdesc.Offset = desc.Offset + gap + length;
newdesc.Length = (desc.Offset + desc.Length) - newdesc.Offset;
desc.Length = gap;
// Does the text we want to remove encompass all of this descriptor
if(gap != 0)
{
Descriptors.Insert(descriptor + 1, newdesc);
Descriptors[descriptor] = desc;
}
else
{
// Instead of removing the old and inserting the new, just set the old to the new inside the array
Descriptors[descriptor] = newdesc;
}
}
}
public void Replace(int offset, int length, string text)
{
Remove(offset, length);
Insert(offset, text);
}
public void SetText(string text)
{
Descriptors.Clear();
ModifiedBuffer = "";
OriginalBuffer = text;
PieceTableDescriptor desc = new PieceTableDescriptor();
desc.Buffer = false;
desc.Offset = 0;
desc.Length = text.Length;
Descriptors.Add(desc);
}
public char GetCharAt(int offset)
{
int off = 0;
int currdesc = 0;
while(true)
{
PieceTableDescriptor desc = (PieceTableDescriptor)Descriptors[currdesc];
// Is the offset in the current descriptor
if((off + desc.Length) > offset)
{
// Find the difference between the beginning of the descriptor and the offset
int gap = offset - off;
if(desc.Buffer == false) // Original Buffer
{
return OriginalBuffer[desc.Offset + gap];
}
else // Modified Buffer
{
return ModifiedBuffer[desc.Offset + gap];
}
}
else
{
off += desc.Length;
}
currdesc++;
if(currdesc == Descriptors.Count) return '\0';
}
}
public string GetText(int offset, int length)
{
string text = "";
int off = 0;
int currdesc = 0;
while(true)
{
// Does the descriptor contain the offset
if((off + ((PieceTableDescriptor)Descriptors[currdesc]).Length) > offset)
{
// Get the text
text += GetTextInternal(currdesc, off, offset, length);
break;
}
else
{
currdesc++;
off += ((PieceTableDescriptor)Descriptors[currdesc]).Length;
if(currdesc == Descriptors.Count)
break;
}
}
return text;
}
protected string GetTextInternal(int descriptor, int lentodesc, int offset, int length)
{
PieceTableDescriptor desc = (PieceTableDescriptor)Descriptors[descriptor];
int gap = offset - lentodesc;
string text = "";
// Is the text we want greater than this descriptor
if((offset + length) > (lentodesc + desc.Length))
{
if(desc.Buffer)
{
text += ModifiedBuffer.Substring(desc.Offset + gap, desc.Length);
}
else
{
text += OriginalBuffer.Substring(desc.Offset + gap, desc.Length);
}
lentodesc += desc.Length;
length -= lentodesc - offset;
offset = lentodesc;
// Get the text from the next descriptor
text += GetTextInternal(descriptor + 1, lentodesc, offset, length);
}
else
{
// The text we want is in this descriptor so get it
if(desc.Buffer)
{
text += ModifiedBuffer.Substring(desc.Offset + gap, length);
}
else
{
text += OriginalBuffer.Substring(desc.Offset + gap, length);
}
}
return text;
}
}
*/
}

3
src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs

@ -50,6 +50,9 @@ namespace ICSharpCode.TextEditor.Document @@ -50,6 +50,9 @@ namespace ICSharpCode.TextEditor.Document
if (length == 0) {
return "";
}
if (offset == 0 && length >= storedText.Length) {
return storedText;
}
return storedText.Substring(offset, Math.Min(length, storedText.Length - offset));
}

2
src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs

@ -395,7 +395,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -395,7 +395,7 @@ namespace ICSharpCode.SharpDevelop.Gui
string[] subdirs = relPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
StringBuilder standardNameSpace = new StringBuilder(project.RootNamespace);
foreach(string subdir in subdirs) {
if (subdir == "." || subdir == ".." || subdir == "")
if (subdir == "." || subdir == ".." || subdir == "" || subdir.Equals("src", StringComparison.OrdinalIgnoreCase))
continue;
standardNameSpace.Append('.');
standardNameSpace.Append(GenerateValidClassName(subdir));

59
src/Main/Core/Project/Src/Services/StringParser/StringParser.cs

@ -76,13 +76,16 @@ namespace ICSharpCode.Core @@ -76,13 +76,16 @@ namespace ICSharpCode.Core
}
}
readonly static Regex pattern = new Regex(@"\$\{([^\}]*)\}", RegexOptions.Compiled | RegexOptions.CultureInvariant);
//readonly static Regex pattern = new Regex(@"\$\{([^\}]*)\}", RegexOptions.Compiled | RegexOptions.CultureInvariant);
/// <summary>
/// Expands ${xyz} style property values.
/// </summary>
public static string Parse(string input, string[,] customTags)
{
// Parse is a important method and should have good performance,
// so we don't use an expensive Regex here.
/*
string output = input;
if (input != null) {
foreach (Match m in pattern.Matches(input)) {
@ -103,6 +106,54 @@ namespace ICSharpCode.Core @@ -103,6 +106,54 @@ namespace ICSharpCode.Core
}
}
return output;
*/
if (input == null)
return null;
int pos = 0;
StringBuilder output = null;
do {
int oldPos = pos;
pos = input.IndexOf("${", pos);
if (pos < 0) {
if (output == null) {
return input;
} else {
if (oldPos < input.Length) {
// normal text after last property
output.Append(input, oldPos, input.Length - oldPos);
}
return output.ToString();
}
}
if (output == null) {
if (pos == 0)
output = new StringBuilder();
else
output = new StringBuilder(input, 0, pos, pos + 16);
} else {
if (pos > oldPos) {
// normal text between two properties
output.Append(input, oldPos, pos - oldPos);
}
}
int end = input.IndexOf('}', pos + 1);
if (end < 0) {
output.Append("${");
pos += 2;
} else {
string property = input.Substring(pos + 2, end - pos - 2);
string val = GetValue(property, customTags);
if (val == null) {
output.Append("${");
output.Append(property);
output.Append('}');
} else {
output.Append(val);
}
pos = end + 1;
}
} while (pos < input.Length);
return output.ToString();
}
static string GetValue(string propertyName, string[,] customTags)
@ -116,14 +167,14 @@ namespace ICSharpCode.Core @@ -116,14 +167,14 @@ namespace ICSharpCode.Core
return null;
}
}
if (propertyName.Equals("DATE", StringComparison.InvariantCultureIgnoreCase))
if (propertyName.Equals("DATE", StringComparison.OrdinalIgnoreCase))
return DateTime.Today.ToShortDateString();
if (propertyName.Equals("TIME", StringComparison.InvariantCultureIgnoreCase))
if (propertyName.Equals("TIME", StringComparison.OrdinalIgnoreCase))
return DateTime.Now.ToShortTimeString();
if (customTags != null) {
for (int j = 0; j < customTags.GetLength(0); ++j) {
if (propertyName.Equals(customTags[j, 0], StringComparison.InvariantCultureIgnoreCase)) {
if (propertyName.Equals(customTags[j, 0], StringComparison.OrdinalIgnoreCase)) {
return customTags[j, 1];
}
}

1
src/Main/Core/Test/ICSharpCode.Core.Tests.csproj

@ -43,6 +43,7 @@ @@ -43,6 +43,7 @@
<Compile Include="AddInTreeTests\AddInTreeLoadingTests.cs" />
<Compile Include="AddInTreeTests\ExtPathTests.cs" />
<Compile Include="TopologicalSortTest.cs" />
<Compile Include="StringParserTests.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="AddInTreeTests\" />

82
src/Main/Core/Test/StringParserTests.cs

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
/*
* Created by SharpDevelop.
* User: Daniel Grunwald
* Date: 05.05.2005
* Time: 22:16
*/
using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
namespace ICSharpCode.Core.Tests
{
[TestFixture]
public class StringParserTest
{
public StringParserTest()
{
StringParser.Properties["test"] = "Value";
StringParser.PropertyObjects["obj"] = this;
}
public string TestProperty {
get {
return "Hello!";
}
}
[Test]
public void SimpleProperty()
{
Assert.AreEqual("Value", StringParser.Parse("${test}"));
}
[Test]
public void CustomInput()
{
Assert.AreEqual("12", StringParser.Parse("${a}${b}", new string[,] {{"a", "1"}, {"b", "2"}}));
}
[Test]
public void CaseInsensitiveProperty()
{
Assert.AreEqual("Value", StringParser.Parse("${tEsT}"));
}
[Test]
public void TextOnly()
{
const string txt = "Text";
Assert.AreEqual(txt, StringParser.Parse(txt));
// reference should be same: StringParser should not do unnecessary allocations
Assert.AreSame(txt, StringParser.Parse(txt));
}
[Test]
public void Mixed()
{
Assert.AreEqual("aValueb", StringParser.Parse("a${test}b"));
}
[Test]
public void MultipleReplacements()
{
Assert.AreEqual("aValuebValuec", StringParser.Parse("a${test}b${test}c"));
}
[Test]
public void PropertyObject()
{
Assert.AreEqual("Hello!", StringParser.Parse("${obj:TestProperty}"));
}
[Test]
public void InvalidPropertyObject()
{
Assert.AreEqual("${invalidObj:TestProperty}", StringParser.Parse("${invalidObj:TestProperty}"));
Assert.AreEqual("${obj:InvalidProperty}", StringParser.Parse("${obj:InvalidProperty}"));
}
}
}
Loading…
Cancel
Save