Пример #1
0
/*
    Create a value node. If the value is an XML node already, we are done. Otherwise, cast the value to a string
    and create a text child node containing the string value.
 */
static EjsXML *createValueNode(Ejs *ejs, EjsXML *elt, EjsObj *value)
{
    EjsXML      *text;
    EjsString   *str;

    if (ejsIsXML(ejs, value)) {
        return (EjsXML*) value;
    }
    if ((str = (EjsString*) ejsCast(ejs, value, String)) == NULL) {
        return 0;
    }
    if (mprGetListLength(elt->elements) == 1) {
        /*
            Update an existing text element
         */
        text = mprGetFirstItem(elt->elements);
        if (text->kind == EJS_XML_TEXT) {
            text->value = str;
            return elt;
        }
    }

    /*
        Create a new text element
     */
    if (str->value[0] != '\0') {
        text = ejsCreateXML(ejs, EJS_XML_TEXT, N(NULL, NULL), elt, str);
        elt = ejsAppendToXML(ejs, elt, text);
    }
    return elt;
}
Пример #2
0
/*
    native function XML(value: Object = null)
 */
static EjsObj *xmlConstructor(Ejs *ejs, EjsXML *thisObj, int argc, EjsObj **argv)
{
    EjsObj      *arg, *vp;
    wchar       *str;

    //  TODO - should be also able to handle a File object

    if (thisObj == 0) {
        /*
            Called as a function - cast the arg
         */
        if (argc > 0) {
            if ((arg = ejsCast(ejs, argv[0], String)) == 0) {
                return 0;
            }
        }
        thisObj = ejsCreateXML(ejs, 0, N(NULL, NULL), NULL, NULL);
    }
    if (argc == 0) {
        return (EjsObj*) thisObj;
    }

    arg = argv[0];
    assert(arg);

    if (!ejsIsDefined(ejs, arg)) {
        return (EjsObj*) thisObj;
    }
    arg = ejsCast(ejs, argv[0], String);
    if (arg && ejsIs(ejs, arg, String)) {
        str = ((EjsString*) arg)->value;
        if (str == 0) {
            return 0;
        }
        while (isspace((uchar) *str)) str++;
        if (*str == '<') {
            /* XML Literal */
            ejsLoadXMLString(ejs, thisObj, (EjsString*) arg);

        } else {
            /* Load from file */
            loadXml(ejs, thisObj, argc, argv);
        }
    } else if (arg && ejsIsXML(ejs, arg)) {
        if ((vp = xmlToString(ejs, argv[0], 0, NULL)) != 0) {
            ejsLoadXMLString(ejs, thisObj, (EjsString*) vp);
        }

    } else {
        ejsThrowArgError(ejs, "Bad type passed to XML constructor");
        return 0;
    }
    return (EjsObj*) thisObj;
}
Пример #3
0
/*
    Update an existing element
 */
static int updateElement(Ejs *ejs, EjsXML *list, EjsXML *elt, int index, EjsObj *value)
{
    EjsXML      *node;
    int         i, j;

    if (!ejsIsXML(ejs, value)) {
        /* Not XML or XMLList -- convert to string */
        value = ejsCast(ejs, value, String);                //  TODO - seem to be doing this in too many places
    }
    mprSetItem(list->elements, index, value);

    if (elt->kind == EJS_XML_ATTRIBUTE) {
        assure(ejsIs(ejs, value, String));
        i = mprLookupItem(elt->parent->elements, elt);
        assure(i >= 0);
        ejsSetXMLElement(ejs, elt->parent, i, elt);
        //  TODO - why do this. Doesn't above do this?
        ejsSetPropertyByName(ejs, elt->parent, elt->qname, value);
        elt->value = (EjsString*) value;
    }

    if (ejsIsXML(ejs, value) && ((EjsXML*) value)->kind == EJS_XML_LIST) {
        value = (EjsObj*) shallowCopy(ejs, (EjsXML*) value);
        if (elt->parent) {
            index = mprLookupItem(elt->parent->elements, elt);
            assure(index >= 0);
            for (j = 0; j < mprGetListLength(((EjsXML*) value)->elements); j++) {
                mprInsertItemAtPos(elt->parent->elements, index, value);
            }
        }

    } else if (ejsIsXML(ejs, value) || elt->kind != EJS_XML_ELEMENT) {
        if (elt->parent) {
            index = mprLookupItem(elt->parent->elements, elt);
            assure(index >= 0);
            mprSetItem(elt->parent->elements, index, value);
            ((EjsXML*) value)->parent = elt->parent;
            if (ejsIs(ejs, value, String)) {
                node = ejsCreateXML(ejs, EJS_XML_TEXT, N(NULL, NULL), list, (EjsString*) value);
                mprSetItem(list->elements, index, node);
            } else {
                mprSetItem(list->elements, index, value);
            }
        }

    } else {
        ejsSetPropertyByName(ejs, elt, N(NULL, "*"), value);
    }
    return index;
}
Пример #4
0
PUBLIC EjsAny *cloneXml(Ejs *ejs, EjsXML *xml, bool deep)
{
    EjsXML      *root, *elt;
    int         next;

    if (xml == 0) {
        return 0;
    }
    if (xml->kind == EJS_XML_LIST) {
        root = ejsCreateXMLList(ejs, xml->targetObject, xml->targetProperty);
    } else {
        root = ejsCreateXML(ejs, xml->kind, xml->qname, NULL, xml->value);
    }
    if (root == 0) {
        return 0;
    }
    //  TODO - must copy inScopeNamespaces?

    if (xml->attributes) {
        root->attributes = mprCreateList(-1, 0);
        for (next = 0; (elt = (EjsXML*) mprGetNextItem(xml->attributes, &next)) != 0; ) {
            elt = ejsClone(ejs, elt, 1);
            if (elt) {
                elt->parent = root;
                mprAddItem(root->attributes, elt);
            }
        }
    }
    if (xml->elements) {
        root->elements = mprCreateList(-1, 0);
        for (next = 0; (elt = mprGetNextItem(xml->elements, &next)) != 0; ) {
            assert(ejsIsXML(ejs, elt));
            elt = ejsClone(ejs, elt, 1);
            if (elt) {
                elt->parent = root;
                mprAddItem(root->elements, elt);
            }
        }
    }
    if (mprHasMemError(ejs)) {
        return 0;
    }
    return root;
}
Пример #5
0
static int parserHandler(MprXml *xp, int state, cchar *tagName, cchar *attName, cchar *value)
{
    Ejs             *ejs;
    EjsXmlState     *parser;
    EjsXmlTagState  *tos;
    EjsName         qname;
    EjsXML          *xml, *node, *parent;

    parser = (EjsXmlState*) xp->parseArg;
    ejs = parser->ejs;
    tos = &parser->nodeStack[parser->topOfStack];
    xml = tos->obj;
    
    mprAssert(xml);

    mprAssert(state >= 0);
    mprAssert(tagName && *tagName);

    switch (state) {
    case MPR_XML_PI:
        node = ejsCreateXML(ejs, EJS_XML_PROCESSING, NULL, xml, value);
        ejsAppendToXML(ejs, xml, node);
        break;

    case MPR_XML_COMMENT:
        node = ejsCreateXML(ejs, EJS_XML_COMMENT, NULL, xml, value);
        ejsAppendToXML(ejs, xml, node);
        break;

    case MPR_XML_NEW_ELT:
        if (parser->topOfStack > E4X_MAX_NODE_DEPTH) {
            ejsThrowSyntaxError(ejs,  "XML nodes nested too deeply in %s at line %d", parser->filename, 
                mprXmlGetLineNumber(xp));
            return MPR_ERR_BAD_SYNTAX;
        }
        if (xml->kind <= 0) {
            ejsConfigureXML(ejs, xml, EJS_XML_ELEMENT, tagName, xml, NULL);
        } else {
            ejsName(&qname, 0, tagName);
            xml = ejsCreateXML(ejs, EJS_XML_ELEMENT, &qname, xml, NULL);
            tos = &parser->nodeStack[++(parser->topOfStack)];
            tos->obj = (EjsXML*) xml;
            tos->attributes = 0;
            tos->comments = 0;
        }
        break;

    case MPR_XML_NEW_ATT:
        ejsName(&qname, 0, attName);
        node = ejsCreateXML(ejs, EJS_XML_ATTRIBUTE, &qname, xml, value);
        ejsAppendAttributeToXML(ejs, xml, node);
        break;

    case MPR_XML_SOLO_ELT_DEFINED:
        if (parser->topOfStack > 0) {
            parent = parser->nodeStack[parser->topOfStack - 1].obj;
            ejsAppendToXML(ejs, parent, xml);
            parser->topOfStack--;
            mprAssert(parser->topOfStack >= 0);
            tos = &parser->nodeStack[parser->topOfStack];
        }
        break;

    case MPR_XML_ELT_DEFINED:
        if (parser->topOfStack > 0) {
            parent = parser->nodeStack[parser->topOfStack - 1].obj;
            ejsAppendToXML(ejs, parent, xml);
        }
        break;

    case MPR_XML_ELT_DATA:
    case MPR_XML_CDATA:
        ejsName(&qname, 0, attName);
        node = ejsCreateXML(ejs, EJS_XML_TEXT, &qname, xml, value);
        ejsAppendToXML(ejs, xml, node);
        break;

    case MPR_XML_END_ELT:
        /*
         *  This is the closing element in a pair "<x>...</x>".
         *  Pop the stack frame off the elt stack
         */
        if (parser->topOfStack > 0) {
            parser->topOfStack--;
            mprAssert(parser->topOfStack >= 0);
            tos = &parser->nodeStack[parser->topOfStack];
        }
        break;

    default:
        ejsThrowSyntaxError(ejs, "XML error in %s at %d\nDetails %s", parser->filename, mprXmlGetLineNumber(xp), 
            mprXmlGetErrorMsg(xp));
        mprAssert(0);
        return MPR_ERR_BAD_SYNTAX;
    }
    return 0;
}
Пример #6
0
/*
    Set a property by name
    There are 7 kinds of qname's: prop, @att, [prop], *, @*, .name, .@name
 */
static int setXmlPropertyByName(Ejs *ejs, EjsXML *xml, EjsName qname, EjsObj *value)
{
    EjsXML      *elt, *xvalue, *rp, *lastElt;
    EjsObj      *originalValue;
    int         index, last;

    last = 0;
    lastElt = 0;

    if (isdigit((uchar) qname.name->value[0]) && allDigitsForXml(qname.name)) {
        ejsThrowTypeError(ejs, "Integer indicies for set are not allowed");
        return EJS_ERR;
    }

    if (xml->kind != EJS_XML_ELEMENT) {
        //  TODO spec requires this -- but why? -- surely throw?
        return 0;
    }

    /*
        Massage the value type.
     */
    originalValue = value;

    xvalue = (EjsXML*) value;
    if (ejsIsXML(ejs, xvalue)) {
        if (xvalue->kind == EJS_XML_LIST) {
            value = cloneXml(ejs, xvalue, 1);

        } else if (xvalue->kind == EJS_XML_TEXT || xvalue->kind == EJS_XML_ATTRIBUTE) {
            value = ejsCast(ejs, originalValue, String);

        } else {
            value = cloneXml(ejs, xvalue, 1);
        }
    } else {
        value = ejsCast(ejs, value, String);
    }
    if (qname.name->value[0] == '@') {
        return setXmlPropertyAttributeByName(ejs, xml, qname, value);
    }
    /*
        Delete redundant elements by the same name.
     */
    if (xml->elements) {
        for (last = -1, index = -1; (elt = mprGetPrevItem(xml->elements, &index)) != 0; ) {
            if (qname.name->value[0] == '*' || (elt->kind == EJS_XML_ELEMENT && elt->qname.name == qname.name)) {
                /*
                    Must remove all redundant elements of the same name except the first one
                 */
                if (last >= 0) {
                    rp = mprGetItem(xml->elements, last);
                    rp->parent = 0;
                    mprRemoveItemAtPos(xml->elements, last);
                }
                last = index;
                lastElt = elt;
            }
        }
    }
    if (xml->elements == 0) {
        //  TODO - need routine to do this centrally so we can control the default number of elements in the list?
        xml->elements = mprCreateList(-1, 0);
    }
    elt = lastElt;
    index = last;

    if (qname.name->value[0] == '*') {
        /*
            Special case when called from XMLList to update the value of an element
         */
        xml = createValueNode(ejs, xml, value);

    } else if (elt == 0) {
        /*
            Not found. New node required.
         */
        elt = ejsCreateXML(ejs, EJS_XML_ELEMENT, qname, xml, NULL);
        if (elt == 0) {
            return 0;
        }
        index = mprGetListLength(xml->elements);
        xml = ejsAppendToXML(ejs, xml, createValueNode(ejs, elt, value));

    } else {
        /*
            Update existing element.
         */
        xml = ejsSetXMLElement(ejs, xml, index, createValueNode(ejs, elt, value));
    }

    if (xml == 0) {
        return EJS_ERR;
    }
    return index;
}
Пример #7
0
/*
    Set a property attribute by name.
 */
static int setXmlPropertyAttributeByName(Ejs *ejs, EjsXML *xml, EjsName qname, EjsObj *value)
{
    EjsXML      *elt, *attribute, *xvalue, *lastElt;
    EjsString   *sv;
    EjsName     qn;
    wchar       *str;
    int         index, last, next;

    /*
        Attribute. If the value is an XML list, convert to a space separated string
     */
    xvalue = (EjsXML*) value;
    if (ejsIsXML(ejs, xvalue) && xvalue->kind == EJS_XML_LIST) {
        str = 0;
        for (next = 0; (elt = mprGetNextItem(xvalue->elements, &next)) != 0; ) {
            sv = (EjsString*) ejsCast(ejs, (EjsObj*) elt, String);
            str = mrejoin(str, NULL, " ", sv->value, NULL);
        }
        value = (EjsObj*) ejsCreateString(ejs, str, -1);

    } else {
        value = ejsCast(ejs, value, String);
    }
    assert(ejsIs(ejs, value, String));

    /*
        Find the first attribute that matches. Delete all other attributes of the same name.
     */
    index = 0;
    if (xml->attributes) {
        lastElt = 0;
        for (last = -1, index = -1; (elt = mprGetPrevItem(xml->attributes, &index)) != 0; ) {
            assert(qname.name->value[0] == '@');
            if (wcmp(elt->qname.name->value, &qname.name->value[1]) == 0) {
                if (last >= 0) {
                    mprRemoveItemAtPos(xml->attributes, last);
                }
                last = index;
                lastElt = elt;
            }
        }
        if (lastElt) {
            /*
                Found a match. So replace its value
             */
            lastElt->value = (EjsString*) value;
            return last;

        } else {
            index = mprGetListLength(xml->attributes);
        }
    }
    //  TODO - namespace work to do here

    /*
        Not found. Create a new attribute node
     */
    assert(ejsIs(ejs, value, String));
    qn.space = NULL;
    qn.name = ejsSubstring(ejs, qname.name, 1, -1);
    attribute = ejsCreateXML(ejs, EJS_XML_ATTRIBUTE, qn, xml, (EjsString*) value);
    if (xml->attributes == 0) {
        xml->attributes = mprCreateList(-1, 0);
    }
    mprSetItem(xml->attributes, index, attribute);
    return index;
}
Пример #8
0
static EjsXML *createXml(Ejs *ejs, EjsType *type, int size)
{
    return ejsCreateXML(ejs, 0, N(NULL, NULL), NULL, NULL);
}
Пример #9
0
static EjsXML *createElement(Ejs *ejs, EjsXML *list, EjsXML *targetObject, EjsName qname, EjsObj *value)
{
    EjsXML      *elt, *last, *attList;
    int         index;
    int         j;

    if (targetObject && ejsIsXML(ejs, targetObject) && targetObject->kind == EJS_XML_LIST) {

        /*
            If the target is a list it must have 1 element. So switch to it.
            TODO - could we get resolve to do this?
         */
        if (mprGetListLength(targetObject->elements) != 1) {
            /* Spec says so - TODO why no error? */
            return 0;
        }
        targetObject = mprGetFirstItem(targetObject->elements);
    }

    /*
        Return if the target object is not an XML element
     */
    if (!ejsIsXML(ejs, targetObject) || targetObject->kind != EJS_XML_ELEMENT) {
            /* Spec says so - TODO why no error? */
        return 0;
    }

    elt = ejsCreateXML(ejs, EJS_XML_ELEMENT, list->targetProperty, targetObject, NULL);

    if (list->targetProperty.name && list->targetProperty.name->value[0] == '@') {
        elt->kind = EJS_XML_ATTRIBUTE;
        attList = ejsGetPropertyByName(ejs, (EjsObj*) targetObject, list->targetProperty);
        if (attList && mprGetListLength(attList->elements) > 0) {
            /* Spec says so. But this surely means you can't update an attribute? */
            return 0;
        }
    } else if (list->targetProperty.name == NULL || qname.name->value[0] == '*') {
        elt->kind = EJS_XML_TEXT;
        elt->qname.name = 0;
    }

    index = mprGetListLength(list->elements);

    if (elt->kind != EJS_XML_ATTRIBUTE) {
        if (targetObject) {
            if (index > 0) {
                /*
                    Find the place of the last list item in the resolved target object.
                 */
                last = mprGetItem(list->elements, index - 1);
                j = mprLookupItem(targetObject->elements, last);
            } else {
                j = -1;
            } 
            if (j < 0) {
                j = mprGetListLength(targetObject->elements) - 1;
            }
            //  TODO - really need to wrap this ejsInsertXML(EjsXML *xml, int index, EjsXML *node)
            if (targetObject->elements == 0) {
                targetObject->elements = mprCreateList(-1, 0);
            }
            /*
                Insert into the target object
             */
            mprInsertItemAtPos(targetObject->elements, j + 1, elt);
        }

        if (ejsIsXML(ejs, value)) {
            if (((EjsXML*) value)->kind == EJS_XML_LIST) {
                elt->qname = ((EjsXML*) value)->targetProperty;
            } else {
                elt->qname = ((EjsXML*) value)->qname;
            }
        }

        /*
            Insert into the XML list
         */
        mprSetItem(list->elements, index, elt);
    }
    return (EjsXML*) mprGetItem(list->elements, index);
}