/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- m_node
- NodeNameEquals
- callProperty
- getAtomProperty
- getMultinameProperty
- setMultinameProperty
- deleteMultinameProperty
- getDescendants
- setAtomProperty
- getUintProperty
- setUintProperty
- delUintProperty
- deleteAtomProperty
- hasUintProperty
- hasMultinameProperty
- hasAtomProperty
- _deepCopy
- AS3_descendants
- _resolveValue
- GenerateUniquePrefix
- __toXMLString
- nextName
- nextValue
- nextNameIndex
- AS3_addNamespace
- AS3_appendChild
- AS3_attribute
- AS3_attributes
- AS3_child
- AS3_childIndex
- AS3_children
- AS3_comments
- AS3_contains
- AS3_copy
- AS3_elements
- XML_AS3_hasOwnProperty
- AS3_hasComplexContent
- AS3_hasSimpleContent
- AS3_inScopeNamespaces
- AS3_insertChildAfter
- AS3_insertChildBefore
- AS3_localName
- AS3_name
- _namespace
- AS3_namespaceDeclarations
- AS3_nodeKind
- AS3_normalize
- AS3_parent
- AS3_processingInstructions
- AS3_prependChild
- XML_AS3_propertyIsEnumerable
- AS3_removeNamespace
- AS3_replace
- AS3_setChildren
- AS3_setLocalName
- AS3_setName
- AS3_setNamespace
- AS3_text
- toString
- AS3_toString
- AS3_toXMLString
- format
- getClass
- _length
- getParent
- setValue
- getValue
- getQName
- AS3_setNotification
- AS3_notification
- notifyNeeded
- childChanges
- nonChildChanges
- issueNotifications
- filter
- _filter
- dispose
- m_mn
- get_localName
- getURI
- get_uri
- GetNamespace
- nextName
- nextValue
- nextNameIndex
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is [Open Source Virtual Machine.].
*
* The Initial Developer of the Original Code is
* Adobe System Incorporated.
* Portions created by the Initial Developer are Copyright (C) 2004-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Adobe AS3 Team
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/////////////////////////////////////////////////////////
// Internal properties of the E4XNode classes
// Multiname *m_name;
// Stringp m_value;
// E4XNode *m_parent; // the parent or NULL for top item
// AtomArray *m_attributes;
// AtomArray *m_namespaces;
// AtomArray *m_children;
//
// kAttribute (AttributeE4XNode)
// m_name = Multiname containing namespace:name pair (marked as an attribute)
// m_value = text value of the attribute
// kText/kCDATA (TextE4XNode, CDATAE4XNode)
// m_value - text value of the node
// kComment (CommentE4XNode)
// m_value - text value of the node
// kProcessingInstruction (PIE4XNode)
// m_name = Multiname containing namespace:name pair
// m_value - text value of the node
// kElement (ElementE4XNode)
// m_name = Multiname containing namespace:name pair
// m_attributes : list of attributes (E4XNodes's)
// m_children : array of children nodes (E4XNodes's) or
// m_namespaces: array of namespaces for this node
//
// QName contains a Multiname with only ONE namespace
// null
// "" empty string
// uri
//
// Properties of the XML object can be either qualified (one associated namespace)
// or unqualified.
//
// Property access to the XML object can be a variety of cases:
// qualified: one namespace
// unqualified: one or more namespaces (the public namespace, plus others)
// anyName operator - matches both qualified and unqualified properties
#include "avmplus.h"
#include "BuiltinNatives.h"
//#define STRING_DEBUG
namespace avmplus
{
XMLObject::XMLObject(XMLClass *type, E4XNode *node)
: ScriptObject(type->ivtable(), type->prototype), m_node(node)
{
SAMPLE_FRAME("XML", this->core());
this->publicNS = core()->findPublicNamespace();
}
// This is considered the "toXML function"
XMLObject::XMLObject(XMLClass *type, Stringp str, Namespace *defaultNamespace)
: ScriptObject(type->ivtable(), type->prototype)
{
SAMPLE_FRAME("XML", this->core());
#if 0//def _DEBUG
// *** NOTE ON THREAD SAFETY ***
//
// Enabling this code means that there may be a race to initialize 'once' on different
// threads, or, alternatively, that only one core gets to run this code, not each core
// individually. This may or may not be OK, but needs to be considered before enabling
// the code.
static bool once = false;
if (!once)
{
once = true;
AvmDebugMsg(false, "sizeof(E4XNode): %d\r\n", sizeof(E4XNode));
AvmDebugMsg(false, "sizeof(TextE4XNode): %d\r\n", sizeof(TextE4XNode));
AvmDebugMsg(false, "sizeof(ElementE4XNode): %d\r\n", sizeof(ElementE4XNode));
AvmDebugMsg(false, "sizeof(E4XNodeAux): %d\r\n", sizeof(E4XNodeAux));
}
#endif
if (!str)
return;
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
MMgc::GC *gc = core->GetGC();
this->publicNS = core->findPublicNamespace();
AvmAssert(traits()->getSizeOfInstance() == sizeof(XMLObject));
AvmAssert(traits()->getExtraSize() == 0);
// str, ignoreWhite
bool bIgnoreWhite = toplevel->xmlClass()->get_ignoreWhitespace() != 0;
XMLParser parser(core, str);
parser.parse(bIgnoreWhite);
parser.setCondenseWhite(true);
XMLTag tag(gc);
E4XNode* p = 0;
// When we're passed in a defaultNamespace, we simulate the following XML code
// <parent xmlns=defaultNamespace's URI>string</parent>
if (defaultNamespace)
{
setNode( new (gc) ElementE4XNode (0) );
// create a namespace for the parent using defaultNamespace->URI()
Namespace *ns = core->internNamespace (core->newNamespace (core->kEmptyString->atom(), defaultNamespace->getURI()->atom()));
m_node->_addInScopeNamespace (core, ns, publicNS);
Stringp name = core->internConstantStringLatin1("parent");
m_node->setQName (core, name, ns);
p = m_node;
}
int m_status;
while ((m_status = parser.getNext(tag)) == XMLParser::kNoError)
{
E4XNode* pNewElement = NULL;
switch (tag.nodeType)
{
case XMLTag::kXMLDeclaration:
{
// !!@ add some checks to ensure this is the first tag
// encountered in our file (deal with <parent> stuff from
// XMLObject and XMLListObject parser setup
}
break;
case XMLTag::kDocTypeDeclaration:
break;
case XMLTag::kElementType:
{
// A closing tag
if (tag.text->charAt(0) == '/')
{
const int32_t nodeNameStart = 1; // skip the slash
Multiname m;
p->getQName(&m, publicNS);
Namespace* ns = m.getNamespace();
// Get our parent's qualified name string here
Stringp parentName = m.getName();
if (!NodeNameEquals(tag.text, nodeNameStart, parentName, ns) &&
// We're trying to support paired nodes where the first node gets a namespace
// from the default namespace.
parentName->Compare(*tag.text, nodeNameStart, tag.text->length()-nodeNameStart) != 0 &&
ns->getURI() == toplevel->getDefaultNamespace()->getURI())
{
// If p == m_node, we are at the top of our tree and we're parsing the fake "parent"
// wrapper tags around our actual XML text. Instead of warning about a missing "</parent>"
// tag, we instead complain about the XML markup not being well-formed.
// (Emulating Rhino behavior)
if (p == m_node)
toplevel->throwTypeError(kXMLMarkupMustBeWellFormed);
else
toplevel->throwTypeError(kXMLUnterminatedElementTag, parentName, parentName);
}
else
{
// Catch the case where our input string ends with a bogus <parent> tag
if (defaultNamespace && (p == m_node))
toplevel->throwTypeError(kXMLMarkupMustBeWellFormed);
// found matching closing tag so we can pop back up a level now
if (p != m_node)
p = p->getParent();
}
}
else // an opening tag
{
ElementE4XNode *e = new (gc) ElementE4XNode(0);
pNewElement = e;
// Our first tag modifies this object itself
if (!m_node)
{
setNode(pNewElement);
}
else // all other tags create a new element tag
{
p->_append(pNewElement);
}
if (!tag.empty) // if our tag is not empty, we're now the "parent" tag
{
p = pNewElement;
}
// Needs to happen after setting m_name->name so throw error can use name in routine
e->CopyAttributesAndNamespaces(core, toplevel, tag, publicNS);
// Find a namespace that matches this tag in our parent chain. If this name
// is a qualified name (ns:name), we search for a namespace with a matching
// prefix. If is an unqualified name, we find the first empty prefix name.
Namespace *ns = pNewElement->FindNamespace(core, toplevel, tag.text, false);
// pg 35, map [[name]].uri to "namespace name" of node
if (!ns) {
// NOTE use caller's public
ns = core->findPublicNamespace();
}
pNewElement->setQName(core, tag.text, ns);
}
}
break;
case XMLTag::kComment:
if (!toplevel->xmlClass()->get_ignoreComments())
{
pNewElement = new (gc) CommentE4XNode (0, tag.text);
if (!m_node)
setNode( pNewElement );
}
break;
case XMLTag::kCDataSection:
pNewElement = new (gc) CDATAE4XNode (0, tag.text);
if (!m_node)
setNode( pNewElement );
break;
case XMLTag::kTextNodeType:
// For small strings, we intern them in an attempt to save memory
// with large XML files with of lot of repeating text nodes.
if (tag.text->length() < 32)
{
Stringp text = core->internString(tag.text);
// Reduce our GC pressure if we know our tag.text is unused.
if (text != tag.text)
{
AvmAssert(!tag.text->isInterned());
tag.text = NULL;
}
pNewElement = new (gc) TextE4XNode(0, text);
}
else
{
pNewElement = new (gc) TextE4XNode(0, tag.text);
}
if (!m_node)
setNode( pNewElement );
break;
case XMLTag::kProcessingInstruction:
if (!toplevel->xmlClass()->get_ignoreProcessingInstructions())
{
Stringp name, val;
int32_t space = tag.text->indexOfLatin1(" ", 1, 0);
if (space < 0)
{
// no spaces, no value
name = tag.text;
val = core->kEmptyString;
}
else
{
name = tag.text->substring(0, space);
while (String::isSpace((wchar) tag.text->charAt(++space))) {}
val = tag.text->substring(space, tag.text->length());
}
pNewElement = new (gc) PIE4XNode(0, val);
// NOTE use caller's public
pNewElement->setQName (core, name, core->findPublicNamespace());
if (!m_node)
setNode( pNewElement );
}
break;
//kNoType = 0,
default:
AvmAssert(0); // unknown tag type??
}
if ( pNewElement && (XMLTag::kElementType != tag.nodeType))
{
if (pNewElement != m_node)
p->_append( pNewElement);
}
if ( m_status != XMLParser::kNoError )
{
break; // stop getting tags
}
}
if ( m_status == XMLParser::kEndOfDocument )
{
m_status = XMLParser::kNoError;
}
else
{
switch (m_status)
{
case XMLParser::kMalformedElement:
toplevel->throwTypeError(kXMLMalformedElement);
break;
case XMLParser::kUnterminatedCDataSection:
toplevel->throwTypeError(kXMLUnterminatedCData);
break;
case XMLParser::kUnterminatedXMLDeclaration:
toplevel->throwTypeError(kXMLUnterminatedXMLDecl);
break;
case XMLParser::kUnterminatedDocTypeDeclaration:
toplevel->throwTypeError(kXMLUnterminatedDocTypeDecl);
break;
case XMLParser::kUnterminatedComment:
toplevel->throwTypeError(kXMLUnterminatedComment);
break;
case XMLParser::kUnterminatedAttributeValue:
toplevel->throwTypeError(kXMLUnterminatedAttribute);
break;
case XMLParser::kUnterminatedElement:
toplevel->throwTypeError(kXMLUnterminatedElement);
break;
case XMLParser::kUnterminatedProcessingInstruction:
toplevel->throwTypeError(kXMLUnterminatedProcessingInstruction);
break;
case XMLParser::kOutOfMemory:
case XMLParser::kElementNeverBegun:
AvmAssert(0);
break;
}
}
if ( p != m_node && ! m_status )
{
Multiname m;
p->getQName(&m, publicNS);
// Get our parents qualified name string here
Stringp parentName = m.getName();
toplevel->throwTypeError(kXMLUnterminatedElementTag, parentName, parentName);
}
}
bool XMLObject::NodeNameEquals(Stringp nodeName, int32_t nodeNameStart, Stringp parentName, Namespace * parentNs)
{
int32_t const nodeNameLength = nodeName->length() - nodeNameStart;
if (parentNs && parentNs->hasPrefix())
{
AvmCore *core = this->core();
Stringp parentNSName = core->string(parentNs->getPrefix());
int prefixLen = parentNSName->length();
// Does nodeName == parentNS:parentName
int totalLen = prefixLen + 1 + parentName->length(); // + 1 for ':' separator
if (totalLen != nodeNameLength)
return false;
if (parentNSName->Compare(*nodeName, nodeNameStart, prefixLen) != 0)
return false;
if (nodeName->charAt(nodeNameStart + prefixLen) != ':')
return false;
// -1 for ':'
prefixLen++;
return (parentName->Compare(*nodeName, nodeNameStart + prefixLen, nodeNameLength - prefixLen) == 0);
}
else
{
return parentName->Compare(*nodeName, nodeNameStart, nodeNameLength) == 0;
}
}
//////////////////////////////////////////////////////////////////////
// E4X Section 9.1.1
//////////////////////////////////////////////////////////////////////
// sec 11.2.2.1 CallMethod
// this = argv[0] (ignored)
// arg1 = argv[1]
// argN = argv[argc]
Atom XMLObject::callProperty(const Multiname* multiname, int argc, Atom* argv)
{
AvmCore *core = this->core();
Atom f = getDelegate()->getMultinameProperty(multiname);
if (f == undefinedAtom)
{
f = getMultinameProperty(multiname);
// If our method returned is a 0 element XMLList, it means that we did not
// find a matching property for this method name. In this case, if our XML
// node is simple, we convert it to a string and callproperty on the string.
// This allows node elements to be treated as simple strings even if they
// are XML or XMLList objects. See 11.2.2.1 in the E4X spec for CallMethod.
if (AvmCore::isXMLList(f) &&
!AvmCore::atomToXMLList(f)->_length() &&
(hasSimpleContent()))
{
Stringp r0 = core->string (this->atom());
return toplevel()->callproperty (r0->atom(), multiname, argc, argv, toplevel()->stringClass->vtable);
}
}
argv[0] = atom(); // replace receiver
return toplevel()->op_call(f, argc, argv);
}
// E4X 9.1.1.1, pg 12 - [[GET]]
Atom XMLObject::getAtomProperty(Atom P) const
{
Multiname m;
toplevel()->ToXMLName(P, m);
return getMultinameProperty(&m);
}
// E4X 9.1.1.1, pg 12 - [[GET]]
Atom XMLObject::getMultinameProperty(const Multiname* name_in) const
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
Multiname name;
toplevel->CoerceE4XMultiname(name_in, name);
#ifdef STRING_DEBUG
Stringp n1 = name.getName();
#endif
if (!name.isAnyName() && !name.isAttr())
{
// We have an integer argument - direct child lookup
Stringp nameString = name.getName();
uint32 index;
if (AvmCore::getIndexFromString (nameString, &index))
{
// //l = ToXMLList (this);
// //return l->get(p);
// ToXMLList on a XMLNode just creates a one item XMLList. The only valid
// property number for the new XMLList is 0 which just returns this node. Handle
// that case here.
if (index == 0)
return this->atom();
else
return undefinedAtom;
}
}
XMLListObject *xl = new (core->GetGC()) XMLListObject(toplevel->xmlListClass(), this->atom(), &name);
if (name.isAttr())
{
// does not hurt, but makes things faster
xl->checkCapacity(m_node->numAttributes());
// for each a in x.[[attributes]]
for (uint32 i = 0; i < m_node->numAttributes(); i++)
{
E4XNode *xml = m_node->getAttribute(i);
AvmAssert(xml && xml->getClass() == E4XNode::kAttribute);
Multiname m;
AvmAssert(xml->getQName(&m, publicNS) != 0);
//if (((n.[[Name]].localName == "*") || (n.[[Name]].localName == a.[[Name]].localName)) &&
// ((n.[[Name]].uri == nulll) || (n.[[Name]].uri == a.[[Name]].uri)))
// l.append (a);
xml->getQName(&m, publicNS);
if (name.matches(&m))
{
xl->_appendNode (xml);
}
}
return xl->atom();
}
// step 5 - look through all the children for a match - [[length]] implies length of children
// n isn't an attributeName so it must be a qname??
// for k = 0 to x.[[length]]-1
// if (n.localName = "*" and this[k].class == "element" and (this[k].name.localName == n.localName)
// and (!n.uri) or (this[k].class == "element) and (n.uri == this[k].name.uri)))
// xl->_append (x[k]);
if (name.isAnyName())
xl->checkCapacity(m_node->numChildren());
for (uint32 i = 0; i < m_node->numChildren(); i++)
{
E4XNode *child = m_node->_getAt(i);
Multiname m;
Multiname *m2 = 0;
if (child->getClass() == E4XNode::kElement)
{
child->getQName(&m, publicNS);
m2 = &m;
}
// if (n.localName = "*" OR this[k].class == "element" and (this[k].name.localName == n.localName)
// and (!n.uri) or (this[k].class == "element) and (n.uri == this[k].name.uri)))
// xl->_append (x[k]);
if (name.matches(m2))
{
xl->_appendNode (child);
}
}
return xl->atom();
}
void XMLObject::setMultinameProperty(const Multiname* name_in, Atom V)
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
Multiname m;
toplevel->CoerceE4XMultiname(name_in, m);
// step 3
if (!m.isAnyName() && !m.isAttr())
{
Stringp name = m.getName();
uint32 index;
if (AvmCore::getIndexFromString (name, &index))
{
// Spec says: NOTE: this operation is reserved for future versions of E4X
toplevel->throwTypeError(kXMLAssignmentToIndexedXMLNotAllowed);
}
}
// step 4
if (getClass() & (E4XNode::kText | E4XNode::kCDATA | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute))
return;
Atom c;
if (AvmCore::atomToXMLList(V))
{
XMLListObject *src = AvmCore::atomToXMLList(V);
if ((src->_length() == 1) && src->_getAt(0)->getClass() & (E4XNode::kText | E4XNode::kAttribute))
{
c = core->string(V)->atom();
}
else
{
c = src->_deepCopy()->atom();
}
}
else if (AvmCore::atomToXML(V))
{
XMLObject *x = AvmCore::atomToXMLObject(V);
if (x->getClass() & (E4XNode::kText | E4XNode::kAttribute))
{
// This string is converted into a XML object below in step 2(g)(iii)
c = core->string(V)->atom();
}
else
{
c = x->_deepCopy()->atom();
}
}
else
{
#ifdef STRING_DEBUG
String *foo = core->string(V);
#endif // STRING_DEBUG
c = core->string(V)->atom();
}
// step 5
//Atom n = core->ToXMLName (P);
// step 6
//Atom defaultNamespace = core->getDefaultNamespace()->atom();
// step 7
if (m.isAttr())
{
// step 7b
Stringp sc;
if (AvmCore::isXMLList(c))
{
XMLListObject *xl = AvmCore::atomToXMLList(c);
if (!xl->_length())
{
sc = core->kEmptyString;
}
else
{
StringBuffer output (core);
output << core->string (xl->_getAt (0)->atom());
for (uint32 i = 1; i < xl->_length(); i++)
{
output << " " << core->string (xl->_getAt (i)->atom());
}
sc = core->newStringUTF8(output.c_str());
}
}
else // step 7c
{
sc = core->string (c);
}
// step 7d
int a = -1; // -1 is null in spec
// step 7e
for (uint32 j = 0; j < this->m_node->numAttributes(); j++)
{
E4XNode *x = m_node->getAttribute(j);
Multiname m2;
x->getQName(&m2, publicNS);
if (m.matches(&m2))
{
if (a == -1)
{
a = j;
}
else
{
this->deleteMultinameProperty(&m2);
// notification occurrs in deleteproperty
}
}
}
Stringp name = !m.isAnyName() ? m.getName() : NULL;
Atom nameAtom = name ? name->atom() : nullStringAtom;
if (a == -1) // step 7f
{
E4XNode *e = new (core->GetGC()) AttributeE4XNode(this->m_node, sc);
Namespace *ns = 0;
if (m.namespaceCount() == 1)
ns = m.getNamespace();
e->setQName(core, name, ns);
this->m_node->addAttribute (e);
e->_addInScopeNamespace(core, ns, publicNS);
nonChildChanges(xmlClass()->kAttrAdded, nameAtom, sc->atom());
}
else // step 7g
{
E4XNode *x = m_node->getAttribute(a);
Stringp prior = x->getValue();
x->setValue (sc);
nonChildChanges(xmlClass()->kAttrChanged, nameAtom, (prior) ? prior->atom() : undefinedAtom);
}
// step 7h
return;
}
if (!m.isAnyName())
{
// step 8
bool isValidName = core->isXMLName(m.getName()->atom());
// step 9
if (!isValidName)
return;
}
// step 10
int32 i = -1; // -1 is undefined in spec
bool primitiveAssign = ((!AvmCore::isXML(c) && !AvmCore::isXMLList(c)) && (!m.isAnyName()));
// step 12
bool notify = notifyNeeded(getNode());
for (int k = _length() - 1; k >= 0; k--)
{
E4XNode *x = m_node->_getAt(k);
Multiname mx;
Multiname *m2 = 0;
if (x->getClass() == E4XNode::kElement)
{
x->getQName(&mx, publicNS);
m2 = &mx;
}
if (m.matches(m2))
{
// remove n-1 nodes of n matching
if (i != -1)
{
E4XNode* was = m_node->_getAt(i);
m_node->_deleteByIndex (i);
// notify
if (notify && (was->getClass() == E4XNode::kElement))
{
XMLObject* nd = new (core->GetGC()) XMLObject (toplevel->xmlClass(), was);
childChanges(xmlClass()->kNodeRemoved, nd->atom());
}
}
i = k;
}
}
// step 13
if (i == -1)
{
i = _length();
if (primitiveAssign)
{
E4XNode *e = new (core->GetGC()) ElementE4XNode (m_node);
// We use m->namespaceCount here to choose to use the default xml namespace
// name here for an unqualified prop access. For a qualified access,
// there will be only one namespace
Stringp name = m.getName();
Namespace *ns;
if (m.namespaceCount() == 1)
ns = m.getNamespace();
else
ns = toplevel->getDefaultNamespace();
e->setQName (core, name, ns);
XMLObject *y = new (core->GetGC()) XMLObject (toplevel->xmlClass(), e);
m_node->_replace (core, toplevel, i, y->atom());
e->_addInScopeNamespace (core, ns, publicNS);
}
}
// step 14
if (primitiveAssign)
{
E4XNode *xi = m_node->_getAt(i);
// children are being removed notify parent if necc.
bool notify = notifyNeeded(xi);
XMLObject* target = (notify) ? new (core->GetGC()) XMLObject(xmlClass(), xi) : 0;
int count = xi->numChildren();
for(int r=0;notify && (r<count); r++)
{
E4XNode* ild = xi->_getAt(r);
if (ild->getClass() == E4XNode::kElement)
{
XMLObject* nd = new (core->GetGC()) XMLObject (toplevel->xmlClass(), ild);
target->childChanges(xmlClass()->kNodeRemoved, nd->atom());
}
}
// remember node if there was one...
Atom prior = undefinedAtom;
if (notify && count > 0)
{
XMLObject* nd = new (core->GetGC()) XMLObject (toplevel->xmlClass(), xi->_getAt(0));
prior = nd->atom();
}
// step 14a - delete all properties of x[i]
xi->clearChildren();
Stringp s = core->string (c);
if (s->length())
{
xi->_replace (core, toplevel, i, c, prior);
}
}
else
{
E4XNode* prior = m_node->_replace (core, toplevel, i, c);
if (notifyNeeded(getNode()))
{
// The above _replace call may be used to insert new nodes at the end. However, if a null is inserted
// the effect is as though nothing was inserted. Test for this case.
if (m_node->_length() > (uint32)i)
{
XMLObject* xml = new (core->GetGC()) XMLObject(xmlClass(), m_node->_getAt(i));
childChanges( (prior) ? xmlClass()->kNodeChanged : xmlClass()->kNodeAdded, xml->atom(), prior);
}
}
}
return;
}
bool XMLObject::deleteMultinameProperty(const Multiname* name_in)
{
AvmCore *core = this->core();
Multiname m;
toplevel()->CoerceE4XMultiname(name_in, m);
// step 1
if (!m.isAnyName() && !m.isAttr())
{
Stringp name = m.getName();
uint32 index;
if (AvmCore::getIndexFromString (name, &index))
{
// Spec says: NOTE: this operation is reserved for future versions of E4X
// In Rhino, this silently fails
return true;
}
}
if (m.isAttr())
{
uint32 j = 0;
while (j < m_node->numAttributes())
{
E4XNode *x = m_node->getAttribute(j);
Multiname m2;
x->getQName(&m2, publicNS);
if (m.matches(&m2))
{
x->setParent(NULL);
// remove the attribute from m_attributes
m_node->getAttributes()->removeAt (j);
Multiname previous;
x->getQName(&previous, publicNS);
Stringp name = previous.getName();
Stringp val = x->getValue();
nonChildChanges(xmlClass()->kAttrRemoved, (name) ? name->atom() : undefinedAtom, (val) ? val->atom() : undefinedAtom);
}
else
{
j++;
}
}
return true;
}
bool notify = notifyNeeded(m_node);
uint32 q = 0;
while (q < _length())
{
E4XNode *x = m_node->_getAt(q);
Multiname mx;
Multiname *m2 = 0;
bool isElem = x->getClass() == (E4XNode::kElement) ? true : false;
if (isElem)
{
x->getQName(&mx, publicNS);
m2 = &mx;
}
if (m.matches(m2))
{
x->setParent (NULL);
m_node->_deleteByIndex (q);
if (notify && isElem)
{
XMLObject *r = new (core->GetGC()) XMLObject (xmlClass(), x);
childChanges(xmlClass()->kNodeRemoved, r->atom());
}
}
else
{
q++;
// if (dp > 0)
// rename property (q) to (q-dp)
// this automatically gets taken care of by deleteByIndex
}
}
// x.length = x.length - dp
// this is handled b _deleteByIndex logic
return true;
}
Atom XMLObject::getDescendants(const Multiname* name_in) const
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
core->stackCheck(toplevel);
Multiname m;
toplevel->CoerceE4XMultiname(name_in, m);
XMLListObject *l = new (core->GetGC()) XMLListObject(toplevel->xmlListClass());
if (m.isAttr())
{
for (uint32 i = 0; i < m_node->numAttributes(); i++)
{
E4XNode *ax = m_node->getAttribute(i);
Multiname m2;
AvmAssert(ax->getQName(&m2, publicNS));
ax->getQName(&m2, publicNS);
if (m.matches(&m2))
{
// for each atribute, if it's name equals m,
l->_appendNode (ax);
}
}
}
for (uint32 k = 0; k < _length(); k++)
{
E4XNode *child = m_node->_getAt(k);
if (!m.isAttr())
{
Multiname mx;
Multiname *m2 = 0;
if (child->getClass() == E4XNode::kElement)
{
child->getQName(&mx, publicNS);
m2 = &mx;
}
if (m.matches(m2))
{
l->_appendNode (child);
}
}
XMLObject *co = new (core->GetGC()) XMLObject(toplevel->xmlClass(), child);
Atom dq = co->getDescendants (&m);
delete co;
XMLListObject *dql = AvmCore::atomToXMLList(dq);
if (dql && dql->_length())
{
l->_append (dq);
}
}
return l->atom();
}
// E4X 9.1.1.2, pg 13 - [[PUT]]
// E4X errata:
// 9.1.1.2 Move steps 3 and 4 to before 1 and 2, to avoid wasted effort in
// ToString or [[DeepCopy]].
void XMLObject::setAtomProperty(Atom P, Atom V)
{
Multiname m;
toplevel()->ToXMLName(P, m);
setMultinameProperty(&m, V);
}
Atom XMLObject::getUintProperty(uint32 index) const
{
if (index == 0)
return this->atom();
else
return undefinedAtom;
}
void XMLObject::setUintProperty(uint32 /*i*/, Atom /*value*/)
{
// Spec says: NOTE: this operation is reserved for future versions of E4X
toplevel()->throwTypeError(kXMLAssignmentToIndexedXMLNotAllowed);
}
bool XMLObject::delUintProperty(uint32 /*i*/)
{
// Spec says: NOTE: this operation is reserved for future versions of E4X
// In Rhino, this silently fails
return true;
}
// E4X 9.1.1.3, pg 14 - [[DELETE]]
bool XMLObject::deleteAtomProperty(Atom P)
{
Multiname m;
toplevel()->ToXMLName(P, m);
return deleteMultinameProperty(&m);
}
// E4X 9.1.1.5, ??
// [[DefaultValue]] ??
bool XMLObject::hasUintProperty(uint32 index) const
{
return (index == 0);
}
bool XMLObject::hasMultinameProperty(const Multiname* name_in) const
{
Multiname m;
toplevel()->CoerceE4XMultiname(name_in, m);
if (!m.isAnyName() && !m.isAttr())
{
Stringp name = m.getName();
uint32 index;
if (AvmCore::getIndexFromString (name, &index))
{
return (index == 0);
}
}
if (m.isAttr())
{
for (uint32 i = 0; i < m_node->numAttributes(); i++)
{
E4XNode *ax = m_node->getAttribute(i);
Multiname m2;
if (ax->getQName(&m2, publicNS) && (m.matches(&m2)))
{
return true;
}
}
return false;
}
// n is a QName
for (uint32 k = 0; k < m_node->_length(); k++)
{
E4XNode *child = m_node->_getAt(k);
Multiname mx;
Multiname *m2 = 0;
if (child->getClass() == E4XNode::kElement)
{
child->getQName(&mx, publicNS);
m2 = &mx;
}
if (m.matches(m2))
{
return true;
}
}
return false;
}
// E4X 9.1.1.6, 16
bool XMLObject::hasAtomProperty(Atom P) const
{
Multiname m;
toplevel()->ToXMLName (P, m);
return hasMultinameProperty(&m);
}
// E4X 9.1.1.7, page 16
XMLObject *XMLObject::_deepCopy () const
{
AvmCore *core = this->core();
E4XNode *e = m_node->_deepCopy (core, toplevel(), publicNS);
XMLObject *y = new (core->GetGC()) XMLObject(xmlClass(), e);
return y;
}
// E4X 9.1.1.8, page 17
XMLListObject *XMLObject::AS3_descendants(Atom P) const
{
Multiname m;
toplevel()->ToXMLName (P, m);
return AvmCore::atomToXMLList (getDescendants (&m));
}
// E4X 9.1.1.10, page 18
Atom XMLObject::_resolveValue ()
{
return this->atom();
}
Namespace *XMLObject::GenerateUniquePrefix (Namespace *ns, const AtomArray *namespaces) const
{
AvmCore *core = this->core();
// should only be called when a namespace doesn't have a prefix
AvmAssert (ns->getPrefix() == undefinedAtom);
// Try to use the empty string as a first try (ISNS changes)
uint32 i;
for (i = 0; i < namespaces->getLength(); i++)
{
Namespace *ns = AvmCore::atomToNamespace (namespaces->getAt(i));
if (ns->getPrefix() == core->kEmptyString->atom())
break;
}
if (i == namespaces->getLength())
{
return core->newNamespace (core->kEmptyString->atom(), ns->getURI()->atom());
}
// Rhino seems to start searching with whatever follows "://www" or "://"
//String *origURI = core()->string(ns->getURI());
wchar s[4];
s[0] = s[1] = s[2] = 'a';
s[3] = 0;
for (wchar x1 = 'a'; x1 <= 'z'; x1++)
{
s[0] = x1;
for (wchar x2 = 'a'; x2 <= 'z'; x2++)
{
s[1] = x2;
for (wchar x3 = 'a'; x3 <= 'z'; x3++)
{
s[2] = x3;
bool bMatch = false;
Atom pre = core->internStringUTF16(s, 3)->atom();
for (uint32 i = 0; i < namespaces->getLength(); i++)
{
Namespace *ns = AvmCore::atomToNamespace (namespaces->getAt(i));
if (pre == ns->getPrefix())
{
bMatch = true;
break;
}
}
if (!bMatch)
{
return core->newNamespace (pre, ns->getURI()->atom());
}
}
}
}
return 0;
}
// E4X 10.2, pg 29
void XMLObject::__toXMLString(StringBuffer &s, AtomArray *AncestorNamespaces, int indentLevel, bool includeChildren) const
{
AvmCore *core = this->core();
core->stackCheck(toplevel());
if (toplevel()->xmlClass()->okToPrettyPrint())
{
for (int i = 0; i < indentLevel; i++)
{
s << " ";
}
}
if (this->getClass() == E4XNode::kText) // CDATA checked below
{
if (toplevel()->xmlClass()->okToPrettyPrint())
{
// v = removing leading and trailing whitespace from x.value
// return escapeElementValue (v);
s << core->EscapeElementValue(m_node->getValue(), true);
return;
}
else
{
s << core->EscapeElementValue(m_node->getValue(), false);
return;
}
}
if (this->getClass() == E4XNode::kCDATA)
{
s << "<![CDATA[" << m_node->getValue() << "]]>";
return;
}
if (this->getClass() == E4XNode::kAttribute)
{
s << core->EscapeAttributeValue (m_node->getValue()->atom());
return;
}
if (this->getClass() == E4XNode::kComment)
{
s << "<!--";
s << m_node->getValue();
s << "-->";
return;
}
if (this->getClass() == E4XNode::kProcessingInstruction) // step 7
{
s << "<?";
Multiname m;
AvmAssert (m_node->getQName(&m, publicNS) != 0);
if (m_node->getQName(&m, publicNS))
{
s << m.getName() << " ";
}
s << m_node->getValue() << "?>";
return;
}
// We're a little different than the spec here. Instead of each XMLObject
// keeping track of its entire in-scope namespace list (all the way to the
// topmost parent), the XMLObject only knows about its own declared nodes.
// So when were converting to a string, we need to build the inScopeNamespace
// list here.
AtomArray *inScopeNS = new (core->GetGC()) AtomArray();
m_node->BuildInScopeNamespaceList (core, inScopeNS);
uint32 origLength = AncestorNamespaces->getLength();
// step 8 - ancestorNamespaces passed in
// step 9/10 - add in our namespaces into ancestorNamespaces if there are no conflicts
for (uint32 i = 0; i < inScopeNS->getLength(); i++)
{
Namespace *ns = AvmCore::atomToNamespace (inScopeNS->getAt(i));
uint32 j;
for (j = 0; j < AncestorNamespaces->getLength(); j++)
{
Namespace *ns2 = AvmCore::atomToNamespace (AncestorNamespaces->getAt(j));
#ifdef STRING_DEBUG
Stringp u1 = ns->getURI();
Stringp p1 = core->string(ns->getPrefix());
Stringp u2 = ns2->getURI();
Stringp p2 = core->string(ns2->getPrefix());
#endif
if ((ns->getURI() == ns2->getURI()) && (ns->getPrefix() == ns2->getPrefix()))
break;
}
if (j == AncestorNamespaces->getLength()) // a match was not found
{
AncestorNamespaces->push (ns->atom());
}
}
// step 11 - new ISNS changes
// If this node's namespace has an undefined prefix, generate a new one
Multiname m;
AvmAssert (getNode()->getQName(&m, publicNS));
getNode()->getQName(&m, publicNS);
Namespace *thisNodesNamespace = GetNamespace (m, AncestorNamespaces);
AvmAssert(thisNodesNamespace != 0);
if (thisNodesNamespace->getPrefix() == undefinedAtom)
{
// find a prefix and add this namespace to our list
thisNodesNamespace = GenerateUniquePrefix (thisNodesNamespace, AncestorNamespaces);
AncestorNamespaces->push (thisNodesNamespace->atom());
}
String *nsPrefix = core->string (thisNodesNamespace->getPrefix());
// If any of this node's attribute's namespaces have an undefined prefix, generate a new one
for (uint32 i = 0; i < m_node->numAttributes(); i++)
{
E4XNode *an = m_node->getAttribute(i);
AvmAssert(an != 0);
AvmAssert(an->getClass() == E4XNode::kAttribute);
Multiname nam;
if (an->getQName(&nam, publicNS))
{
Namespace* ns = GetNamespace(nam, AncestorNamespaces);
AvmAssert(ns != 0);
if (ns->getPrefix() == undefinedAtom)
{
// find a prefix and add this namespace to our list
ns = GenerateUniquePrefix (ns, AncestorNamespaces);
AncestorNamespaces->push (ns->atom());
}
}
}
// step 12
s << "<";
// step13 - insert namespace prefix if we have one
if (nsPrefix != core->kEmptyString)
{
s << nsPrefix << ":";
}
// step 14
AvmAssert (!m.isAnyName());
s << m.getName();
// step 15 - attrAndNamespaces = sum of x.attributes and namespaceDeclarations
// step 16
// for each an in attrAndNamespaces
for (uint32 i = 0; i < m_node->numAttributes(); i++)
{
// step 17a
E4XNode *an = m_node->getAttribute(i);
AvmAssert(an != 0);
AvmAssert(an->getClass() == E4XNode::kAttribute);
Multiname nam;
if (an->getQName(&nam, publicNS))
{
s << " ";
// step16b-i - ans = an->getName->getNamespace(AncestorNamespace);
AvmAssert(nam.isAttr());
Namespace *attr_ns = GetNamespace (nam, AncestorNamespaces);
//!!@step16b-ii - should never get hit now with revised 10.2.1 step 11.
AvmAssert(attr_ns->getPrefix() != undefinedAtom);
// step16b-iii
if (attr_ns && attr_ns->hasPrefix ())
{
s << core->string(attr_ns->getPrefix()) << ":";
}
//step16b-iv
s << nam.getName();
//step16c - namespace case - see below
//step 16d
s << "=\"";
//step 16e
s << core->EscapeAttributeValue(an->getValue()->atom());
//step 16f - namespace case
//step 16g
s << "\"";
}
}
// This adds any NS that were added to our ancestor namespace list (from origLength on up)
for (uint32 i = origLength; i < AncestorNamespaces->getLength(); i++)
{
Namespace *an = AvmCore::atomToNamespace(AncestorNamespaces->getAt(i));
if (an->getURI() != core->kEmptyString)
{
s << " xmlns";
AvmAssert (an->getPrefix() != undefinedAtom);
if (an->getPrefix() != core->kEmptyString->atom())
{
// 17c iii
s << ":" << core->string(an->getPrefix());
}
// 17d
s << "=\"";
//step 17f - namespace case
s << an->getURI();
//step 17g
s << "\"";
}
}
// if (thisNodesNamespace)
// AncestorNamespaces->push (thisNodesNamespace->atom());
// step 18
if (!m_node->numChildren())
{
s << "/>";
return;
}
// step 19
s << ">";
// Added by mmorearty for the debugger
if (!includeChildren)
{
return;
}
// step 20
E4XNode *firstChild = m_node->_getAt(0);
AvmAssert(firstChild != 0);
bool bIndentChildren = ((_length() > 1) || (firstChild->getClass() & ~(E4XNode::kText | E4XNode::kCDATA)));
// step 21/22
int nextIndentLevel = 0;
if (toplevel()->xmlClass()->get_prettyPrinting() && bIndentChildren)
{
nextIndentLevel = indentLevel + toplevel()->xmlClass()->get_prettyIndent();
}
// We need to prune any namespaces with duplicate prefixes in our AncestorNamespace
// array to prevent shadowing of similar namespaces. Bug 153363.
// var x = <order xmlns:x="x">
// <item id="1" xmlns:x="x2">
// <menuName xmlns:x="x" x:foo='10'>burger</menuName>
// <price>3.95</price>
// </item>
// </order>;
//
// The namespace for menuName should be output even though the identical namespace
// was output for the top node. (Since the item node is using an incompatible
// namespace with the same prefix.)
AtomArray *newNamespaceArray = new (core->GetGC()) AtomArray();
uint32 anLen = AncestorNamespaces->getLength();
for (uint32 i = 0; i < anLen; i++)
{
Namespace *first = AvmCore::atomToNamespace(AncestorNamespaces->getAt(i));
if (i < origLength)
{
uint32 j;
for (j = origLength; j < anLen; j++)
{
Namespace *second = AvmCore::atomToNamespace(AncestorNamespaces->getAt(j));
if (second->getPrefix() == first->getPrefix())
{
break;
}
}
// No match, push our namespace on the list.
if (j == anLen)
{
newNamespaceArray->push (first->atom());
}
}
else
{
newNamespaceArray->push (first->atom());
}
}
uint32 namespaceLength = newNamespaceArray->getLength();
// step 23
for (uint32 i = 0; i < _length(); i++)
{
// step 23b
E4XNode *child = m_node->_getAt(i);
XMLObject *xo = new (core->GetGC()) XMLObject(toplevel()->xmlClass(), child);
if (toplevel()->xmlClass()->okToPrettyPrint() && bIndentChildren)
{
s << "\n";
}
xo->__toXMLString (s, newNamespaceArray, nextIndentLevel, includeChildren);
// Our __toXMLString call might have added new namespace onto our list. We don't want to
// save these new namespaces so clear them out here.
newNamespaceArray->setLength (namespaceLength);
}
// Part of the latest spec
if (toplevel()->xmlClass()->okToPrettyPrint() && bIndentChildren)
{
s << "\n";
}
//step 24
if (toplevel()->xmlClass()->okToPrettyPrint() && bIndentChildren)
{
for (int i = 0; i < indentLevel; i++)
{
s << " ";
}
}
//step 25
s << "</";
//step 26
if (nsPrefix != core->kEmptyString)
{
s << nsPrefix << ":";
}
//step 27
s << m.getName() << ">";
//step 28
return;
}
// E4X 12.2, page 59
// Support for for-in, for-each for XMLObjects
Atom XMLObject::nextName(int index)
{
AvmAssert(index > 0);
if (index == 1)
{
AvmCore *core = this->core();
return core->internInt (0)->atom();
}
else
{
return nullStringAtom;
}
}
Atom XMLObject::nextValue(int index)
{
AvmAssert(index > 0);
if (index == 1)
return this->atom();
else
return undefinedAtom;
}
int XMLObject::nextNameIndex(int index)
{
AvmAssert(index >= 0);
// XML types just return one value
if (index == 0)
return 1;
else
return 0;
}
XMLObject *XMLObject::AS3_addNamespace (Atom _namespace)
{
AvmCore *core = this->core();
if (core->isNamespace (_namespace))
{
m_node->_addInScopeNamespace (core, AvmCore::atomToNamespace(_namespace), publicNS);
}
else
{
Namespace *ns = core->newNamespace (_namespace);
m_node->_addInScopeNamespace (core, ns, publicNS);
_namespace = ns->atom();
}
nonChildChanges(xmlClass()->kNamespaceAdded, _namespace);
return this;
}
XMLObject *XMLObject::AS3_appendChild (Atom child)
{
AvmCore *core = this->core();
if(!(PoolObject::kbug444630 & traits()->pool->bugFlags))
{
if (AvmCore::isXML(child))
{
child = AvmCore::atomToXMLObject(child)->atom();
}
else if (AvmCore::isXMLList(child))
{
child = AvmCore::atomToXMLList(child)->atom();
}
else // all other types go through XML constructor as a string
{
child = xmlClass()->ToXML (core->string(child)->atom());
}
}
Atom children = getStringProperty(core->kAsterisk);
XMLListObject *cxl = AvmCore::atomToXMLList(children);
int index = _length();
cxl->setUintProperty (index, child);
return this;
}
XMLListObject *XMLObject::AS3_attribute (Atom arg)
{
// E4X 13.4.4.4
// name= ToAttributeName (attributeName);
// return [[get]](name)
return AvmCore::atomToXMLList(getAtomProperty(toplevel()->ToAttributeName(arg)->atom()));
}
XMLListObject *XMLObject::AS3_attributes ()
{
// E4X 13.4.4.5
// name= ToAttributeName ("*");
// return [[get]](name)
return AvmCore::atomToXMLList(getAtomProperty(toplevel()->ToAttributeName(core()->kAsterisk)->atom()));
}
XMLListObject *XMLObject::AS3_child (Atom P)
{
AvmCore *core = this->core();
// We have an integer argument - direct child lookup
uint32 index;
if (AvmCore::getIndexFromString (core->string(P), &index))
{
XMLListObject *xl = new (core->GetGC()) XMLListObject(toplevel()->xmlListClass());
if (index < m_node->numChildren())
{
xl->_appendNode (m_node->_getAt(index));
}
return xl;
}
return AvmCore::atomToXMLList(getAtomProperty(P));
}
int XMLObject::AS3_childIndex()
{
return m_node->childIndex();
}
XMLListObject *XMLObject::AS3_children ()
{
return AvmCore::atomToXMLList(getStringProperty(core()->kAsterisk));
}
// E4X 13.4.4.8, pg 75
XMLListObject *XMLObject::AS3_comments ()
{
AvmCore *core = this->core();
XMLListObject *l = new (core->GetGC()) XMLListObject(toplevel()->xmlListClass(), this->atom());
for (uint32 i = 0; i < m_node->_length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if (child->getClass() == E4XNode::kComment)
{
l->_appendNode (child);
}
}
return l;
}
// E4X 13.4.4.10, pg 75
bool XMLObject::AS3_contains (Atom value)
{
AvmCore *core = this->core();
// !!@ Rhino returns false for this case...
// var xml = new XML("simple");
// print ("contains: " + xml.contains ("simple"));
// ...which seems to imply that this routine is calling _equals and not
// does a "comparison x == value" as stated in the spec. We'll mimic
// Rhino for the time being but the correct behavior needs to be determined
if (this->atom() == value)
return true;
if (!AvmCore::isXML(value))
return false;
E4XNode *v = AvmCore::atomToXML(value);
return getNode()->_equals(toplevel(), core, v); // rhino
//SPEC - return (core()->equals (this->atom(), value) == trueAtom);
}
// E4X 13.4.4.11, pg 76
XMLObject *XMLObject::AS3_copy ()
{
return _deepCopy ();
}
// E4X 13.4.4.13, pg 76
XMLListObject *XMLObject::AS3_elements (Atom name) // name defaults to '*'
{
AvmCore *core = this->core();
Multiname m;
toplevel()->ToXMLName(name, m);
XMLListObject *l = new (core->GetGC()) XMLListObject(toplevel()->xmlListClass(), this->atom());
for (uint32 i = 0; i < _length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if (child->getClass() == E4XNode::kElement)
{
Multiname m2;
child->getQName(&m2, publicNS);
// if name.localName = "*" or name.localName =child->name.localName)
// and (name.uri == null) or (name.uri == child.name.uri))
if (m.matches(&m2))
{
// if name.localName = "*" or name.localName =child->name.localName)
// and (name.uri == null) or (name.uri == child.name.uri))
l->_appendNode (child);
}
}
}
return l;
}
// E4X 13.4.4.14, page 77
bool XMLObject::XML_AS3_hasOwnProperty (Atom P)
{
if (hasAtomProperty(P))
return true;
// if this has a property with name ToSString(P), return true;
// !!@ spec talks about prototype object being different from regular XML object
return false;
}
// E4X 13.4.4.15, page 77
bool XMLObject::AS3_hasComplexContent ()
{
return m_node->hasComplexContent();
}
// E4X 13.4.4.16, page 77
bool XMLObject::AS3_hasSimpleContent ()
{
return m_node->hasSimpleContent();
}
// E4X 13.4.4.17, page 78
ArrayObject *XMLObject::AS3_inScopeNamespaces ()
{
AvmCore *core = this->core();
// step 2
AtomArray *inScopeNS = new (core->GetGC()) AtomArray();
// step 3
m_node->BuildInScopeNamespaceList (core, inScopeNS);
ArrayObject *a = toplevel()->arrayClass->newArray(inScopeNS->getLength());
uint32 i;
for (i = 0; i < inScopeNS->getLength(); i++)
{
a->setUintProperty (i, inScopeNS->getAt(i));
}
// !!@ Rhino behavior always seems to return at least one NS
if (!inScopeNS->getLength())
{
// NOTE use caller's public
a->setUintProperty (i, core->findPublicNamespace()->atom());
}
return a;
}
// E4X 13.4.4.18, page 78
Atom XMLObject::AS3_insertChildAfter (Atom child1, Atom child2)
{
AvmCore *core = this->core();
Toplevel *toplevel = this->toplevel();
if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute | E4XNode::kCDATA))
return undefinedAtom;
if(!(PoolObject::kbug444630 & traits()->pool->bugFlags))
{
if (AvmCore::isXML(child2))
{
child2 = AvmCore::atomToXMLObject(child2)->atom();
}
else if (AvmCore::isXMLList(child2))
{
child2 = AvmCore::atomToXMLList(child2)->atom();
}
else // all other types go through XML constructor as a string
{
child2 = xmlClass()->ToXML (core->string(child2)->atom());
}
}
if (AvmCore::isNull(child1))
{
m_node->_insert (core, toplevel, 0, child2);
childChanges(xmlClass()->kNodeAdded, child2);
return this->atom();
}
else
{
E4XNode *c1 = AvmCore::atomToXML(child1);
// Errata extension to E4X spec - treat XMLList with length=1 as a XMLNode
if (!c1 && AvmCore::isXMLList(child1))
{
XMLListObject *xl = AvmCore::atomToXMLList(child1);
if (xl->_length() == 1)
c1 = xl->_getAt(0)->m_node;
}
if (c1)
{
for (uint32 i = 0; i < _length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if (child == c1)
{
m_node->_insert (core, toplevel, i + 1, child2);
childChanges(xmlClass()->kNodeAdded, child2);
return this->atom();
}
}
}
}
return undefinedAtom;
}
// E4X 13.4.4.19, page 79
Atom XMLObject::AS3_insertChildBefore (Atom child1, Atom child2)
{
AvmCore *core = this->core();
Toplevel *toplevel = this->toplevel();
if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute | E4XNode::kCDATA))
return undefinedAtom;
if(!(PoolObject::kbug444630 & traits()->pool->bugFlags))
{
if (AvmCore::isXML(child2))
{
child2 = AvmCore::atomToXMLObject(child2)->atom();
}
else if (AvmCore::isXMLList(child2))
{
child2 = AvmCore::atomToXMLList(child2)->atom();
}
else // all other types go through XML constructor as a string
{
child2 = xmlClass()->ToXML (core->string(child2)->atom());
}
}
if (AvmCore::isNull(child1))
{
m_node->_insert (core, toplevel, _length(), child2);
childChanges(xmlClass()->kNodeAdded, child2);
return this->atom();
}
else
{
E4XNode *c1 = AvmCore::atomToXML(child1);
// Errata extension to E4X spec - treat XMLList with length=1 as a XMLNode
if (!c1 && AvmCore::isXMLList(child1))
{
XMLListObject *xl = AvmCore::atomToXMLList(child1);
if (xl->_length() == 1)
c1 = xl->_getAt(0)->m_node;
}
if (c1)
{
for (uint32 i = 0; i < _length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if (child == c1)
{
m_node->_insert (core, toplevel, i, child2);
childChanges(xmlClass()->kNodeAdded, child2);
return this->atom();
}
}
}
}
return undefinedAtom;
}
// E4X 13.4.4.21, page 80
Atom XMLObject::AS3_localName ()
{
Multiname m;
if (m_node->getQName(&m, publicNS) == 0)
{
return nullStringAtom;
}
else
{
return m.getName()->atom();
}
}
// E4X 13.4.4.22, page 80
Atom XMLObject::AS3_name ()
{
AvmCore *core = this->core();
Multiname m;
if (!m_node->getQName(&m, publicNS))
return nullObjectAtom;
return (new (core->GetGC(), toplevel()->qnameClass()->ivtable()->getExtraSize()) QNameObject(toplevel()->qnameClass(), m))->atom();
}
// E4X 13.4.4.23, page 80
Atom XMLObject::_namespace (Atom p_prefix, int argc) // prefix is optional
{
AvmAssert(argc == 0 || argc == 1);
AvmCore *core = this->core();
// step 2
AtomArray *inScopeNS = new (core->GetGC()) AtomArray();
// step 3
m_node->BuildInScopeNamespaceList (core, inScopeNS);
// step 5
if (!argc)
{
// step 5a
if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kCDATA | E4XNode::kProcessingInstruction))
return nullObjectAtom;
// step 5b
// Return the result of calling [[GetNamespace]] method of
// x.[[Name]] with argument inScopeNS
Multiname m;
AvmAssert(getQName(&m));
getQName(&m);
Namespace *ns = GetNamespace (m, inScopeNS);
return (ns->atom());
}
else
{
Atom prefix = core->internString(core->string (p_prefix))->atom();
for (uint32 i = 0; i < inScopeNS->getLength(); i++)
{
Namespace *ns = AvmCore::atomToNamespace (inScopeNS->getAt(i));
if (ns->getPrefix() == prefix)
return ns->atom();
}
return undefinedAtom;
}
}
// 13.4.4.24, pg 80-81
ArrayObject *XMLObject::AS3_namespaceDeclarations ()
{
AvmCore *core = this->core();
ArrayObject *a = toplevel()->arrayClass->newArray();
if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute | E4XNode::kCDATA))
return a;
E4XNode *y = m_node->getParent();
// step 4+5
AtomArray *ancestorNS = new (core->GetGC()) AtomArray();
if (y)
y->BuildInScopeNamespaceList (core, ancestorNS);
uint32 arrayIndex = 0;
// step 7+8+9+10
for (uint32 i = 0; i < m_node->numNamespaces(); i++)
{
Namespace *ns = AvmCore::atomToNamespace (m_node->getNamespaces()->getAt(i));
if (!ns->hasPrefix ())
{
// Emulating Rhino behavior
if (ns->getURI() != core->kEmptyString)
{
bool bMatch = false;
for (uint32 j = 0; j < ancestorNS->getLength(); j++)
{
Namespace *ns2 = AvmCore::atomToNamespace (ancestorNS->getAt(j));
if (ns->getURI() == ns2->getURI())
{
bMatch = true;
break;
}
}
if (!bMatch)
{
a->setUintProperty (arrayIndex++, ns->atom());
}
}
}
else // ns.prefix is NOT empty
{
bool bMatch = false;
for (uint32 j = 0; j < ancestorNS->getLength(); j++)
{
Namespace *ns2 = AvmCore::atomToNamespace (ancestorNS->getAt(j));
if (ns->getPrefix() == ns2->getPrefix() && ns->getURI() == ns2->getURI())
{
bMatch = true;
break;
}
}
if (!bMatch)
{
a->setUintProperty (arrayIndex++, ns->atom());
}
}
}
return a;
}
String *XMLObject::AS3_nodeKind () const
{
return m_node->nodeKind(toplevel());
}
XMLObject *XMLObject::AS3_normalize ()
{
AvmCore* core = this->core();
bool notify = notifyNeeded(getNode());
uint32 i = 0;
while (i < _length())
{
E4XNode *x = m_node->_getAt(i);
if (x->getClass() == E4XNode::kElement)
{
XMLObject *xo = new (core->GetGC()) XMLObject(toplevel()->xmlClass(), x);
xo->normalize();
delete xo;
i++;
}
else if (x->getClass() & (E4XNode::kText | E4XNode::kCDATA))
{
Stringp prior = x->getValue();
while (((i + 1) < _length()) && (m_node->_getAt(i + 1)->getClass() & (E4XNode::kText | E4XNode::kCDATA)))
{
E4XNode *x2 = m_node->_getAt(i + 1);
x->setValue (core->concatStrings(x->getValue(), x2->getValue()));
m_node->_deleteByIndex (i + 1);
if (notify)
{
XMLObject *nd = new (core->GetGC()) XMLObject (xmlClass(), x2);
childChanges(xmlClass()->kNodeRemoved, nd->atom());
}
}
/// Need to check if string is "empty" - 0 length or filled with whitespace
if (x->getValue()->isWhitespace())
{
E4XNode* prior = m_node->_getAt(i);
m_node->_deleteByIndex (i);
if (notify)
{
XMLObject *nd = new (core->GetGC()) XMLObject (xmlClass(), prior);
childChanges(xmlClass()->kNodeRemoved, nd->atom());
}
}
else
{
i++;
}
// notify if the node has changed value
Stringp current = x->getValue();
if ((current != prior) && notify)
{
XMLObject *xo = new (core->GetGC()) XMLObject (xmlClass(), x);
xo->nonChildChanges(xmlClass()->kTextSet, current->atom(), (prior) ? prior->atom() : undefinedAtom);
}
}
else
{
i++;
}
}
return this;
}
Atom XMLObject::AS3_parent ()
{
if (m_node->getParent())
return (new (core()->GetGC()) XMLObject (toplevel()->xmlClass(), m_node->getParent()))->atom();
else
return undefinedAtom;
}
XMLListObject *XMLObject::AS3_processingInstructions (Atom name) // name defaults to '*'
{
AvmCore *core = this->core();
Multiname m;
toplevel()->ToXMLName(name, m);
XMLListObject *xl = new (core->GetGC()) XMLListObject(toplevel()->xmlListClass(), this->atom());
if (m.isAttr())
return xl;
for (uint32 i = 0; i < m_node->_length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if (child->getClass() == E4XNode::kProcessingInstruction)
{
Multiname m2;
bool bFound = child->getQName(&m2, publicNS);
// if name.localName = "*" or name.localName =child->name.localName)
// and (name.uri == null) or (name.uri == child.name.uri))
if (m.matches(bFound ? &m2 : 0))
{
xl->_appendNode (child);
}
}
}
return xl;
}
XMLObject *XMLObject::AS3_prependChild (Atom value)
{
AvmCore *core = this->core();
Toplevel *toplevel = this->toplevel();
if(!(PoolObject::kbug444630 & traits()->pool->bugFlags))
{
if (AvmCore::isXML(value))
{
value = AvmCore::atomToXMLObject(value)->atom();
}
else if (AvmCore::isXMLList(value))
{
value = AvmCore::atomToXMLList(value)->atom();
}
else // all other types go through XML constructor as a string
{
value = xmlClass()->ToXML (core->string(value)->atom());
}
}
m_node->_insert (core, toplevel, 0, value);
childChanges(xmlClass()->kNodeAdded, value);
return this;
}
bool XMLObject::XML_AS3_propertyIsEnumerable(Atom P) // NOT virtual, not an override
{
AvmCore *core = this->core();
if (core->intern(P) == core->internConstantStringLatin1("0"))
return true;
return false;
}
// 13.4.4.31, pg 83
XMLObject *XMLObject::AS3_removeNamespace (Atom nsAtom)
{
AvmCore *core = this->core();
if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute | E4XNode::kCDATA))
return this;
Namespace *ns = core->isNamespace (nsAtom) ? AvmCore::atomToNamespace (nsAtom) : core->newNamespace (nsAtom);
Multiname m;
AvmAssert(getQName(&m));
getQName(&m);
Namespace *thisNS = GetNamespace (m, m_node->getNamespaces());
// step 4
if (thisNS == ns)
return this;
//step 5
for (uint32 j = 0; j < m_node->numAttributes(); j++)
{
E4XNode *a = m_node->getAttribute(j);
Multiname m;
AvmAssert(a->getQName(&m, publicNS));
a->getQName(&m, publicNS);
Namespace *anNS = GetNamespace (m, m_node->getNamespaces());
if (anNS == ns)
return this;
}
// step 6+7
int32 i = m_node->FindMatchingNamespace (core, ns);
if (i != -1)
{
m_node->getNamespaces()->removeAt(i);
}
// step 8
for (uint32 k = 0; k < _length(); k++)
{
E4XNode *p = m_node->_getAt(k);
if (p->getClass() == E4XNode::kElement)
{
XMLObject *xo = new (core->GetGC()) XMLObject(toplevel()->xmlClass(), p);
xo->removeNamespace (ns->atom());
delete xo;
}
}
// step 9
// Note about namespaces in ancestors and parents, etc.
nonChildChanges(xmlClass()->kNamespaceRemoved, ns->atom());
return this;
}
XMLObject *XMLObject::AS3_replace (Atom P, Atom value)
{
AvmCore *core = this->core();
Toplevel *toplevel = this->toplevel();
if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute | E4XNode::kCDATA))
return this;
Atom c;
if (AvmCore::isXML(value))
{
XMLObject *x = AvmCore::atomToXMLObject(value);
c = x->_deepCopy()->atom();
}
else if (AvmCore::isXMLList(value))
{
XMLListObject *xl = AvmCore::atomToXMLList(value);
c = xl->_deepCopy()->atom();
}
else
{
if(!(PoolObject::kbug444630 & traits()->pool->bugFlags))
c = xmlClass()->ToXML (core->string(value)->atom());
else
c = core->string(value)->atom();
}
uint32 index;
if (AvmCore::getIndexFromString (core->string(P), &index))
{
E4XNode* prior = m_node->_replace (core, toplevel, index, c);
childChanges(xmlClass()->kNodeChanged, c, prior);
return this;
}
QNameObject *qn1 = new (core->GetGC(), toplevel->qnameClass()->ivtable()->getExtraSize()) QNameObject(toplevel->qnameClass(), P);
Multiname m;
qn1->getMultiname(m);
bool notify = notifyNeeded(getNode());
int i = -1;
for (int k = int(_length()) - 1; k >= 0; k--)
{
E4XNode *x = m_node->_getAt (k);
Multiname *m2 = 0;
// m3 needs to exist outside this if scope since m2 will point to it
Multiname m3;
if (x->getClass() == E4XNode::kElement)
{
if (x->getQName(&m3, publicNS))
m2 = &m3;
}
if (m.matches(m2))
{
if (i != -1)
{
E4XNode* was = m_node->_getAt(i);
m_node->_deleteByIndex (i);
// notify
if (notify && was->getClass() == E4XNode::kElement)
{
XMLObject* nd = new (core->GetGC()) XMLObject (xmlClass(), was);
childChanges(xmlClass()->kNodeRemoved, nd->atom());
}
}
i = k;
}
}
delete qn1;
if (i == -1)
return this;
E4XNode* prior = m_node->_replace (core, toplevel, i, c);
childChanges( (prior) ? xmlClass()->kNodeChanged : xmlClass()->kNodeAdded, c, prior);
return this;
}
XMLObject *XMLObject::AS3_setChildren (Atom value)
{
setStringProperty(core()->kAsterisk, value);
return this;
}
void XMLObject::AS3_setLocalName (Atom name)
{
if (m_node->getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kCDATA))
return;
AvmCore *core = this->core();
QNameObject *qn = AvmCore::atomToQName(name);
Stringp newname;
if (qn)
{
newname = qn->get_localName();
}
else
{
newname = core->intern(name);
}
if (!core->isXMLName(newname->atom()))
toplevel()->throwTypeError(kXMLInvalidName, newname);
Multiname m;
if (this->getNode()->getQName(&m, publicNS))
{
Multiname previous;
getNode()->getQName(&previous, publicNS);
Stringp prior = previous.getName();
m.setName (newname);
getNode()->setQName (core, &m);
nonChildChanges(xmlClass()->kNameSet, m.getName()->atom(), (prior) ? prior->atom() : undefinedAtom );
}
return;
}
void XMLObject::AS3_setName (Atom name)
{
AvmCore *core = this->core();
if (m_node->getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kCDATA))
return;
if (AvmCore::isQName(name))
{
QNameObject *q = AvmCore::atomToQName(name);
if (AvmCore::isNull(q->getURI()))
{
name = q->get_localName()->atom();
}
}
QNameObject *n = new (core->GetGC(), toplevel()->qnameClass()->ivtable()->getExtraSize()) QNameObject(toplevel()->qnameClass(), name);
Stringp s = n->get_localName();
if (!core->isXMLName(s->atom()))
toplevel()->throwTypeError(kXMLInvalidName, s);
Multiname m;
if (m_node->getQName(&m, publicNS))
{
if (m_node->getClass() == E4XNode::kProcessingInstruction)
{
m_node->setQName (core, n->get_localName(), core->findPublicNamespace());
}
else // only for attribute and element nodes
{
Multiname m2;
n->getMultiname (m2);
m_node->setQName (core, &m2);
// ISNS changes
if (n->getURI() != core->kEmptyString->atom())
{
m_node->getQName(&m, publicNS); // get our new multiname
if (this->getClass() == E4XNode::kAttribute && getNode()->getParent())
{
getNode()->getParent()->_addInScopeNamespace (core, m.getNamespace(), publicNS);
}
else if (this->getClass() == E4XNode::kElement)
{
getNode()->_addInScopeNamespace (core, m.getNamespace(), publicNS);
}
}
}
nonChildChanges(xmlClass()->kNameSet, name, m.getName()->atom());
}
return;
}
void XMLObject::AS3_setNamespace (Atom ns)
{
AvmCore *core = this->core();
if (m_node->getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kCDATA))
return;
Namespace* newns = core->newNamespace (ns);
Multiname m;
if (m_node->getQName(&m, publicNS))
{
m_node->setQName (core, m.getName(), newns);
}
// ISNS changes
if (this->getClass() == E4XNode::kAttribute && getNode()->getParent())
{
getNode()->getParent()->_addInScopeNamespace (core, newns, publicNS);
}
else if (this->getClass() == E4XNode::kElement)
{
getNode()->_addInScopeNamespace (core, newns, publicNS);
}
nonChildChanges(xmlClass()->kNamespaceSet, newns->atom());
return;
}
XMLListObject *XMLObject::AS3_text ()
{
XMLListObject *l = new (gc()) XMLListObject(toplevel()->xmlListClass(), this->atom());
for (uint32 i = 0; i < m_node->_length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if (child->getClass() & (E4XNode::kText | E4XNode::kCDATA))
{
l->_appendNode (child);
}
}
return l;
}
// E4X 10.1, page 28
Atom XMLObject::toString ()
{
AvmCore *core = this->core();
if (getClass() & (E4XNode::kText | E4XNode::kCDATA | E4XNode::kAttribute))
{
return m_node->getValue()->atom();
}
if (hasSimpleContent())
{
Stringp s = core->kEmptyString;
for (uint32 i = 0; i < _length(); i++)
{
E4XNode *child = m_node->_getAt(i);
if ((child->getClass() != E4XNode::kComment) && (child->getClass() != E4XNode::kProcessingInstruction))
{
XMLObject *xo = new (core->GetGC()) XMLObject(toplevel()->xmlClass(), child);
s = core->concatStrings(s, core->string(xo->toString()));
delete xo;
}
}
return s->atom();
}
else
{
AtomArray *AncestorNamespaces = new (core->GetGC()) AtomArray();
StringBuffer s(core);
__toXMLString(s, AncestorNamespaces, 0);
return core->newStringUTF8(s.c_str())->atom();
}
}
Stringp XMLObject::AS3_toString()
{
return core()->atomToString(toString());
}
String *XMLObject::AS3_toXMLString ()
{
AtomArray *AncestorNamespaces = new (MMgc::GC::GetGC(this)) AtomArray();
StringBuffer s(core());
__toXMLString(s, AncestorNamespaces, 0);
return core()->newStringUTF8(s.c_str());
}
#ifdef AVMPLUS_VERBOSE
Stringp XMLObject::format(AvmCore* core) const
{
//
// [mmorearty 10/24/05] Flex Builder 2.0 relies on this format in order to
// have a nice display of XML in the Variables view:
//
// "XML@hexaddr nodeKind text_to_display"
//
AtomArray *AncestorNamespaces = new (core->GetGC()) AtomArray();
StringBuffer openTag(core);
__toXMLString(openTag, AncestorNamespaces, 0, false);
Stringp openingTag = core->newStringUTF8(openTag.c_str());
Stringp result = ScriptObject::format(core);
result = result->appendLatin1(" ");
result = core->concatStrings(result, nodeKind());
result = result->appendLatin1(" ");
result = core->concatStrings(result, openingTag);
return result;
}
#endif
int XMLObject::getClass() const
{
return m_node->getClass() ;
}
uint32 XMLObject::_length() const
{
return m_node->_length();
}
XMLObject *XMLObject::getParent()
{
if (m_node->getParent())
return new (core()->GetGC()) XMLObject (toplevel()->xmlClass(), m_node->getParent());
else
return 0;
}
void XMLObject::setValue(Stringp s)
{
m_node->setValue (s);
}
Stringp XMLObject::getValue()
{
return m_node->getValue();
}
bool XMLObject::getQName(Multiname *m)
{
return m_node->getQName(m, publicNS);
}
Atom XMLObject::AS3_setNotification(FunctionObject* f)
{
AvmCore* core = this->core();
// Notifiers MUST be functions or null
if (f && !AvmCore::istype(f->atom(), core->traits.function_itraits))
toplevel()->throwArgumentError( kInvalidArgumentError, "f");
else
m_node->setNotification(core, f, publicNS);
// since AS3 sez this returns an Atom, our implementation must do so.
return undefinedAtom;
}
FunctionObject* XMLObject::AS3_notification()
{
return m_node->getNotification();
}
bool XMLObject::notifyNeeded(E4XNode* initialTarget)
{
// do a quick probe to see if we need to issue any notifications
bool hit = false;
E4XNode* node = initialTarget;
while(node)
{
if (node->getNotification())
{
hit = true;
break;
}
node = node->getParent();
}
return hit;
}
/**
* Notification on generic node addition from XML or XMLList
*/
void XMLObject::childChanges(Stringp type, Atom value, E4XNode* prior)
{
AvmCore* core = this->core();
Toplevel* top = this->toplevel();
E4XNode* initialTarget = m_node;
if (notifyNeeded(initialTarget))
{
XMLObject* target = new (core->GetGC()) XMLObject(top->xmlClass(), initialTarget);
Atom detail = undefinedAtom;
if (prior)
{
XMLObject* xml = new (core->GetGC()) XMLObject(xmlClass(), prior);
detail = xml->atom();
}
if (AvmCore::isXML(value))
{
issueNotifications(core, top, initialTarget, target->atom(), type, value, detail);
}
else if (AvmCore::isXMLList(value))
{
// if its a list each element in the list is added.
XMLListObject* xl = AvmCore::atomToXMLList(value);
if (xl)
{
issueNotifications(core, top, initialTarget, target->atom(), type, xl->atom(), detail);
}
else
{
AvmAssert(false);
}
}
else
{
// non child updates
}
}
}
void XMLObject::nonChildChanges(Stringp type, Atom value, Atom detail)
{
AvmCore* core = this->core();
Toplevel* top = this->toplevel();
E4XNode* initialTarget = m_node;
if (notifyNeeded(initialTarget))
{
XMLObject* target = new (core->GetGC()) XMLObject(top->xmlClass(), initialTarget);
issueNotifications(core, top, initialTarget, target->atom(), type, value, detail);
}
}
/**
* Perform the callback for each node in which the notification property is set.
*/
void XMLObject::issueNotifications(AvmCore* core, Toplevel* top, E4XNode* initialTarget, Atom target, Stringp type, Atom value, Atom detail)
{
// start notification at initialtarget
E4XNode* volatile node = initialTarget;
while(node)
{
// check if notification param set
ScriptObject* methodObj = node->getNotification();
if (methodObj)
{
XMLObject* currentTarget = new (core->GetGC()) XMLObject(top->xmlClass(), node);
Atom argv[6] = { top->atom(), currentTarget->atom(), type->atom(), target, value, detail };
int argc = 5;
//EnterScriptTimeout enterScriptTimeout(core);
TRY(core, kCatchAction_Rethrow)
{
methodObj->call(argc, argv);
}
CATCH(Exception *exception)
{
// you chuck, we chuck
core->throwException(exception);
}
END_CATCH
END_TRY
}
// bubble up
node = node->getParent();
}
}
#ifdef XML_FILTER_EXPERIMENT
XMLListObject * XMLObject::filter (Atom propertyName, Atom value)
{
Multiname m;
toplevel()->ToXMLName(propertyName, m);
Multiname name;
toplevel()->CoerceE4XMultiname(&m, name);
// filter opcode experiment
XMLListObject *l = new (core()->gc) XMLListObject(toplevel()->xmlListClass(), nullObjectAtom);
this->_filter (l, name, value);
return l;
}
void XMLObject::_filter (XMLListObject *l, const Multiname &name, Atom value)
{
AvmCore *core = this->core();
if (!name.isAnyName())
{
// We have an integer argument - direct child lookup
Stringp nameString = name.getName();
uint32 index;
if (AvmCore::getIndexFromString (nameString, &index))
{
if (index == 0)
{
if (core->equals (this->atom(), value))
{
l->_append (this->getNode());
}
}
}
}
if (name.isAttr())
{
// for each a in x.[[attributes]]
for (uint32 i = 0; i < m_node->numAttributes(); i++)
{
E4XNode *xml = m_node->getAttribute(i);
AvmAssert(xml && xml->getClass() == E4XNode::kAttribute);
Multiname m;
AvmAssert(xml->getQName(&m, publicNS) != 0);
xml->getQName(&m, publicNS);
if (name.matches(&m))
{
if (core->equals(xml->getValue()->atom(), value) == trueAtom)
l->_append (xml);
}
}
return;
}
for (uint32 i = 0; i < m_node->numChildren(); i++)
{
E4XNode *child = m_node->_getAt(i);
Multiname m;
Multiname *m2 = 0;
if (child->getClass() == E4XNode::kElement)
{
child->getQName(&m, publicNS);
m2 = &m;
}
if (name.matches(m2))
{
// If we're an element node, we do something more complicated than a string compare
if (child->getClass() == E4XNode::kElement)
{
// Hacky swaping of our XMLObject's node ptr to point to the child
// node so we can call out to AvmCore::eq with an atom.
E4XNode *savedNode = this->m_node;
this->m_node = child;
if (core->equals(this->atom(), value) == trueAtom)
l->_append (child);
this->m_node = savedNode;
}
else
{
// !!@ this needs testing with comments/PI/text/etc.
if (core->equals(child->getValue()->atom(), value) == trueAtom)
l->_append (child);
}
}
}
}
#endif // XML_FILTER_EXPERIMENT
void XMLObject::dispose()
{
m_node->dispose();
}
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
QNameObject::QNameObject (QNameClass *factory, const Multiname &name)
: ScriptObject(factory->ivtable(), factory->prototype), m_mn(name)
{
}
/**
* QNameObject is used to represent the "QName" object in the E4X Specification.
*
* We also use this same object to represent "AttributeName" in the E4X spec.
* An AttributeName is simply a QName wrapper for finding properties that have a leading @ sign.
* It's an internal class to the spec and the only difference between a QName is the @. Instead of
* having the overhead of an AttributeName class that wraps the QName class, we just use a boolean
* inside the QName to differentiate betweent the two types.
*/
QNameObject::QNameObject(QNameClass *factory, Namespace *ns, Atom nameatom, bool bA)
: ScriptObject(factory->ivtable(), factory->prototype)
{
AvmCore *core = this->core();
Stringp name;
if (AvmCore::isQName(nameatom))
{
QNameObject *q = AvmCore::atomToQName(nameatom);
name = q->m_mn.getName();
}
else if (nameatom == undefinedAtom)
{
name = core->kEmptyString;
}
else
{
name = core->intern(nameatom);
}
Multiname mn;
// Set attribute bit in multiname
if (bA)
mn.setAttr();
if (name == core->kAsterisk)
{
mn.setAnyName();
AvmAssert(mn.isAnyName());
}
else
{
mn.setName(name);
}
if (ns == NULL)
{
mn.setAnyNamespace();
}
else
{
mn.setNamespace(core->internNamespace(ns));
mn.setQName();
}
this->m_mn = mn;
}
/**
* called when no namespace specified.
*/
QNameObject::QNameObject(QNameClass *factory, Atom nameatom, bool bA)
: ScriptObject(factory->ivtable(), factory->prototype)
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
Multiname mn;
if (AvmCore::isQName(nameatom))
{
QNameObject *q = AvmCore::atomToQName(nameatom);
mn = q->m_mn;
}
else
{
Stringp name = core->intern(nameatom);
if (name == core->kAsterisk)
{
mn.setAnyNamespace();
mn.setAnyName();
AvmAssert(mn.isAnyName());
}
else
{
if (nameatom == undefinedAtom)
{
mn.setName(core->kEmptyString);
}
else
{
mn.setName(name);
}
Namespacep ns = ApiUtils::getVersionedNamespace(core, toplevel->getDefaultNamespace(), core->getAPI(NULL));
mn.setNamespace(ns);
}
}
// Set attribute bit in multiname
if (bA)
mn.setAttr();
this->m_mn = mn;
}
Stringp QNameObject::get_localName() const
{
if (this->m_mn.isAnyName())
return core()->kAsterisk;
return m_mn.getName();
}
Atom QNameObject::getURI() const
{
if (m_mn.isAnyNamespace())
{
return nullStringAtom;
}
else if (m_mn.namespaceCount() > 1)
{
return core()->kEmptyString->atom();
}
else
{
return m_mn.getNamespace()->getURI()->atom();
}
}
Atom QNameObject::get_uri() const
{
return getURI();
}
// E4X 13.3.5.4, pg 69
Namespace *XMLObject::GetNamespace (const Multiname &mn, const AtomArray *nsArray) const
{
AvmCore *core = this->core();
Stringp uri = (mn.isAnyNamespace() ? 0 : mn.getNamespace()->getURI());
if (nsArray)
{
for (uint32 i = 0; i < nsArray->getLength(); i++)
{
Namespace *ns = AvmCore::atomToNamespace (nsArray->getAt(i));
AvmAssert(ns!=NULL);
#ifdef STRING_DEBUG
Stringp s1 = ns->getURI();
Stringp s2 = uri;
#endif // STRING_DEBUG
if (ns->getURI() == uri)
{
return ns;
}
}
}
// not found, return empty namespace based upon this QName's uri.
return core->newNamespace (uri->atom());
}
// Iterator support - for in, for each
Atom QNameObject::nextName(int index)
{
AvmAssert(index > 0);
// first return "uri" then "localName"
if (index == 1)
return toplevel()->qnameClass()->kUri;
else if (index == 2)
return toplevel()->qnameClass()->kLocalName;
else
return nullObjectAtom;
}
Atom QNameObject::nextValue(int index)
{
AvmAssert(index > 0);
// first return uri then localName
if (index == 1)
return this->get_localName()->atom();
else if (index == 2)
return this->getURI();
else
return nullStringAtom;
}
int QNameObject::nextNameIndex(int index)
{
AvmAssert(index >= 0);
if (index < 2)
return index + 1;
else
return 0;
}
}