/* * 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; } } } }