فهرست منبع

throw a proper exception with context information instead of a dumb string in case of xml parsing errors

Sebastian Thomschke 9 سال پیش
والد
کامیت
7468270bf9
1فایلهای تغییر یافته به همراه73 افزوده شده و 47 حذف شده
  1. 73 47
      std/haxe/xml/Parser.hx

+ 73 - 47
std/haxe/xml/Parser.hx

@@ -46,13 +46,57 @@ extern private class S {
 	public static inline var ESCAPE			= 18;
 }
 
-class Position
+class XmlParserException
 {
-	public function new()
-	{}
-
-	public var line:Int = 1;
-	public var character:Int = 0;
+	/**
+	 * the XML parsing error message
+	 */
+	public var message:String;
+	
+	/**
+	 * the line number at which the XML parsing error occured
+	 */
+	public var lineNumber:Int;
+	
+	/**
+	 * the character position in the reported line at which the parsing error occured
+	 */
+	public var positionAtLine:Int;
+	
+	/**
+	 * the character position in the XML string at which the parsing error occured
+	 */
+	public var position:Int;
+	
+	/**
+	 * the invalid XML string
+	 */
+	public var xml:String;
+	
+	public function new(message:String, xml:String, position:Int)
+	{
+		this.xml = xml;
+		this.message = message;
+		this.position = position;
+		lineNumber = 1;
+		positionAtLine = 0;
+		
+		for( i in 0...position)
+		{
+			var c = xml.fastCodeAt(i);
+			if (c == '\n'.code) {
+				lineNumber++;
+				positionAtLine = 0;
+			} else {
+				if (c != '\r'.code) positionAtLine++;
+			}
+		}
+	}
+	
+	public function toString():String
+	{
+		return Type.getClassName(Type.getClass(this)) + ": " + message + " at line " + lineNumber + " char " + positionAtLine;
+	}
 }
 
 class Parser
@@ -68,8 +112,10 @@ class Parser
 	}
 
 	/**
-		Parses the String into an XML Document. Set strict parsing to true in order to enable a strict check of XML attributes and entities.
-	**/
+	 * Parses the String into an XML Document. Set strict parsing to true in order to enable a strict check of XML attributes and entities.
+	 * 
+	 * @throws haxe.xml.XmlParserException
+	 */
 	static public function parse(str:String, strict = false)
 	{
 		var doc = Xml.createDocument();
@@ -153,7 +199,7 @@ class Parser
 							{
 								p += 2;
 								if (str.substr(p, 6).toUpperCase() != "CDATA[")
-									throw "Expected <![CDATA[" + getPositionAsString(str, p);
+									throw new XmlParserException("Expected <![CDATA[", str, p);
 								p += 5;
 								state = S.CDATA;
 								start = p + 1;
@@ -161,13 +207,13 @@ class Parser
 							else if (str.fastCodeAt(p + 1) == 'D'.code || str.fastCodeAt(p + 1) == 'd'.code)
 							{
 								if(str.substr(p + 2, 6).toUpperCase() != "OCTYPE")
-									throw "Expected <!DOCTYPE" + getPositionAsString(str, p);
+									throw new XmlParserException("Expected <!DOCTYPE", str, p);
 								p += 8;
 								state = S.DOCTYPE;
 								start = p + 1;
 							}
 							else if( str.fastCodeAt(p + 1) != '-'.code || str.fastCodeAt(p + 2) != '-'.code )
-								throw "Expected <!--" + getPositionAsString(str, p);
+								throw new XmlParserException("Expected <!--", str, p);
 							else
 							{
 								p += 2;
@@ -179,7 +225,7 @@ class Parser
 							start = p;
 						case '/'.code:
 							if( parent == null )
-								throw "Expected node name" + getPositionAsString(str, p);
+								throw new XmlParserException("Expected node name", str, p);
 							start = p + 1;
 							state = S.IGNORE_SPACES;
 							next = S.CLOSE;
@@ -192,7 +238,7 @@ class Parser
 					if (!isValidChar(c))
 					{
 						if( p == start )
-							throw "Expected node name" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected node name", str, p);
 						xml = Xml.createElement(str.substr(start, p - start));
 						addChild(xml);
 						state = S.IGNORE_SPACES;
@@ -216,11 +262,11 @@ class Parser
 					{
 						var tmp;
 						if( start == p )
-							throw "Expected attribute name" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected attribute name", str, p);
 						tmp = str.substr(start,p-start);
 						aname = tmp;
 						if( xml.exists(aname) )
-							throw "Duplicate attribute [" + aname + "]" + getPositionAsString(str, p);
+							throw new XmlParserException("Duplicate attribute [" + aname + "]", str, p);
 						state = S.IGNORE_SPACES;
 						next = S.EQUALS;
 						continue;
@@ -232,7 +278,7 @@ class Parser
 							state = S.IGNORE_SPACES;
 							next = S.ATTVAL_BEGIN;
 						default:
-							throw "Expected =" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected =", str, p);
 					}
 				case S.ATTVAL_BEGIN:
 					switch(c)
@@ -243,7 +289,7 @@ class Parser
 							start = p + 1;
 							attrValQuote = c;
 						default:
-							throw "Expected \"" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected \"", str, p);
 					}
 				case S.ATTRIB_VAL:
 					switch (c) {
@@ -254,7 +300,7 @@ class Parser
 							start = p + 1;
 						case '>'.code | '<'.code if( strict ):
 							// HTML allows these in attributes values
-							throw "Invalid unescaped " + String.fromCharCode(c) + " in attribute value" + getPositionAsString(str, p);
+							throw new XmlParserException("Invalid unescaped " + String.fromCharCode(c) + " in attribute value", str, p);
 						case _ if (c == attrValQuote):
 							buf.addSub(str, start, p - start);
 							var val = buf.toString();
@@ -273,7 +319,7 @@ class Parser
 						case '>'.code:
 							state = S.BEGIN;
 						default :
-							throw "Expected >"  + getPositionAsString(str, p);
+							throw new XmlParserException("Expected >", str, p);
 					}
 				case S.WAIT_END_RET:
 					switch(c)
@@ -283,17 +329,17 @@ class Parser
 								parent.addChild(Xml.createPCData(""));
 							return p;
 						default :
-							throw "Expected >" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected >", str, p);
 					}
 				case S.CLOSE:
 					if (!isValidChar(c))
 					{
 						if( start == p )
-							throw "Expected node name" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected node name", str, p);
 
 						var v = str.substr(start,p - start);
 						if (v != parent.nodeName)
-							throw "Expected </" +parent.nodeName + ">" + getPositionAsString(str, p);
+							throw new XmlParserException("Expected </" +parent.nodeName + ">", str, p);
 
 						state = S.IGNORE_SPACES;
 						next = S.WAIT_END_RET;
@@ -348,13 +394,13 @@ class Parser
 									buf.addChar(0x80 | ((c >> 6) & 63));
 									buf.addChar(0x80 | (c & 63));
 								} else
-									throw "Cannot encode UTF8-char " + c + getPositionAsString(str, p);
+									throw new XmlParserException("Cannot encode UTF8-char " + c, str, p);
 							} else
 							#end
 							buf.addChar(c);
 						} else if (!escapes.exists(s)) {
 							if( strict )
-								throw 'Undefined entity: $s' + getPositionAsString(str, p);
+								throw new XmlParserException("Undefined entity: " + s, str, p);
 							buf.add('&$s;');
 						} else {
 							buf.add(escapes.get(s));
@@ -363,7 +409,7 @@ class Parser
 						state = escapeNext;
 					} else if (!isValidChar(c) && c != "#".code) {
 						if( strict )
-							throw 'Invalid character in entity: ' + String.fromCharCode(c) + getPositionAsString(str, p);
+							throw new XmlParserException("Invalid character in entity: " + String.fromCharCode(c), str, p);
 						buf.addChar("&".code);
 						buf.addSub(str, start, p - start);
 						p--;
@@ -396,30 +442,10 @@ class Parser
 			return p;
 		}
 
-		throw "Unexpected end" + getPositionAsString(str, p);
+		throw new XmlParserException("Unexpected end", str, p);
 	}
 
 	static inline function isValidChar(c) {
 		return (c >= 'a'.code && c <= 'z'.code) || (c >= 'A'.code && c <= 'Z'.code) || (c >= '0'.code && c <= '9'.code) || c == ':'.code || c == '.'.code || c == '_'.code || c == '-'.code;
 	}
-	
-	static function getPosition(str:String, p:Int):Position {
-		var pos = new Position();
-		for( i in 0...p)
-		{
-			var c = str.fastCodeAt(i);
-			if (c == '\n'.code) {
-				pos.line++;
-				pos.character = 0;
-			} else {
-				if (c != '\r'.code) pos.character++;
-			}
-		}
-		return pos;
-	}
-	
-	static function getPositionAsString(str:String, p:Int):String {
-		var pos = getPosition(str, p);
-		return " at line " + pos.line + " char " + pos.character;
-	}
-}
+}