Partly implement XML child and elements

This makes #3294 (rollercoaster-creator-2) fully playable.

Missing is any (*) matching for child()/elements() and the existing attributes() method.
Also missing is support for number indexes with child().
This commit is contained in:
Tom Schuster 2023-03-11 23:31:09 +01:00 committed by Aaron Hill
parent ea3fdfa27e
commit f53c83cfa4
2 changed files with 77 additions and 20 deletions

View File

@ -10,7 +10,9 @@ package {
AS3 native function name():Object; AS3 native function name():Object;
AS3 native function localName():Object; AS3 native function localName():Object;
AS3 native function toXMLString():String; AS3 native function toXMLString():String;
AS3 native function child(name:Object):XMLList;
AS3 native function children():XMLList; AS3 native function children():XMLList;
AS3 native function elements(name:*):XMLList;
AS3 native function attributes():XMLList; AS3 native function attributes():XMLList;
AS3 native function attribute(name:*):XMLList; AS3 native function attribute(name:*):XMLList;
@ -36,11 +38,21 @@ package {
return self.AS3::toXMLString(); return self.AS3::toXMLString();
}; };
prototype.children = function():String { prototype.child = function(name:Object):XMLList {
var self:XML = this;
return self.AS3::child(name);
};
prototype.children = function():XMLList {
var self:XML = this; var self:XML = this;
return self.AS3::children(); return self.AS3::children();
}; };
prototype.elements = function(name:*):XMLList {
var self:XML = this;
return self.AS3::elements(name);
}
prototype.toString = function():String { prototype.toString = function():String {
if (this === prototype) { if (this === prototype) {
return ""; return "";

View File

@ -88,6 +88,50 @@ pub fn to_xml_string<'gc>(
Ok(Value::String(node.xml_to_xml_string(activation)?)) Ok(Value::String(node.xml_to_xml_string(activation)?))
} }
fn name_to_multiname<'gc>(
activation: &mut Activation<'_, 'gc>,
name: &Value<'gc>,
) -> Result<Multiname<'gc>, Error<'gc>> {
Ok(match name {
Value::String(s) => Multiname::new(activation.avm2().public_namespace, *s),
Value::Object(o) => {
if let Some(qname) = o.as_qname_object() {
qname.name().clone()
} else {
Multiname::new(
activation.avm2().public_namespace,
name.coerce_to_string(activation)?,
)
}
}
_ => Multiname::new(
activation.avm2().public_namespace,
name.coerce_to_string(activation)?,
),
})
}
pub fn child<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let xml = this.unwrap().as_xml_object().unwrap();
let multiname = name_to_multiname(activation, &args[0])?;
// FIXME: Support numerical indexes.
let children = if let E4XNodeKind::Element { children, .. } = &*xml.node().kind() {
children
.iter()
.filter(|node| node.matches_name(&multiname))
.map(|node| E4XOrXml::E4X(*node))
.collect()
} else {
Vec::new()
};
Ok(XmlListObject::new(activation, children, Some(xml.into())).into())
}
pub fn children<'gc>( pub fn children<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
@ -103,6 +147,25 @@ pub fn children<'gc>(
Ok(XmlListObject::new(activation, children, Some(xml.into())).into()) Ok(XmlListObject::new(activation, children, Some(xml.into())).into())
} }
pub fn elements<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let xml = this.unwrap().as_xml_object().unwrap();
let children = if let E4XNodeKind::Element { children, .. } = &*xml.node().kind() {
children
.iter()
.filter(|node| matches!(&*node.kind(), E4XNodeKind::Element { .. }))
.map(|node| E4XOrXml::E4X(*node))
.collect()
} else {
Vec::new()
};
Ok(XmlListObject::new(activation, children, Some(xml.into())).into())
}
pub fn attributes<'gc>( pub fn attributes<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
@ -126,25 +189,7 @@ pub fn attribute<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let this = this.unwrap(); let this = this.unwrap();
let xml = this.as_xml_object().unwrap(); let xml = this.as_xml_object().unwrap();
let name = args[0]; let multiname = name_to_multiname(activation, &args[0])?;
let multiname = match name {
Value::String(s) => Multiname::new(activation.avm2().public_namespace, s),
Value::Object(o) => {
if let Some(qname) = o.as_qname_object() {
qname.name().clone()
} else {
Multiname::new(
activation.avm2().public_namespace,
name.coerce_to_string(activation)?,
)
}
}
_ => Multiname::new(
activation.avm2().public_namespace,
name.coerce_to_string(activation)?,
),
};
let attribute = if let E4XNodeKind::Element { attributes, .. } = &*xml.node().kind() { let attribute = if let E4XNodeKind::Element { attributes, .. } = &*xml.node().kind() {
attributes attributes
.iter() .iter()