bmx-ng / brl.mod

BlitzMax Runtime Libraries, for BlitzMax NG.
12 stars 11 forks source link

Brl.xml: expose mxmlFindPath() and mxmlFindElement() #91

Open GWRon opened 5 years ago

GWRon commented 5 years ago

Low priority:

libXML has some pretty mighty xpath functionality - but mxml provides a simple approach to fetch and find nodes too: mxmlFindPath() and mxmlFindElement().

Might be a good idea to expose them to brl.xml too.

GWRon commented 5 years ago

mxmlFindPath()

xml.bmx - TxmlNode add:

    Rem
    bbdoc: Get the first child node with the given path
    about:
    (From the mxml-docs:) The first child node of the found node is
    returned if the given node has children and the first child is a
    value node.
    returns: node or null
    End Rem
    Method FindPath:TxmlNode(path:String)
        return TxmlNode._create(bmx_mxmlFindPath(nodePtr, path))
    End Method

glue.c add:

mxml_node_t * bmx_mxmlFindPath( mxml_node_t * top, BBString * path) {
    char * charPath = bbStringToUTF8String(path);
    mxml_node_t * node = mxmlFindPath(top, charPath);
    bbMemFree(charPath);
    return node;
}

common.bmx add to extern block:

    Function bmx_mxmlFindPath:Byte Ptr(top:Byte Ptr, path:String)

Tested it and it works - results looked odd at first until I've read that the first child-value is returned if there are children - so GetContent() returns only the first child node-text instead of the whole mess. This seems to be "intented"

GWRon commented 5 years ago

mxmlFindElement()

xml.bmx - TxmlNode add:

    Rem
    bbdoc: Find the named element.
    about:
    (From the mxml-docs:) The search is constrained by the name,
    attribute name, and value; any NULL names or values are treated as
    wildcards, so different kinds of searches can be implemented by
    looking for all elements of a given name or all elements with a
    specific attribute. The descend argument determines whether the
    search descends into child nodes; normally you will use
    MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to
    find additional direct descendents of the node. The top node
    argument constrains the search to a particular node's children.
    returns: node or null
    End Rem
    Method FindElement:TxmlNode(top:TxmlNode, element:string = "", attr:string = "", value:string = "", descend:Int = -1)
        'descend = -1 equals to MXML_DESCEND_FIRST
        If top
            return TxmlNode._create(bmx_mxmlFindElement(nodePtr, top.nodePtr, element, attr, value, descend))
        Else
            return TxmlNode._create(bmx_mxmlFindElement(nodePtr, 0, element, attr, value, descend))
        End If
    End Method

glue.c add:

mxml_node_t * bmx_mxmlFindElement(mxml_node_t * node, mxml_node_t * top, BBString * element, BBString * attr, BBString * value, int descend) {
    char * charElement = bbStringToUTF8String(element);
    char * charAttr = bbStringToUTF8String(attr);
    char * charValue = bbStringToUTF8String(value);
    if (charElement && !charElement[0]) { charElement = NULL; }
    if (charAttr && !charAttr[0]) { charAttr = NULL; }
    if (charValue && !charValue[0]) { charValue = NULL; }
    mxml_node_t * foundNode = mxmlFindElement(node, top, charElement, charAttr, charValue, descend);
    bbMemFree(charElement);
    bbMemFree(charAttr);
    bbMemFree(charValue);
    return foundNode;
}

common.bmx add to extern block:

    Function bmx_mxmlFindElement:Byte Ptr(node:Byte Ptr, top:Byte Ptr, element:string, attr:string, value:string, descend:Int)

It compiles - but I miss something (either in my initial call or the code) as it just does not find a node. I also tried to set the passed params to "NULL" in case of empty strings being interpreted differently. Also passing "NULL" directly to the function (as if my code was not doing what it should) did not find the "element".

GWRon commented 5 years ago

@woollybah Do you see any flaws in the mxmlFindElement() as it does not what it should (= what I planned it should do ;-)).

woollybah commented 5 years ago

I've got an implementation of it working, which I'll commit now. I dropped top and descend args :-)

GWRon commented 5 years ago

You might add findPath too.

Or... Wait until Pull Request later this evening (will do as you did...with bbnullstring and so on).

GWRon commented 5 years ago

Hmm checked your (and mine) "FindElement" - and ... it seems that the mxml function returns the first "inner" node.

I have a function to search for "child elements by name" - as soon as the node.getName() corresponds to a given string, this node is returned. Outputting ToString() or GetContent() of this node includes the whole node code itself (eg. <test>string<children>string</children></test> and the content being "stringstring"). Using "FindElement" or "FindPath" results in an empty ToContent() and a "string" for ToString().

BUT ... if I replace FindElement(...).ToString() with FindElement(...).GetParent().ToString() (or GetContent) I receive the same output than with my custom function.

So it seems as if those both functions do not return the desired node - but a subsequent one (maybe some "inner node" or so).

GWRon commented 5 years ago

Regarding my last comment: mxmlFindPath() documentation states this "odd" behaviour:

The first child node of the found node is returned if the given node has children and the first child is a value node.

https://www.msweet.org/mxml/mxml.html#mxmlFindPath

Interestingly this is not annotated for mxmlFindElement.