/*
* Copyright (C) 2013 FooProject
* * 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 .
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using Slusser.Collections.Generic;
namespace FooEditEngine
{
///
/// ランダムアクセス可能な列挙子を提供するインターフェイス
///
///
public interface IRandomEnumrator
{
///
/// インデクサーを表す
///
/// インデックス
/// Tを返す
T this[int index]{get;}
}
sealed class StringBuffer : IEnumerable, IRandomEnumrator
{
GapBuffer buf = new GapBuffer();
public StringBuffer()
{
this.Update += (s, e) => { };
}
public StringBuffer(StringBuffer buffer)
: this()
{
buf.AddRange(buffer.buf);
}
public char this[int index]
{
get
{
char c = buf[index];
return c;
}
}
public string ToString(int index, int length)
{
StringBuilder temp = new StringBuilder();
temp.Clear();
for (int i = index; i < index + length; i++)
temp.Append(buf[i]);
return temp.ToString();
}
public IEnumerable GetLines(int startIndex, int endIndex, int maxCharCount = -1)
{
foreach (Tuple range in this.ForEachLines(startIndex, endIndex, maxCharCount))
{
StringBuilder temp = new StringBuilder();
temp.Clear();
int lineEndIndex = range.Item1;
if (range.Item2 > 0)
lineEndIndex += range.Item2 - 1;
for (int i = range.Item1; i <= lineEndIndex; i++)
temp.Append(buf[i]);
yield return temp.ToString();
}
}
public IEnumerable> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
{
int currentLineHeadIndex = startIndex;
int currentLineLength = 0;
for (int i = startIndex; i <= endIndex; i++)
{
currentLineLength++;
char c = this.buf[i];
if (c == Document.NewLine ||
(maxCharCount != -1 && currentLineLength >= maxCharCount))
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
if (uc != UnicodeCategory.NonSpacingMark &&
uc != UnicodeCategory.SpacingCombiningMark &&
uc != UnicodeCategory.EnclosingMark &&
uc != UnicodeCategory.Surrogate)
{
yield return new Tuple(currentLineHeadIndex, currentLineLength);
currentLineHeadIndex += currentLineLength;
currentLineLength = 0;
}
}
}
if (currentLineLength > 0)
yield return new Tuple(currentLineHeadIndex, currentLineLength);
}
public int Length
{
get { return this.buf.Count; }
}
internal event DocumentUpdateEventHandler Update;
internal void Replace(StringBuffer buf)
{
this.Replace(buf.buf);
}
internal void Replace(GapBuffer buf)
{
this.Clear();
this.buf = buf;
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
}
internal void Replace(int index, int length, IEnumerable chars,int count)
{
if (length > 0)
this.buf.RemoveRange(index, length);
this.buf.InsertRange(index, chars,count);
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, index, length, count));
}
internal void Replace(string target, string pattern,bool ci = false)
{
TextSearch ts = new TextSearch(target,ci);
int left = 0, right = 0;
char[] pattern_chars = pattern.ToCharArray();
while((right = ts.IndexOf(this.buf,left)) != -1)
{
this.buf.RemoveRange(right, target.Length);
this.buf.InsertRange(right, pattern_chars, pattern.Length);
left = right + pattern.Length;
}
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
}
internal int IndexOf(string target, int start,bool ci = false)
{
TextSearch ts = new TextSearch(target,ci);
return ts.IndexOf(this.buf, start);
}
///
/// 文字列を削除する
///
internal void Clear()
{
this.buf.Clear();
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, 0, this.buf.Count,0));
}
internal IEnumerable GetEnumerator(int start, int length)
{
for (int i = start; i < start + length; i++)
yield return this.buf[i];
}
#region IEnumerable メンバー
public IEnumerator GetEnumerator()
{
for (int i = 0; i < this.Length; i++)
yield return this.buf[i];
}
#endregion
#region IEnumerable メンバー
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
for (int i = 0; i < this.Length; i++)
yield return this[i];
}
#endregion
}
sealed class TextSearch
{
char[] pattern;
int patternLength;
Dictionary qsTable = new Dictionary();
bool caseInsenstive;
public TextSearch(string pattern,bool ci = false)
{
this.patternLength = pattern.Length;
this.caseInsenstive = ci;
if (ci)
{
this.CreateQSTable(pattern.ToLower());
this.CreateQSTable(pattern.ToUpper());
this.pattern = new char[pattern.Length];
for (int i = 0; i < pattern.Length; i++)
this.pattern[i] = CharTool.ToUpperFastIf(pattern[i]);
}
else
{
this.CreateQSTable(pattern);
this.pattern = pattern.ToCharArray();
}
}
void CreateQSTable(string pattern)
{
int len = pattern.Length;
for (int i = 0; i < len; i++)
{
if (!this.qsTable.ContainsKey(pattern[i]))
this.qsTable.Add(pattern[i], len - i);
else
this.qsTable[pattern[i]] = len - i;
}
}
public int IndexOf(GapBuffer buf, int start)
{
//QuickSearch法
int buflen = buf.Count - 1;
int plen = this.patternLength;
int i = start;
int end = buf.Count - plen;
//最適化のためわざとコピペした
if (this.caseInsenstive)
{
while (i <= end)
{
int j = 0;
while (j < plen)
{
if (CharTool.ToUpperFastIf(buf[i + j]) != this.pattern[j])
break;
j++;
}
if (j == plen)
{
return i;
}
else
{
int k = i + plen;
if (k <= buflen) //buffer以降にアクセスする可能性がある
{
int moveDelta;
if (this.qsTable.TryGetValue(buf[k], out moveDelta))
i += moveDelta;
else
i += plen;
}
else
{
break;
}
}
}
}
else
{
while (i <= end)
{
int j = 0;
while (j < plen)
{
if (buf[i + j] != this.pattern[j])
break;
j++;
}
if (j == plen)
{
return i;
}
else
{
int k = i + plen;
if (k <= buflen) //buffer以降にアクセスする可能性がある
{
int moveDelta;
if (this.qsTable.TryGetValue(buf[k], out moveDelta))
i += moveDelta;
else
i += plen;
}
else
{
break;
}
}
}
}
return -1;
}
}
static class CharTool
{
///
/// Converts characters to lowercase.
///
const string _lookupStringL =
"---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-";
///
/// Converts characters to uppercase.
///
const string _lookupStringU =
"---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-";
///
/// Get lowercase version of this ASCII character.
///
public static char ToLower(char c)
{
return _lookupStringL[c];
}
///
/// Get uppercase version of this ASCII character.
///
public static char ToUpper(char c)
{
return _lookupStringU[c];
}
///
/// Translate uppercase ASCII characters to lowercase.
///
public static char ToLowerFastIf(char c)
{
if (c >= 'A' && c <= 'Z')
{
return (char)(c + 32);
}
else
{
return c;
}
}
///
/// Translate lowercase ASCII characters to uppercase.
///
public static char ToUpperFastIf(char c)
{
if (c >= 'a' && c <= 'z')
{
return (char)(c - 32);
}
else
{
return c;
}
}
}
}