You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by gr...@apache.org on 2019/05/19 06:47:13 UTC
[royale-asjs] 01/05: Numerous updates to XML/XMLList to address
issues identified during testing
This is an automated email from the ASF dual-hosted git repository.
gregdove pushed a commit to branch improvements/Language
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
commit 722f16599441be4fd0243d6fe9f2cd856f1a6b0b
Author: greg-dove <gr...@gmail.com>
AuthorDate: Wed May 15 10:54:34 2019 +1200
Numerous updates to XML/XMLList to address issues identified during testing
---
frameworks/projects/XML/src/main/royale/XML.as | 258 ++++++++++++++-------
frameworks/projects/XML/src/main/royale/XMLList.as | 43 +++-
2 files changed, 206 insertions(+), 95 deletions(-)
diff --git a/frameworks/projects/XML/src/main/royale/XML.as b/frameworks/projects/XML/src/main/royale/XML.as
index e6ea42e..01229c2 100644
--- a/frameworks/projects/XML/src/main/royale/XML.as
+++ b/frameworks/projects/XML/src/main/royale/XML.as
@@ -19,6 +19,9 @@
package
{
COMPILE::JS
+ /**
+ * @royaleignorepublicvarwarning
+ */
public class XML
{
import org.apache.royale.debugging.assert;
@@ -127,7 +130,6 @@ package
static private function escapeAttributeValue(value:String):String
{
- var outArr:Array = [];
var arr:Array = String(value).split("");
var len:int = arr.length;
for(var i:int=0;i<len;i++)
@@ -135,61 +137,55 @@ package
switch(arr[i])
{
case "<":
- outArr[i] = "<";
+ arr[i] = "<";
break;
case "&":
- if(arr[i+1] == "#")
- outArr[i] = "&";
- else
- outArr[i] = "&";
+ if(arr[i+1] != "#")
+ arr[i] = "&";
break;
case '"':
- outArr[i] = """;
+ arr[i] = """;
break;
case "\u000A":
- outArr[i] = "
";
+ arr[i] = "
";
break;
case "\u000D":
- outArr[i] = "
";
+ arr[i] = "
";
break;
case "\u0009":
- outArr[i] = "	";
+ arr[i] = "	";
break;
default:
- outArr[i] = arr[i];
break;
}
}
- return outArr.join("");
+ return arr.join("");
}
static private function escapeElementValue(value:String):String
{
var i:int;
- var outArr:Array = [];
var arr:Array = value.split("");
- for(i=0;i<arr.length;i++)
+ const len:uint = arr.length;
+ for(i=0;i<len;i++)
{
switch(arr[i])
{
case "<":
- outArr[i] = "<";
+ arr[i] = "<";
break;
case ">":
- outArr[i] = ">";
+ arr[i] = ">";
break;
case "&":
- if(arr[i+1] == "#")
- outArr[i] = "&";
- else
- outArr[i] = "&";
+ if(arr[i+1] != "#")
+ arr[i] = "&";
break;
default:
- outArr[i] = arr[i];
break;
}
}
- return outArr.join("");
+ return arr.join("");
}
static private function insertAttribute(att:Attr,parent:XML):XML
@@ -208,9 +204,15 @@ package
// add attributes
var attrs:* = node.attributes;
var len:int = node.attributes.length;
+
for(i=0;i<len;i++)
{
- insertAttribute(attrs[i],xml);
+ var att:Attr = attrs[i];
+ if (att.prefix == 'xmlns' && att.namespaceURI == 'http://www.w3.org/2000/xmlns/') {
+ //from e4x spec: NOTE Although namespaces are declared using attribute syntax in XML, they are not represented in the [[Attributes]] property.
+ xml.addNamespace(new Namespace(att.localName, att.nodeValue));
+ }
+ else insertAttribute(att,xml);
}
// loop through childNodes which will be one of:
// text, cdata, processing instrution or comment and add them as children of the element
@@ -218,18 +220,24 @@ package
len = childNodes.length;
for(i=0;i<len;i++)
{
- var child:XML = fromNode(childNodes[i]);
- xml.addChildInternal(child);
+ var nativeNode:Node = childNodes[i];
+ if (nativeNode.nodeType == 7 && XML.ignoreProcessingInstructions) {
+ continue;
+ }
+ var child:XML = fromNode(nativeNode);
+ if (child)
+ xml.addChildInternal(child);
}
}
/**
* returns an XML object from an existing node without the need to parse the XML.
* The new XML object is not normalized
+ *
+ * @royaleignorecoercion Element
*/
- static private function fromNode(node:Element):XML
+ static private function fromNode(node:Node):XML
{
var xml:XML;
- var i:int;
var data:* = node.nodeValue;
var localName:String = node.nodeName;
var prefix:String = node.prefix;
@@ -245,22 +253,24 @@ package
xml = new XML();
xml.setNodeKind("element");
xml.setName(qname);
- iterateElement(node,xml);
+ iterateElement(node as Element,xml);
break;
//case 2:break;// ATTRIBUTE_NODE (handled separately)
case 3:
//TEXT_NODE
+ if (XML.ignoreWhitespace) {
+ data = data.trim();
+ if (!data) return null;
+ }
xml = new XML();
xml.setNodeKind("text");
- xml.setName(qname);
- if(XML.ignoreWhitespace)
- data = data.trim();
+ //xml.setName(qname); e4X: the name must be null (text node rules)
xml.setValue(data);
break;
case 4:
//CDATA_SECTION_NODE
xml = new XML();
- xml.setName(qname);
+ //xml.setName(qname); e4X: the name must be null (text node rules)
xml.setNodeKind("text");
data = "<![CDATA[" + data + "]]>";
xml.setValue(data);
@@ -276,8 +286,10 @@ package
break;
case 8:
//COMMENT_NODE
+
xml = new XML();
xml.setNodeKind("comment");
+ //e4X: the name must be null (comment node rules)
xml.setValue(data);
break;
//case 9:break;//DOCUMENT_NODE
@@ -398,7 +410,7 @@ package
// _children = [];
if(xml)
{
- var xmlStr:String = "" + xml;
+ var xmlStr:String = ignoreWhitespace ? trimXMLWhitespace("" + xml) : "" + xml;
if(xmlStr.indexOf("<") == -1)
{
_nodeKind = "text";
@@ -418,11 +430,15 @@ package
configurable: true
}
);
-
}
private static var xmlRegEx:RegExp = /&(?![\w]+;)/g;
private static var parser:DOMParser;
private static var errorNS:String;
+
+ /**
+ *
+ * @royaleignorecoercion Element
+ */
private function parseXMLStr(xml:String):void
{
//escape ampersands
@@ -440,6 +456,10 @@ package
errorNS = "na";
}
}
+ //various node types not supported directly
+ //maybe it should always wrap (e4x seems to say yes on p34, 'Semantics' of e4x-Ecma-357.pdf)
+ var wrap:Boolean = (xml.indexOf('<?') == 0 && xml.indexOf('<?xml ') != 0) || (xml.indexOf('<![CDATA[') == 0) || (xml.indexOf('<!--') == 0);
+ if (wrap) xml = '<parseRoot>'+xml+'</parseRoot>';
try
{
var doc:Document = parser.parseFromString(xml, "application/xml");
@@ -453,9 +473,10 @@ package
var errorNodes:NodeList = doc.getElementsByTagNameNS(errorNS, 'parsererror');
if(errorNodes.length > 0)
throw new Error(errorNodes[0].innerHTML);
+ if (wrap) doc = doc.childNodes[0];
for(var i:int=0;i<doc.childNodes.length;i++)
{
- var node:Element = doc.childNodes[i];
+ var node:Node = doc.childNodes[i];
if(node.nodeType == 1)
{
_version = doc.xmlVersion;
@@ -465,16 +486,52 @@ package
// _name.prefix = node.prefix;
// _name.uri = node.namespaceURI;
// _name.localName = node.localName;
- iterateElement(node,this);
+ iterateElement(node as Element,this);
}
else
{
+ if (node.nodeType == 7) {
+ if (XML.ignoreProcessingInstructions) {
+ this.setNodeKind('text');
+ //e4x: The value of the [[Name]] property is null if and only if the XML object represents an XML comment or text node
+ _name = null;
+ this.setValue('');
+ } else {
+ this.setNodeKind('processing-instruction');
+ this.setName(node.nodeName);
+ this.setValue(node.nodeValue);
+ }
+
+ } else if (node.nodeType == 4) {
+ this.setNodeKind('text');
+ //e4x: The value of the [[Name]] property is null if and only if the XML object represents an XML comment or text node
+ _name = null;
+ if (node.nodeName == '#cdata-section') {
+ this.setValue('<![CDATA[' + node.nodeValue + ']]>');
+ } else {
+ this.setValue(node.nodeValue);
+ }
+ } else if (node.nodeType == 8) {
+ //e4x: The value of the [[Name]] property is null if and only if the XML object represents an XML comment or text node
+ _name = null;
+ if (XML.ignoreComments) {
+ this.setNodeKind('text');
+ this.setValue('');
+ } else {
+ this.setNodeKind('comment');
+ this.setValue(node.nodeValue);
+ }
+
+
+ }
+
// Do we record the nodes which are probably processing instructions?
// var child:XML = XML.fromNode(node);
// addChild(child);
}
}
- normalize();
+ //normalize seems wrong here:
+ //normalize();
//need to deal with errors https://bugzilla.mozilla.org/show_bug.cgi?id=45566
// get rid of nodes we do not want
//loop through the child nodes and build XML obejcts for each.
@@ -499,8 +556,6 @@ package
return _namespaces;
}
- private var _origStr:String;
-
/**
* @private
@@ -614,34 +669,38 @@ package
* @param child
* @return
*
+ * @royaleignorecoercion XML
+ *
*/
public function appendChild(child:*):XML
{
/*
- [[Insert]] (P, V)
- 1. If x.[[Class]] ∈ {"text", "comment", "processing-instruction", "attribute"}, return
- 2. Let i = ToUint32(P)
- 3. If (ToString(i) is not equal to P), throw a TypeError exception
- 4. If Type(V) is XML and (V is x or an ancestor of x) throw an Error exception
- 5. Let n = 1
- 6. If Type(V) is XMLList, let n = V.[[Length]]
- 7. If n == 0, Return
- 8. For j = x.[[Length]]-1 downto i, rename property ToString(j) of x to ToString(j + n)
- 9. Let x.[[Length]] = x.[[Length]] + n
- 10. If Type(V) is XMLList
- a. For j = 0 to V.[[Length-1]]
- i. V[j].[[Parent]] = x
- ii. x[i + j] = V[j]
- 11. Else
- a. Call the [[Replace]] method of x with arguments i and V
- 12. Return
+ 1. Let children be the result of calling the [[Get]] method of x with argument "*"
+ 2. Call the [[Put]] method of children with arguments children.[[Length]] and child
+ 3. Return x
+
*/
var childType:String = typeof child;
- if(childType != "object")
- child = xmlFromStringable(child);
+
+ if(childType != "object") {
+ var last:uint = childrenLength();
+ const lastChild:XML = last ? _children[last-1] : null;
+ if (lastChild && lastChild.nodeKind() == 'element') {
+
+ const wrapper:XML = new XML();
+ child = new XML(child.toString());
+ wrapper.setName(lastChild.name());
+ child.setParent(wrapper);
+ wrapper.getChildren().push(child);
+ child = wrapper;
+ } else {
+ child = xmlFromStringable(child);
+ }
+ }
appendChildInternal(child);
- normalize();
+ //normalize seems not correct here:
+ //normalize();
return this;
}
@@ -1405,7 +1464,7 @@ package
*/
public function localName():String
{
- return name().localName;
+ return _name? _name.localName : null;
}
private var _name:QName;
@@ -1418,8 +1477,8 @@ package
*/
public function name():QName
{
- if(!_name)
- _name = getQName("","","",false);
+ /*if(!_name)
+ _name = getQName("","","",false);*/
return _name;
}
@@ -2257,6 +2316,20 @@ package
*/
public function setName(name:*):void
{
+ //@todo add tests to review against the following:
+ /*1. If x.[[Class]] ∈ {"text", "comment"}, return
+ 2. If (Type(name) is Object) and (name.[[Class]] == "QName") and (name.uri == null)
+ a. Let name = name.localName
+ 3. Let n be a new QName created if by calling the constructor new QName(name)
+ 4. If x.[[Class]] == "processing-instruction", let n.uri be the empty string
+ 5. Let x.[[Name]] = n
+ 6. Let ns be a new Namespace created as if by calling the constructor new Namespace(n.prefix, n.uri)
+ 7. If x.[[Class]] == "attribute"
+ a. If x.[[Parent]] == null, return
+ b. Call x.[[Parent]].[[AddInScopeNamespace]](ns)
+ 8. If x.[[Class]] == "element"
+ a. Call x.[[AddInScopeNamespace]](ns)*/
+ if (_nodeKind == 'text' || _nodeKind == 'comment') return; //e4x, see 1 above
var nameRef:QName;
if(name is QName)
nameRef = name;
@@ -2274,22 +2347,23 @@ package
*/
public function setNamespace(ns:Object):void
{
- if(_nodeKind == "text" || _nodeKind == "comment" || _nodeKind == "processing-instruction")
+ var kind:String = _nodeKind;
+ if(kind == "text" || kind == "comment" || kind == "processing-instruction")
return;
var ns2:Namespace = new Namespace(ns);
var nameRef:QName = new QName(ns2,name());
- if(_nodeKind == "attribute")
+ if(kind == "attribute")
{
- nameRef.isAttribute = true;
if(_parent == null)
return;
+ nameRef.isAttribute = true;
_parent.addNamespace(ns2);
}
-
+
_name = getQName(nameRef.localName,nameRef.prefix,nameRef.uri,nameRef.isAttribute);
- if(_nodeKind == "element")
+ if(kind == "element")
addNamespace(ns2);
}
@@ -2553,26 +2627,28 @@ package
indentArr.push(_indentStr);
var indent:String = indentArr.join("");
- if(this.nodeKind() == "text")
+ const nodeType:String = this.nodeKind();
+ if(nodeType == "text") //4.
{
if(prettyPrinting)
{
var v:String = trimXMLWhitespace(_value);
- if(name().localName == "#cdata-section")
+ if (v.indexOf('<![CDATA[') == 0) {
return indent + v;
+ }
return indent + escapeElementValue(v);
}
- if(name().localName == "#cdata-section")
+ if (_value.indexOf('<![CDATA[') == 0)
return _value;
return escapeElementValue(_value);
}
- if(this.nodeKind() == "attribute")
+ if(nodeType == "attribute")
return indent + escapeAttributeValue(_value);
- if(this.nodeKind() == "comment")
+ if(nodeType == "comment")
return indent + "<!--" + _value + "-->";
- if(this.nodeKind() == "processing-instruction")
+ if(nodeType == "processing-instruction")
return indent + "<?" + name().localName + " " + _value + "?>";
// We excluded the other types, so it's a normal element
@@ -2613,24 +2689,8 @@ package
if(ns.prefix)
strArr.push(ns.prefix+":");
strArr.push(name().localName);
-
+
//attributes and namespace declarations... (15-16)
- for(i=0;i<declarations.length;i++)
- {
- var decVal:String = escapeAttributeValue(declarations[i].uri);
- if(decVal)
- {
- strArr.push(" xmlns");
- if(declarations[i].prefix)
- {
- strArr.push(":");
- strArr.push(declarations[i].prefix);
- }
- strArr.push('="');
- strArr.push(decVal);
- strArr.push('"');
- }
- }
len = attributeLength();
for(i=0;i<len;i++)
{
@@ -2649,6 +2709,24 @@ package
strArr.push(escapeAttributeValue(_attributes[i].getValue()));
strArr.push('"');
}
+ // see 15. namespace declarations is after
+ for(i=0;i<declarations.length;i++)
+ {
+ var decVal:String = escapeAttributeValue(declarations[i].uri);
+ if(decVal)
+ {
+ strArr.push(" xmlns");
+ if(declarations[i].prefix)
+ {
+ strArr.push(":");
+ strArr.push(declarations[i].prefix);
+ }
+ strArr.push('="');
+ strArr.push(decVal);
+ strArr.push('"');
+ }
+ }
+
// now write elements or close the tag if none exist
len = childrenLength();
if(len == 0)
diff --git a/frameworks/projects/XML/src/main/royale/XMLList.as b/frameworks/projects/XML/src/main/royale/XMLList.as
index 49a902d..a54df14 100644
--- a/frameworks/projects/XML/src/main/royale/XMLList.as
+++ b/frameworks/projects/XML/src/main/royale/XMLList.as
@@ -603,6 +603,28 @@ package
i. Let i = i + 1
3. Return list
*/
+ var len:uint = _xmlArray.length;
+ var textAccumulator:XML;
+ for (var i:int=0; i<len; i++) {
+ var node:XML = XML(_xmlArray[i]);
+ var nodeKind:String = node.nodeKind();
+ if (nodeKind == 'element' ) {
+ node.normalize();
+ textAccumulator = null;
+ } else if (nodeKind == 'text') {
+ if (textAccumulator) {
+ textAccumulator.setValue(textAccumulator.getValue() + node.getValue());
+ removeChildAt(i);
+ i--;
+ len--;
+ } else {
+ textAccumulator = node;
+ }
+ } else {
+ textAccumulator = null;
+ }
+ }
+
return this;
}
@@ -973,26 +995,37 @@ package
if(str)
retVal.push(str);
}
- return retVal.join("");
+ return retVal.join("\n");
}
/**
* Returns a string representation of all the XML objects in an XMLList object.
*
* @return
- *
+ *
+ * @royaleignorecoercion XML
*/
public function toString():String
{
var retVal:Array = [];
var len:int = _xmlArray.length;
+ var cumulativeText:String = '';
for (var i:int=0;i<len;i++)
{
var str:String = _xmlArray[i].toString();
- if(str)
- retVal.push(str);
+ if (XML(_xmlArray[i]).nodeKind() == 'text') {
+ cumulativeText += str;
+ } else {
+ if (cumulativeText) {
+ retVal.push(cumulativeText);
+ cumulativeText = '';
+ }
+ if(str)
+ retVal.push(str);
+ }
}
- return retVal.join("");
+ if (cumulativeText) retVal.push(cumulativeText);
+ return retVal.join("\n");
}
/**