You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
15 KiB
485 lines
15 KiB
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; |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|