486 lines
15 KiB
C#
486 lines
15 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace FairyGUI.Utils
|
|
{
|
|
public enum XMLTagType
|
|
{
|
|
Start,
|
|
End,
|
|
Void,
|
|
CDATA,
|
|
Comment,
|
|
Instruction
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class XMLIterator
|
|
{
|
|
public static string tagName;
|
|
public static XMLTagType tagType;
|
|
public static string lastTagName;
|
|
|
|
static string source;
|
|
static int sourceLen;
|
|
static int parsePos;
|
|
static int tagPos;
|
|
static int tagLength;
|
|
static int lastTagEnd;
|
|
static bool attrParsed;
|
|
static bool lowerCaseName;
|
|
static StringBuilder buffer = new StringBuilder();
|
|
static Dictionary<string, string> attributes = new Dictionary<string, string>();
|
|
|
|
const string CDATA_START = "<![CDATA[";
|
|
const string CDATA_END = "]]>";
|
|
const string COMMENT_START = "<!--";
|
|
const string COMMENT_END = "-->";
|
|
|
|
public static void Begin(string source, bool lowerCaseName = false)
|
|
{
|
|
XMLIterator.source = source;
|
|
XMLIterator.lowerCaseName = lowerCaseName;
|
|
sourceLen = source.Length;
|
|
parsePos = 0;
|
|
lastTagEnd = 0;
|
|
tagPos = 0;
|
|
tagLength = 0;
|
|
tagName = null;
|
|
}
|
|
|
|
public static bool NextTag()
|
|
{
|
|
int pos;
|
|
char c;
|
|
tagType = XMLTagType.Start;
|
|
buffer.Length = 0;
|
|
lastTagEnd = parsePos;
|
|
attrParsed = false;
|
|
lastTagName = tagName;
|
|
|
|
while ((pos = source.IndexOf('<', parsePos)) != -1)
|
|
{
|
|
parsePos = pos;
|
|
pos++;
|
|
|
|
if (pos == sourceLen)
|
|
break;
|
|
|
|
c = source[pos];
|
|
if (c == '!')
|
|
{
|
|
if (sourceLen > pos + 7 && source.Substring(pos - 1, 9) == CDATA_START)
|
|
{
|
|
pos = source.IndexOf(CDATA_END, pos);
|
|
tagType = XMLTagType.CDATA;
|
|
tagName = string.Empty;
|
|
tagPos = parsePos;
|
|
if (pos == -1)
|
|
tagLength = sourceLen - parsePos;
|
|
else
|
|
tagLength = pos + 3 - parsePos;
|
|
parsePos += tagLength;
|
|
return true;
|
|
}
|
|
else if (sourceLen > pos + 2 && source.Substring(pos - 1, 4) == COMMENT_START)
|
|
{
|
|
pos = source.IndexOf(COMMENT_END, pos);
|
|
tagType = XMLTagType.Comment;
|
|
tagName = string.Empty;
|
|
tagPos = parsePos;
|
|
if (pos == -1)
|
|
tagLength = sourceLen - parsePos;
|
|
else
|
|
tagLength = pos + 3 - parsePos;
|
|
parsePos += tagLength;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
pos++;
|
|
tagType = XMLTagType.Instruction;
|
|
}
|
|
}
|
|
else if (c == '/')
|
|
{
|
|
pos++;
|
|
tagType = XMLTagType.End;
|
|
}
|
|
else if (c == '?')
|
|
{
|
|
pos++;
|
|
tagType = XMLTagType.Instruction;
|
|
}
|
|
|
|
for (; pos < sourceLen; pos++)
|
|
{
|
|
c = source[pos];
|
|
if (Char.IsWhiteSpace(c) || c == '>' || c == '/')
|
|
break;
|
|
}
|
|
if (pos == sourceLen)
|
|
break;
|
|
|
|
buffer.Append(source, parsePos + 1, pos - parsePos - 1);
|
|
if (buffer.Length > 0 && buffer[0] == '/')
|
|
buffer.Remove(0, 1);
|
|
|
|
bool singleQuoted = false, doubleQuoted = false;
|
|
int possibleEnd = -1;
|
|
for (; pos < sourceLen; pos++)
|
|
{
|
|
c = source[pos];
|
|
if (c == '"')
|
|
{
|
|
if (!singleQuoted)
|
|
doubleQuoted = !doubleQuoted;
|
|
}
|
|
else if (c == '\'')
|
|
{
|
|
if (!doubleQuoted)
|
|
singleQuoted = !singleQuoted;
|
|
}
|
|
|
|
if (c == '>')
|
|
{
|
|
if (!(singleQuoted || doubleQuoted))
|
|
{
|
|
possibleEnd = -1;
|
|
break;
|
|
}
|
|
|
|
possibleEnd = pos;
|
|
}
|
|
else if (c == '<')
|
|
break;
|
|
}
|
|
if (possibleEnd != -1)
|
|
pos = possibleEnd;
|
|
|
|
if (pos == sourceLen)
|
|
break;
|
|
|
|
if (source[pos - 1] == '/')
|
|
tagType = XMLTagType.Void;
|
|
|
|
tagName = buffer.ToString();
|
|
if (lowerCaseName)
|
|
tagName = tagName.ToLower();
|
|
tagPos = parsePos;
|
|
tagLength = pos + 1 - parsePos;
|
|
parsePos += tagLength;
|
|
|
|
return true;
|
|
}
|
|
|
|
tagPos = sourceLen;
|
|
tagLength = 0;
|
|
tagName = null;
|
|
return false;
|
|
}
|
|
|
|
public static string GetTagSource()
|
|
{
|
|
return source.Substring(tagPos, tagLength);
|
|
}
|
|
|
|
public static string GetRawText(bool trim = false)
|
|
{
|
|
if (lastTagEnd == tagPos)
|
|
return string.Empty;
|
|
else if (trim)
|
|
{
|
|
int i = lastTagEnd;
|
|
for (; i < tagPos; i++)
|
|
{
|
|
char c = source[i];
|
|
if (!char.IsWhiteSpace(c))
|
|
break;
|
|
}
|
|
|
|
if (i == tagPos)
|
|
return string.Empty;
|
|
else
|
|
return source.Substring(i, tagPos - i).TrimEnd();
|
|
}
|
|
else
|
|
return source.Substring(lastTagEnd, tagPos - lastTagEnd);
|
|
}
|
|
|
|
public static string GetText(bool trim = false)
|
|
{
|
|
if (lastTagEnd == tagPos)
|
|
return string.Empty;
|
|
else if (trim)
|
|
{
|
|
int i = lastTagEnd;
|
|
for (; i < tagPos; i++)
|
|
{
|
|
char c = source[i];
|
|
if (!char.IsWhiteSpace(c))
|
|
break;
|
|
}
|
|
|
|
if (i == tagPos)
|
|
return string.Empty;
|
|
else
|
|
return XMLUtils.DecodeString(source.Substring(i, tagPos - i).TrimEnd());
|
|
}
|
|
else
|
|
return XMLUtils.DecodeString(source.Substring(lastTagEnd, tagPos - lastTagEnd));
|
|
}
|
|
|
|
public static bool HasAttribute(string attrName)
|
|
{
|
|
if (!attrParsed)
|
|
{
|
|
attributes.Clear();
|
|
ParseAttributes(attributes);
|
|
attrParsed = true;
|
|
}
|
|
|
|
return attributes.ContainsKey(attrName);
|
|
}
|
|
|
|
public static string GetAttribute(string attrName)
|
|
{
|
|
if (!attrParsed)
|
|
{
|
|
attributes.Clear();
|
|
ParseAttributes(attributes);
|
|
attrParsed = true;
|
|
}
|
|
|
|
string value;
|
|
if (attributes.TryGetValue(attrName, out value))
|
|
return value;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
public static string GetAttribute(string attrName, string defValue)
|
|
{
|
|
string ret = GetAttribute(attrName);
|
|
if (ret != null)
|
|
return ret;
|
|
else
|
|
return defValue;
|
|
}
|
|
|
|
public static int GetAttributeInt(string attrName)
|
|
{
|
|
return GetAttributeInt(attrName, 0);
|
|
}
|
|
|
|
public static int GetAttributeInt(string attrName, int defValue)
|
|
{
|
|
string value = GetAttribute(attrName);
|
|
if (value == null || value.Length == 0)
|
|
return defValue;
|
|
|
|
int ret;
|
|
if (int.TryParse(value, out ret))
|
|
return ret;
|
|
else
|
|
return defValue;
|
|
}
|
|
|
|
public static float GetAttributeFloat(string attrName)
|
|
{
|
|
return GetAttributeFloat(attrName, 0);
|
|
}
|
|
|
|
public static float GetAttributeFloat(string attrName, float defValue)
|
|
{
|
|
string value = GetAttribute(attrName);
|
|
if (value == null || value.Length == 0)
|
|
return defValue;
|
|
|
|
float ret;
|
|
if (float.TryParse(value, out ret))
|
|
return ret;
|
|
else
|
|
return defValue;
|
|
}
|
|
|
|
public static bool GetAttributeBool(string attrName)
|
|
{
|
|
return GetAttributeBool(attrName, false);
|
|
}
|
|
|
|
public static bool GetAttributeBool(string attrName, bool defValue)
|
|
{
|
|
string value = GetAttribute(attrName);
|
|
if (value == null || value.Length == 0)
|
|
return defValue;
|
|
|
|
bool ret;
|
|
if (bool.TryParse(value, out ret))
|
|
return ret;
|
|
else
|
|
return defValue;
|
|
}
|
|
|
|
public static Dictionary<string, string> GetAttributes(Dictionary<string, string> result)
|
|
{
|
|
if (result == null)
|
|
result = new Dictionary<string, string>();
|
|
|
|
if (attrParsed)
|
|
{
|
|
foreach (KeyValuePair<string, string> kv in attributes)
|
|
result[kv.Key] = kv.Value;
|
|
}
|
|
else //这里没有先ParseAttributes再赋值给result是为了节省复制的操作
|
|
ParseAttributes(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
public static Hashtable GetAttributes(Hashtable result)
|
|
{
|
|
if (result == null)
|
|
result = new Hashtable();
|
|
|
|
if (attrParsed)
|
|
{
|
|
foreach (KeyValuePair<string, string> kv in attributes)
|
|
result[kv.Key] = kv.Value;
|
|
}
|
|
else //这里没有先ParseAttributes再赋值给result是为了节省复制的操作
|
|
ParseAttributes(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void ParseAttributes(IDictionary attrs)
|
|
{
|
|
string attrName;
|
|
int valueStart;
|
|
int valueEnd;
|
|
bool waitValue = false;
|
|
int quoted;
|
|
buffer.Length = 0;
|
|
int i = tagPos;
|
|
int attrEnd = tagPos + tagLength;
|
|
|
|
if (i < attrEnd && source[i] == '<')
|
|
{
|
|
for (; i < attrEnd; i++)
|
|
{
|
|
char c = source[i];
|
|
if (Char.IsWhiteSpace(c) || c == '>' || c == '/')
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (; i < attrEnd; i++)
|
|
{
|
|
char c = source[i];
|
|
if (c == '=')
|
|
{
|
|
valueStart = -1;
|
|
valueEnd = -1;
|
|
quoted = 0;
|
|
for (int j = i + 1; j < attrEnd; j++)
|
|
{
|
|
char c2 = source[j];
|
|
if (Char.IsWhiteSpace(c2))
|
|
{
|
|
if (valueStart != -1 && quoted == 0)
|
|
{
|
|
valueEnd = j - 1;
|
|
break;
|
|
}
|
|
}
|
|
else if (c2 == '>')
|
|
{
|
|
if (quoted == 0)
|
|
{
|
|
valueEnd = j - 1;
|
|
break;
|
|
}
|
|
}
|
|
else if (c2 == '"')
|
|
{
|
|
if (valueStart != -1)
|
|
{
|
|
if (quoted != 1)
|
|
{
|
|
valueEnd = j - 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
quoted = 2;
|
|
valueStart = j + 1;
|
|
}
|
|
}
|
|
else if (c2 == '\'')
|
|
{
|
|
if (valueStart != -1)
|
|
{
|
|
if (quoted != 2)
|
|
{
|
|
valueEnd = j - 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
quoted = 1;
|
|
valueStart = j + 1;
|
|
}
|
|
}
|
|
else if (valueStart == -1)
|
|
{
|
|
valueStart = j;
|
|
}
|
|
}
|
|
|
|
if (valueStart != -1 && valueEnd != -1)
|
|
{
|
|
attrName = buffer.ToString();
|
|
if (lowerCaseName)
|
|
attrName = attrName.ToLower();
|
|
buffer.Length = 0;
|
|
attrs[attrName] = XMLUtils.DecodeString(source.Substring(valueStart, valueEnd - valueStart + 1));
|
|
i = valueEnd + 1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else if (!Char.IsWhiteSpace(c))
|
|
{
|
|
if (waitValue || c == '/' || c == '>')
|
|
{
|
|
if (buffer.Length > 0)
|
|
{
|
|
attrName = buffer.ToString();
|
|
if (lowerCaseName)
|
|
attrName = attrName.ToLower();
|
|
attrs[attrName] = string.Empty;
|
|
buffer.Length = 0;
|
|
}
|
|
|
|
waitValue = false;
|
|
}
|
|
|
|
if (c != '/' && c != '>')
|
|
buffer.Append(c);
|
|
}
|
|
else
|
|
{
|
|
if (buffer.Length > 0)
|
|
waitValue = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|