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