/* 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; }
/* 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; }