using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace FairyGUI.Utils { public enum XMLTagType { Start, End, Void, CDATA, Comment, Instruction } /// /// /// 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 attributes = new Dictionary(); const string CDATA_START = ""; const string COMMENT_START = ""; 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 GetAttributes(Dictionary result) { if (result == null) result = new Dictionary(); if (attrParsed) { foreach (KeyValuePair 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 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; } } } } }