Skip to content

Commit

Permalink
Cache filtered children in XML (#7998)
Browse files Browse the repository at this point in the history
We currently scan the entire child list for each call to children, num_children, and get_child_element.

Instead, we should cache the filtered child list on first access.
  • Loading branch information
GregoryTravis authored Oct 6, 2023
1 parent f348083 commit a90af9f
Showing 1 changed file with 21 additions and 9 deletions.
30 changes: 21 additions & 9 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/XML.enso
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ type XML_Document
root_element self =
XML_Error.handle_java_exceptions <|
java_element = self.java_document.getDocumentElement
XML_Element.Value java_element
XML_Element.new java_element

## PRIVATE
Convert to a JavaScript Object representing this XML_Document.
Expand Down Expand Up @@ -159,7 +159,7 @@ type XML_Element
get : Text | Integer -> Any -> Any | Text | XML_Element | Vector (Text | XML_Element) ! No_Such_Key | Index_Out_Of_Bounds | XML_Error
get self key:(Text|Integer) ~if_missing=Nothing =
case key of
_ : Integer -> self.children.get key if_missing
_ : Integer -> self.children_cache.get key if_missing
_ : Text -> if is_attribute_key key then self.get_xpath key . get 0 if_missing else self.get_xpath key

## Gets a child or attribute of an XML element.
Expand Down Expand Up @@ -215,9 +215,7 @@ type XML_Element
XML_Document.from_text '<foo><baz>hello</baz></foo>' . root_element . children
# => [XML_Document.from_text "<baz>hello</baz>"]
children : Vector (XML_Element | Text) ! XML_Error
children self =
XML_Error.handle_java_exceptions <|
only_wanted_nodes self.java_element.getChildNodes
children self = self.children_cache

## Gets the number children of an XML element.

Expand All @@ -231,7 +229,7 @@ type XML_Element
XML_Document.from_text '<foo> <bar>hello</bar> <bar>hello2</bar>< </foo>' . root_element . child_count
# => 2
child_count : Integer ! XML_Error
child_count self = self.children.length
child_count self = self.children_cache.length

## Get an attribute of an XML element.

Expand Down Expand Up @@ -328,11 +326,17 @@ type XML_Element
builder.append ["type", "XML_Element"]
builder.append ["tag", self.name]
builder.append ["attributes", self.attributes.to_js_object]
builder.append ["children", self.children.to_js_object]
builder.append ["children", self.children_cache.to_js_object]
JS_Object.from_pairs builder.to_vector

## PRIVATE
Value (java_element:Element)

Build a new XML_Element, populating the lazy `children_cache` field.
new : Element -> XML_Element
new java_element = XML_Element.Value java_element (build_child_list java_element)

## PRIVATE
Value (java_element:Element) (~children_cache:(Vector (XML_Element | Text)))

type XML_Error
# An error that indicates that the XML data could not be parsed.
Expand Down Expand Up @@ -387,12 +391,20 @@ only_wanted_nodes node_list:NodeList =

# If an Element, wrap in XML_Element. If Java_Text, extract the string. If an attribute, extract the value.
convert node =
if node.getNodeType == Node.ELEMENT_NODE then XML_Element.Value node else
if node.getNodeType == Node.ELEMENT_NODE then XML_Element.new node else
if node.getNodeType == Node.TEXT_NODE then node.getNodeValue else
if node.getNodeType == Node.ATTRIBUTE_NODE then node.getValue else
Panic.throw (Illegal_State.Error ("Unexpected child type " + node.getNodeType.to_text))
nodes.filter is_wanted . map convert

## PRIVATE

Build the child list, filtering out unwanted child nodes.
build_child_list : Element -> Vector (XML_Element | Text) ! XML_Error
build_child_list java_element =
XML_Error.handle_java_exceptions <|
only_wanted_nodes java_element.getChildNodes

## PRIVATE
Returns true if `key` starts with "@".
is_attribute_key : Text -> Boolean
Expand Down

0 comments on commit a90af9f

Please sign in to comment.