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