mirror of
https://github.com/samba-team/samba.git
synced 2025-12-14 20:23:54 +03:00
1328 lines
33 KiB
C
1328 lines
33 KiB
C
/*
|
|
* @file ejsXml.c
|
|
* @brief E4X XML support
|
|
*/
|
|
/********************************* Copyright **********************************/
|
|
/*
|
|
* @copy default
|
|
*
|
|
* Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
|
|
* Copyright (c) Michael O'Brien, 1994-1995. All Rights Reserved.
|
|
*
|
|
* This software is distributed under commercial and open source licenses.
|
|
* You may use the GPL open source license described below or you may acquire
|
|
* a commercial license from Mbedthis Software. You agree to be fully bound
|
|
* by the terms of either license. Consult the LICENSE.TXT distributed with
|
|
* this software for full details.
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See the GNU General Public License for more
|
|
* details at: http://www.mbedthis.com/downloads/gplLicense.html
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* This GPL license does NOT permit incorporating this software into
|
|
* proprietary programs. If you are unable to comply with the GPL, you must
|
|
* acquire a commercial license to use this software. Commercial licenses
|
|
* for this software and support services are available from Mbedthis
|
|
* Software at http://www.mbedthis.com
|
|
*
|
|
* @end
|
|
*/
|
|
/************************************ Doc *************************************/
|
|
/*
|
|
* Javascript class definition
|
|
*
|
|
* class XML {
|
|
* public XML();
|
|
* public XML(string xmlString); // "<tag... "
|
|
* public XML(string file); // "file"
|
|
*
|
|
* public void load(string file);
|
|
* public void save(string file);
|
|
* public Array children();
|
|
* public Array attributes();
|
|
* }
|
|
*
|
|
[[Internal Properties / Methods]]
|
|
- prototype - Ptr to class prototype (base class)
|
|
- class - Type of class
|
|
Object.prototype.toString
|
|
- Value -
|
|
- Get(name) - Returns the value
|
|
- Put(name, value) - Sets the value
|
|
- HasProperty(name) - Bool if property exists
|
|
- Delete(name) - Delete property
|
|
- DefaultValue(hint) - Return default primitive (not obj) value
|
|
toString, if result is obj, then call valueOf
|
|
if hint is number, then call valueOf, then toString
|
|
- Construct(arg list) - Constructor
|
|
- Call(arg list) - Function call
|
|
- HasInstance(value) - ??
|
|
- Scope - Frame scope chain
|
|
- Match(string, index) - Regexp match
|
|
|
|
- Example:
|
|
XML attribute @name
|
|
@*
|
|
*
|
|
var node = new XML("<order/>");
|
|
Operators:
|
|
var prices = order..price;
|
|
var urgentItems = order.item(@level == "rush");
|
|
var itemAttrs = order.item[0].@*; # @ for attributes
|
|
XML Literals
|
|
order.customer.address =
|
|
<address>.....
|
|
<zip>{zipCode}</zip> Where {var} is a JS var
|
|
<tag attribute={prefix}> ... Also for attributes
|
|
</address>
|
|
Omit namespaces
|
|
Example:
|
|
var html = <html/>;
|
|
html.head.title = "My title";
|
|
head.body@bgcolor = "#e4e4e4";
|
|
*/
|
|
|
|
/********************************** Includes **********************************/
|
|
|
|
#include "ejs.h"
|
|
#include "exml.h"
|
|
|
|
/************************************ Data ************************************/
|
|
#if BLD_FEATURE_EJS_E4X
|
|
|
|
/*
|
|
* Per tag state
|
|
*/
|
|
typedef struct XmlTagState {
|
|
EjsVar *obj;
|
|
EjsVar *attributes;
|
|
EjsVar *comments;
|
|
} XmlTagState;
|
|
|
|
/*
|
|
* Parser state
|
|
*/
|
|
typedef struct XmlState {
|
|
Ejs *ep;
|
|
EjsVar *xmlClass;
|
|
EjsVar *xmlListClass;
|
|
XmlTagState nodeStack[E4X_MAX_NODE_DEPTH];
|
|
int topOfStack;
|
|
long inputSize;
|
|
long inputPos;
|
|
const char *inputBuf;
|
|
const char *fileName;
|
|
} XmlState;
|
|
|
|
/****************************** Forward Declarations **************************/
|
|
/*
|
|
* XML methods
|
|
*/
|
|
static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int name(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int save(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int toString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int valueOf(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
|
|
/* MOB -- temp */
|
|
static int getList(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int setText(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
|
|
#if FUTURE
|
|
static int length(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int toXmlString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
|
|
static int appendChild(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int attributes(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int child(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int children(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int comments(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int decendants(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int elements(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int insertChildAfter(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int insertChildBefore(Ejs *ep, EjsVar *thisObj, int argc,
|
|
EjsVar **argv);
|
|
static int replace(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int setName(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv);
|
|
#endif
|
|
|
|
/*
|
|
* Internal methods
|
|
*/
|
|
static EjsVar *createXmlProperty(Ejs *ep, EjsVar *obj, const char *property);
|
|
static int deleteXmlProperty(Ejs *ep, EjsVar *obj, const char *property);
|
|
static EjsVar *getXmlProperty(Ejs *ep, EjsVar *obj, const char *property);
|
|
static EjsVar *setXmlProperty(Ejs *ep, EjsVar *obj, const char *property,
|
|
const EjsVar *value);
|
|
static int loadXmlString(Ejs *ep, EjsVar *thisObj, const char *xmlString);
|
|
|
|
/*
|
|
* XMLList methods
|
|
*/
|
|
static EjsVar *createXmlListProperty(Ejs *ep, EjsVar *obj,
|
|
const char *property);
|
|
static int deleteXmlListProperty(Ejs *ep, EjsVar *obj,
|
|
const char *property);
|
|
static EjsVar *getXmlListProperty(Ejs *ep, EjsVar *obj, const char *property);
|
|
static EjsVar *setXmlListProperty(Ejs *ep, EjsVar *obj, const char *property,
|
|
const EjsVar *value);
|
|
|
|
/*
|
|
* Misc
|
|
*/
|
|
static int readFileData(Exml *xp, void *data, char *buf, int size);
|
|
static int readStringData(Exml *xp, void *data, char *buf, int size);
|
|
static int parserHandler(Exml *xp, int state, const char *tagName,
|
|
const char *attName, const char *value);
|
|
static void termParser(Exml *xp);
|
|
static Exml *initParser(Ejs *ep, EjsVar *thisObj, const char *fileName);
|
|
static int getNumElements(EjsVar *obj);
|
|
static int getText(MprBuf *buf, EjsVar *obj);
|
|
static int xmlToString(Ejs *ep, MprBuf *buf, EjsVar *obj, int indentLevel);
|
|
static void indent(MprBuf *bp, int level);
|
|
static char *cleanTagName(char *name);
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Define the E4X classes (XML, XMLList)
|
|
*/
|
|
|
|
int ejsDefineXmlClasses(Ejs *ep)
|
|
{
|
|
EjsMethods *methods;
|
|
EjsVar *xmlClass, *xmlListClass;
|
|
|
|
/*
|
|
* Create the XML class
|
|
*/
|
|
xmlClass = ejsDefineClass(ep, "XML", "Object", ejsXmlConstructor);
|
|
if (xmlClass == 0) {
|
|
return MPR_ERR_CANT_INITIALIZE;
|
|
}
|
|
|
|
/*
|
|
* Define the XML class methods
|
|
*/
|
|
ejsDefineCMethod(ep, xmlClass, "text", text, EJS_NO_LOCAL);
|
|
ejsDefineCMethod(ep, xmlClass, "name", name, EJS_NO_LOCAL);
|
|
ejsDefineCMethod(ep, xmlClass, "load", load, EJS_NO_LOCAL);
|
|
ejsDefineCMethod(ep, xmlClass, "save", save, EJS_NO_LOCAL);
|
|
ejsDefineCMethod(ep, xmlClass, "toString", toString, EJS_NO_LOCAL);
|
|
ejsDefineCMethod(ep, xmlClass, "valueOf", valueOf, EJS_NO_LOCAL);
|
|
|
|
/* MOB -- temporary only */
|
|
ejsDefineCMethod(ep, xmlClass, "getList", getList, EJS_NO_LOCAL);
|
|
ejsDefineCMethod(ep, xmlClass, "setText", setText, EJS_NO_LOCAL);
|
|
|
|
/*
|
|
* Setup the XML internal methods.
|
|
*/
|
|
methods = mprAllocTypeZeroed(ep, EjsMethods);
|
|
xmlClass->objectState->methods = methods;
|
|
|
|
methods->createProperty = createXmlProperty;
|
|
methods->deleteProperty = deleteXmlProperty;
|
|
methods->getProperty = getXmlProperty;
|
|
methods->setProperty = setXmlProperty;
|
|
|
|
/*
|
|
* Create the XMLList class
|
|
*/
|
|
xmlListClass = ejsDefineClass(ep, "XMLList", "Array",
|
|
ejsXmlListConstructor);
|
|
|
|
/*
|
|
* Define the XMLList class methods
|
|
*/
|
|
|
|
/*
|
|
* Setup the XML internal methods.
|
|
*/
|
|
methods = mprAllocTypeZeroed(ep, EjsMethods);
|
|
xmlListClass->objectState->methods = methods;
|
|
|
|
methods->createProperty = createXmlListProperty;
|
|
methods->deleteProperty = deleteXmlListProperty;
|
|
methods->getProperty = getXmlListProperty;
|
|
methods->setProperty = setXmlListProperty;
|
|
|
|
/* MOB -- need to complete xmlListClass */
|
|
|
|
return (ejsObjHasErrors(xmlClass) || ejsObjHasErrors(xmlListClass))
|
|
? MPR_ERR_CANT_INITIALIZE : 0;
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Routine to create an XML object using a default constructor
|
|
*/
|
|
|
|
EjsVar *ejsCreateXml(Ejs *ep)
|
|
{
|
|
EjsVar *op;
|
|
|
|
op = ejsCreateSimpleObj(ep, "XML");
|
|
if (op == 0) {
|
|
mprAssert(op);
|
|
return op;
|
|
}
|
|
ejsSetVarName(ep, op, "xmlNode");
|
|
|
|
/*
|
|
* Invoke class constructors manually (for speed and space)
|
|
*/
|
|
if (ejsXmlConstructor(ep, op, 0, 0) < 0) {
|
|
mprFree(op);
|
|
mprAssert(0);
|
|
return 0;
|
|
}
|
|
return op;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* XML constructor
|
|
*/
|
|
|
|
int ejsXmlConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
EjsVar *vp;
|
|
const char *str;
|
|
|
|
ejsSetVarFlags(thisObj, EJS_XML_FLAGS_ELEMENT);
|
|
|
|
if (argc == 1) {
|
|
vp = argv[0];
|
|
|
|
if (ejsVarIsObject(vp)) {
|
|
/* Convert DOM to XML. Not implemented */;
|
|
|
|
} else if (ejsVarIsString(vp)) {
|
|
str = vp->string;
|
|
if (str == 0) {
|
|
return 0;
|
|
}
|
|
if (*str == '<') {
|
|
/* XML Literal */
|
|
return loadXmlString(ep, thisObj, str);
|
|
|
|
} else {
|
|
/* Load from file */
|
|
return load(ep, thisObj, argc, argv);
|
|
}
|
|
} else {
|
|
ejsError(ep, EJS_TYPE_ERROR, "Bad type passed to XML constructor");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Routine to create an XMLList object
|
|
*/
|
|
|
|
EjsVar *ejsCreateXmlList(Ejs *ep)
|
|
{
|
|
EjsVar *op;
|
|
|
|
/* Sanity limit for size of hash table */
|
|
|
|
op = ejsCreateSimpleObj(ep, "XMLList");
|
|
if (op == 0) {
|
|
mprAssert(0);
|
|
return op;
|
|
}
|
|
if (ejsArrayConstructor(ep, op, 0, 0) < 0 ||
|
|
ejsXmlConstructor(ep, op, 0, 0) < 0) {
|
|
mprFree(op);
|
|
mprAssert(0);
|
|
return 0;
|
|
}
|
|
return op;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* XMLList constructor
|
|
*/
|
|
|
|
int ejsXmlListConstructor(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
// ejsSetVarFlags(vp, EJS_XML_FLAGS_ELEMENT);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/******************************** Internal Methods ****************************/
|
|
/******************************************************************************/
|
|
|
|
static EjsVar *createXmlProperty(Ejs *ep, EjsVar *obj, const char *property)
|
|
{
|
|
return ejsGetVarPtr(ejsCreateSimpleProperty(ep, obj, property));
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int deleteXmlProperty(Ejs *ep, EjsVar *obj, const char *property)
|
|
{
|
|
return ejsDeleteProperty(ep, obj, property);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* MOB -- need ep as an arg */
|
|
static EjsVar *getXmlProperty(Ejs *ep, EjsVar *obj, const char *property)
|
|
{
|
|
#if NEW
|
|
EjsVar *lp;
|
|
|
|
lp = ejsCreateXmlList(ep);
|
|
if (isdigit(*property)) {
|
|
/* MOB -- where do we store these. Do we need them ? */
|
|
lp->targetObject = obj
|
|
lp->targetProperty = property
|
|
return getXmlListProperty(lp, property);
|
|
}
|
|
|
|
/* What about a simple elment. Should it not return the text */
|
|
|
|
if (*property == '@') {
|
|
ap = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
|
|
while (ap) {
|
|
vp = ejsGetVarPtr(ap);
|
|
/* MOB -- are attributes unique ? */
|
|
if (vp->flags & EJS_XML_FLAGS_ATTRIBUTE &&
|
|
strcmp(property, ap->name) == 0) {
|
|
ejsAppendXml(lp, vp);
|
|
}
|
|
ap = ejsGetNexttProperty(ap, EJS_ENUM_ALL);
|
|
}
|
|
} else {
|
|
while (ap) {
|
|
vp = ejsGetVarPtr(ap);
|
|
/* MOB -- are attributes unique ? */
|
|
if (vp->flags & EJS_XML_FLAGS_ELEMENT &&
|
|
strcmp(property, ap->name) == 0) {
|
|
ejsAppendXml(lp, vp);
|
|
}
|
|
ap = ejsGetNexttProperty(ap, EJS_ENUM_ALL);
|
|
}
|
|
}
|
|
return l;
|
|
|
|
// Must always return XML or XMLList event for comments and attributes
|
|
#endif
|
|
return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property));
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static EjsVar *setXmlProperty(Ejs *ep, EjsVar *obj, const char *property,
|
|
const EjsVar *value)
|
|
{
|
|
EjsProperty *pp;
|
|
EjsVar *vp;
|
|
|
|
pp = ejsCreateSimpleProperty(ep, obj, property);
|
|
if (pp == 0) {
|
|
/* Should never happen */
|
|
mprAssert(pp);
|
|
return 0;
|
|
}
|
|
vp = ejsGetVarPtr(pp);
|
|
if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0) {
|
|
return 0;
|
|
}
|
|
return ejsGetVarPtr(pp);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
NEW
|
|
|
|
static EjsVar *setXmlProperty(Ejs *ep, EjsVar *op, const char *property,
|
|
EjsVar *value)
|
|
{
|
|
|
|
if ((value->objectState->baseClass != XML &&
|
|
value->objectState->baseClass != XMLList) ||
|
|
value->string[0] != '<') {
|
|
ejsVarToString(luevalue.toString();
|
|
ejsRunMethod(ep, value, "toString", 0);
|
|
value = ejsDupVar(ep->result);
|
|
|
|
} else {
|
|
value = ejsDupVar(value);
|
|
}
|
|
|
|
if (isdigit(*property)) {
|
|
// ERROR -- reserved for future versions
|
|
return 0;
|
|
}
|
|
|
|
if (*property == '@') {
|
|
if (op->objectState->baseClass == XMLList) {
|
|
if (op->obj.LENGTH_PROPERTY == 0) {
|
|
c = "";
|
|
} else {
|
|
// Catenate all result of toString on all elts in list
|
|
}
|
|
} else {
|
|
c = c.toString();
|
|
}
|
|
// Replace existing attribute of same name or insert
|
|
return;
|
|
}
|
|
for (i = op->obj.LENGTH - 1; i >= 0; i--) {
|
|
// Delete item of same name
|
|
}
|
|
if (not Found) {
|
|
Append new Xml object
|
|
- set [[name]], [[class]] == "element"
|
|
}
|
|
|
|
mprFree(value);
|
|
}
|
|
|
|
*/
|
|
/******************************************************************************/
|
|
/************************************ Methods *********************************/
|
|
/******************************************************************************/
|
|
|
|
static int load(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
const char *fileName;
|
|
XmlState *parser;
|
|
Exml *xp;
|
|
MprFile *file;
|
|
|
|
if (argc != 1 || !ejsVarIsString(argv[0])) {
|
|
ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: load(fileName);");
|
|
return -1;
|
|
}
|
|
fileName = argv[0]->string;
|
|
|
|
/* MOB -- not romable
|
|
Need rom code in MPR not MprServices
|
|
*/
|
|
file = mprOpen(ep, fileName, O_RDONLY, 0664);
|
|
if (file == 0) {
|
|
ejsError(ep, EJS_IO_ERROR, "Can't open: %s", fileName);
|
|
return -1;
|
|
}
|
|
|
|
/* MOB -- should we empty thisObj of all existing properties ? */
|
|
|
|
xp = initParser(ep, thisObj, fileName);
|
|
parser = exmlGetParseArg(xp);
|
|
|
|
exmlSetInputStream(xp, readFileData, (void*) file);
|
|
|
|
if (exmlParse(xp) < 0) {
|
|
if (! ejsGotException(ep)) {
|
|
ejsError(ep, EJS_IO_ERROR, "Can't parse XML file: %s\nDetails %s",
|
|
fileName, exmlGetErrorMsg(xp));
|
|
}
|
|
termParser(xp);
|
|
mprClose(file);
|
|
return -1;
|
|
}
|
|
|
|
ejsSetReturnValue(ep, parser->nodeStack[0].obj);
|
|
|
|
termParser(xp);
|
|
mprClose(file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int loadXmlString(Ejs *ep, EjsVar *thisObj, const char *xmlString)
|
|
{
|
|
XmlState *parser;
|
|
Exml *xp;
|
|
|
|
xp = initParser(ep, thisObj, "string");
|
|
parser = exmlGetParseArg(xp);
|
|
|
|
parser->inputBuf = xmlString;
|
|
parser->inputSize = strlen(xmlString);
|
|
|
|
exmlSetInputStream(xp, readStringData, (void*) 0);
|
|
|
|
if (exmlParse(xp) < 0) {
|
|
if (! ejsGotException(ep)) {
|
|
ejsError(ep, EJS_IO_ERROR, "Can't parse XML string\nError %s",
|
|
exmlGetErrorMsg(xp));
|
|
}
|
|
termParser(xp);
|
|
return -1;
|
|
}
|
|
|
|
ejsSetReturnValue(ep, parser->nodeStack[0].obj);
|
|
|
|
termParser(xp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int text(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
EjsVar *vp;
|
|
|
|
vp = ejsGetVarPtr(ejsGetSimpleProperty(ep, thisObj, E4X_TEXT_PROPERTY));
|
|
if (vp == 0) {
|
|
ejsSetReturnValueToString(ep, "");
|
|
return 0;
|
|
}
|
|
ejsSetReturnValue(ep, vp);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Return the tag name
|
|
*/
|
|
|
|
static int name(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
EjsVar *vp;
|
|
|
|
vp = ejsGetVarPtr(ejsGetSimpleProperty(ep, thisObj, E4X_TAG_NAME_PROPERTY));
|
|
if (vp == 0) {
|
|
ejsSetReturnValueToString(ep, "");
|
|
return 0;
|
|
}
|
|
ejsSetReturnValue(ep, vp);
|
|
#if UNDEFINED
|
|
char *name;
|
|
/* MOB -- not ideal as we can't guarantee thisObj is a property */
|
|
name = ejsGetPropertyPtr(thisObj)->name;
|
|
if (name == 0) {
|
|
name = "";
|
|
}
|
|
ejsSetReturnValueToString(ep, name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* MOB -- temporary only */
|
|
|
|
static int setText(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
if (argc != 1) {
|
|
ejsArgError(ep, "usage: setText(string)");
|
|
}
|
|
|
|
ejsSetProperty(ep, thisObj, E4X_TEXT_PROPERTY, argv[0]);
|
|
ejsSetReturnValue(ep, argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static Exml *initParser(Ejs *ep, EjsVar *thisObj, const char *fileName)
|
|
{
|
|
XmlState *parser;
|
|
Exml *xp;
|
|
|
|
xp = exmlOpen(ep, 512, E4X_BUF_MAX);
|
|
mprAssert(xp);
|
|
|
|
/*
|
|
* Create the parser stack
|
|
*/
|
|
parser = mprAllocTypeZeroed(ep, XmlState);
|
|
parser->ep = ep;
|
|
parser->nodeStack[0].obj = thisObj;
|
|
parser->xmlClass = ejsGetClass(ep, 0, "XML");
|
|
parser->xmlListClass = ejsGetClass(ep, 0, "XMLList");
|
|
parser->fileName = fileName;
|
|
|
|
exmlSetParseArg(xp, parser);
|
|
exmlSetParserHandler(xp, parserHandler);
|
|
|
|
return xp;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static void termParser(Exml *xp)
|
|
{
|
|
mprFree(exmlGetParseArg(xp));
|
|
exmlClose(xp);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* XML parsing callback. Called for each elt and attribute/value pair.
|
|
* For speed, we handcraft the object model here rather than calling
|
|
* putXmlProperty.
|
|
*
|
|
* "<!-- txt -->" parseHandler(efd, , EXML_COMMENT);
|
|
* "<elt" parseHandler(efd, , EXML_NEW_ELT);
|
|
* "...att=value" parseHandler(efd, , EXML_NEW_ATT);
|
|
* "<elt ...>" parseHandler(efd, , EXML_ELT_DEFINED);
|
|
* "<elt/>" parseHandler(efd, , EXML_SOLO_ELT_DEFINED);
|
|
* "<elt> ...<" parseHandler(efd, , EXML_ELT_DATA);
|
|
* "...</elt>" parseHandler(efd, , EXML_END_ELT);
|
|
*
|
|
* Note: we recurse on every new nested elt.
|
|
*/
|
|
|
|
static int parserHandler(Exml *xp, int state, const char *tagName,
|
|
const char *attName, const char *value)
|
|
{
|
|
XmlState *parser;
|
|
XmlTagState *tos;
|
|
EjsVar *currentNode, *vp, *tagNode, *parent, *vpx;
|
|
EjsProperty *pp;
|
|
Ejs *ep;
|
|
char *name;
|
|
|
|
parser = (XmlState*) xp->parseArg;
|
|
ep = parser->ep;
|
|
tos = &parser->nodeStack[parser->topOfStack];
|
|
currentNode = tos->obj;
|
|
|
|
mprAssert(state >= 0);
|
|
mprAssert(tagName && *tagName);
|
|
|
|
switch (state) {
|
|
case EXML_PI:
|
|
/*
|
|
* By using a property name with a leading space, we can store
|
|
* non-user-visible processing instructions as regular properties.
|
|
*/
|
|
pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, E4X_PI_PROPERTY);
|
|
ejsMakePropertyEnumerable(pp, 1);
|
|
vp = ejsGetVarPtr(pp);
|
|
ejsWriteVarAsString(ep, vp, value);
|
|
ejsSetVarFlags(vp, EJS_XML_FLAGS_PI);
|
|
break;
|
|
|
|
case EXML_COMMENT:
|
|
/*
|
|
* By using a property name with a leading space, we can store
|
|
* non- user-visible comments as regular properties.
|
|
*/
|
|
pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode,
|
|
E4X_COMMENT_PROPERTY);
|
|
ejsMakePropertyEnumerable(pp, 1);
|
|
vp = ejsGetVarPtr(pp);
|
|
ejsWriteVarAsString(ep, vp, value);
|
|
ejsSetVarFlags(vp, EJS_XML_FLAGS_COMMENT);
|
|
break;
|
|
|
|
case EXML_NEW_ELT:
|
|
if (parser->topOfStack > E4X_MAX_NODE_DEPTH) {
|
|
ejsError(ep, EJS_IO_ERROR,
|
|
"XML nodes nested too deeply in %s at line %d",
|
|
parser->fileName, exmlGetLineNumber(xp));
|
|
return MPR_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
name = mprStrdup(xp, tagName);
|
|
if (name == 0) {
|
|
return MPR_ERR_MEMORY;
|
|
}
|
|
|
|
if (cleanTagName(name) < 0) {
|
|
ejsError(ep, EJS_TYPE_ERROR, "Bad XML tag name in %s at %d",
|
|
parser->fileName, exmlGetLineNumber(xp));
|
|
mprFree(name);
|
|
return MPR_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode, name);
|
|
ejsMakePropertyEnumerable(pp, 1);
|
|
|
|
tagNode = ejsGetVarPtr(pp);
|
|
|
|
/* MOB -- OPT */
|
|
vpx = ejsCreateXml(ep);
|
|
vp = ejsWriteVar(ep, tagNode, vpx, EJS_SHALLOW_COPY);
|
|
ejsMakeObjLive(vp, 1);
|
|
ejsFreeVar(ep, vpx);
|
|
|
|
/* MOB -- return code */
|
|
pp = ejsSetPropertyToString(ep, vp, E4X_TAG_NAME_PROPERTY, name);
|
|
ejsMakePropertyEnumerable(pp, 0);
|
|
|
|
ejsSetVarFlags(vp, EJS_XML_FLAGS_ELEMENT);
|
|
ejsMakePropertyEnumerable(ejsGetPropertyPtr(vp), 1);
|
|
|
|
tos = &parser->nodeStack[++(parser->topOfStack)];
|
|
currentNode = tos->obj = vp;
|
|
tos->attributes = 0;
|
|
tos->comments = 0;
|
|
mprFree(name);
|
|
break;
|
|
|
|
case EXML_NEW_ATT:
|
|
if (mprAllocSprintf(MPR_LOC_ARGS(xp), &name, 0, "@%s", attName) < 0) {
|
|
return MPR_ERR_MEMORY;
|
|
}
|
|
pp = ejsCreateProperty(ep, currentNode, name);
|
|
ejsMakePropertyEnumerable(pp, 1);
|
|
|
|
vp = ejsGetVarPtr(pp);
|
|
ejsWriteVarAsString(ep, vp, value);
|
|
ejsSetVarFlags(vp, EJS_XML_FLAGS_ATTRIBUTE);
|
|
mprFree(name);
|
|
break;
|
|
|
|
case EXML_SOLO_ELT_DEFINED:
|
|
parser->topOfStack--;
|
|
mprAssert(parser->topOfStack >= 0);
|
|
tos = &parser->nodeStack[parser->topOfStack];
|
|
break;
|
|
|
|
case EXML_ELT_DEFINED:
|
|
if (parser->topOfStack > 0) {
|
|
parent = parser->nodeStack[parser->topOfStack - 1].obj;
|
|
ejsSetProperty(ep, currentNode, E4X_PARENT_PROPERTY, parent);
|
|
}
|
|
break;
|
|
|
|
case EXML_ELT_DATA:
|
|
case EXML_CDATA:
|
|
pp = ejsCreateSimpleNonUniqueProperty(ep, currentNode,
|
|
E4X_TEXT_PROPERTY);
|
|
ejsMakePropertyEnumerable(pp, 1);
|
|
vp = ejsGetVarPtr(pp);
|
|
ejsWriteVarAsString(ep, vp, value);
|
|
ejsSetVarFlags(vp, EJS_XML_FLAGS_TEXT);
|
|
break;
|
|
|
|
case EXML_END_ELT:
|
|
/*
|
|
* This is the closing element in a pair "<x>...</x>".
|
|
* Pop the stack frame off the elt stack
|
|
*/
|
|
parser->topOfStack--;
|
|
mprAssert(parser->topOfStack >= 0);
|
|
tos = &parser->nodeStack[parser->topOfStack];
|
|
break;
|
|
|
|
default:
|
|
ejsError(ep, EJS_IO_ERROR, "XML error in %s at %d\nDetails %s",
|
|
parser->fileName, exmlGetLineNumber(xp), exmlGetErrorMsg(xp));
|
|
mprAssert(0);
|
|
return MPR_ERR_BAD_SYNTAX;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static char *cleanTagName(char *name)
|
|
{
|
|
char *cp;
|
|
|
|
for (cp = name; *cp; cp++) {
|
|
if (*cp == ':') {
|
|
*cp = '_';
|
|
} else if (!isalnum(*cp) && *cp != '_' && *cp != '$' && *cp != '@') {
|
|
return 0;
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int readFileData(Exml *xp, void *data, char *buf, int size)
|
|
{
|
|
mprAssert(xp);
|
|
mprAssert(data);
|
|
mprAssert(buf);
|
|
mprAssert(size > 0);
|
|
|
|
return mprRead((MprFile*) data, buf, size);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int readStringData(Exml *xp, void *data, char *buf, int size)
|
|
{
|
|
XmlState *parser;
|
|
int rc, len;
|
|
|
|
mprAssert(xp);
|
|
mprAssert(buf);
|
|
mprAssert(size > 0);
|
|
|
|
parser = (XmlState*) xp->parseArg;
|
|
|
|
if (parser->inputPos < parser->inputSize) {
|
|
len = min(size, (parser->inputSize - parser->inputPos));
|
|
rc = mprMemcpy(buf, size, &parser->inputBuf[parser->inputPos], len);
|
|
parser->inputPos += len;
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int save(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
const char *fileName;
|
|
MprBuf *buf;
|
|
MprFile *file;
|
|
int bytes, len;
|
|
|
|
if (argc != 1 || !ejsVarIsString(argv[0])) {
|
|
ejsError(ep, EJS_ARG_ERROR, "Bad args. Usage: save(fileName);");
|
|
return -1;
|
|
}
|
|
fileName = argv[0]->string;
|
|
|
|
/* MOB -- not romable
|
|
Need rom code in MPR not MprServices
|
|
*/
|
|
|
|
/*
|
|
* Convert to a string
|
|
*/
|
|
buf = mprCreateBuf(ep, E4X_BUF_SIZE, E4X_BUF_MAX);
|
|
if (xmlToString(ep, buf, thisObj, -1) < 0) {
|
|
mprFree(buf);
|
|
return -1;
|
|
}
|
|
|
|
file = mprOpen(ep, fileName,
|
|
O_CREAT | O_TRUNC | O_WRONLY | O_TEXT, 0664);
|
|
if (file == 0) {
|
|
ejsError(ep, EJS_IO_ERROR, "Can't open: %s, %d", fileName,
|
|
mprGetOsError());
|
|
return -1;
|
|
}
|
|
|
|
len = mprGetBufLength(buf);
|
|
bytes = mprWrite(file, buf->start, len);
|
|
if (bytes != len) {
|
|
ejsError(ep, EJS_IO_ERROR, "Can't write to: %s", fileName);
|
|
mprClose(file);
|
|
return -1;
|
|
}
|
|
mprWrite(file, "\n", 1);
|
|
mprFree(buf);
|
|
|
|
mprClose(file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int toString(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
MprBuf *buf;
|
|
|
|
buf = mprCreateBuf(ep, E4X_BUF_SIZE, E4X_BUF_MAX);
|
|
|
|
if (xmlToString(ep, buf, thisObj, -1) < 0) {
|
|
mprFree(buf);
|
|
return -1;
|
|
}
|
|
ejsWriteVarAsString(ep, ep->result, (char*) buf->start);
|
|
|
|
mprFree(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* MOB -- need to support XMLList */
|
|
|
|
static int xmlToString(Ejs *ep, MprBuf *buf, EjsVar *obj, int indentLevel)
|
|
{
|
|
EjsProperty *pp;
|
|
EjsVar *vp;
|
|
char *varBuf;
|
|
int endTag, sawElements;
|
|
|
|
if (indentLevel < 0) {
|
|
mprPutStringToBuf(buf, "<?xml version=\"1.0\"?>");
|
|
}
|
|
|
|
switch (obj->type) {
|
|
case EJS_TYPE_STRING:
|
|
if (obj->flags & EJS_XML_FLAGS_ATTRIBUTE) {
|
|
mprPutFmtStringToBuf(buf, " %s=\"%s\"",
|
|
&ejsGetPropertyPtr(obj)->name[1], obj->string);
|
|
/* No new line */
|
|
|
|
} else if (obj->flags & EJS_XML_FLAGS_COMMENT) {
|
|
mprPutCharToBuf(buf, '\n');
|
|
indent(buf, indentLevel);
|
|
mprPutFmtStringToBuf(buf, "<!-- %s -->", obj->string);
|
|
|
|
} else if (obj->flags & EJS_XML_FLAGS_TEXT) {
|
|
mprPutStringToBuf(buf, obj->string);
|
|
|
|
} else {
|
|
// indent(buf, indentLevel);
|
|
mprPutStringToBuf(buf, obj->string);
|
|
// mprPutCharToBuf(buf, '\n');
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Primitive types come here */
|
|
indent(buf, indentLevel);
|
|
/* MOB -- rc */
|
|
varBuf = ejsVarToString(ep, obj);
|
|
mprPutStringToBuf(buf, varBuf);
|
|
break;
|
|
|
|
case EJS_TYPE_OBJECT:
|
|
if (obj->objectState->baseClass == ejsGetClass(ep, 0, "XML")) {
|
|
if (!obj->objectState->visited) {
|
|
obj->objectState->visited = 1;
|
|
|
|
/* MOB -- opt. Flags would be quicker */
|
|
if (strcmp(ejsGetPropertyPtr(obj)->name,
|
|
E4X_PARENT_PROPERTY) == 0) {
|
|
return 0;
|
|
}
|
|
/*
|
|
* MOB -- short term fix for tags with no body but with
|
|
* attributes
|
|
*/
|
|
if (getNumElements(obj) == 0 && 0) {
|
|
/*
|
|
* XML element is simple with no elements, so return just
|
|
* the text.
|
|
*/
|
|
if (getText(buf, obj) < 0) {
|
|
ejsError(ep, EJS_IO_ERROR,
|
|
"XML is to big to convert to a string");
|
|
obj->objectState->visited = 0;
|
|
return -1;
|
|
}
|
|
|
|
} else if (obj->flags & (EJS_XML_FLAGS_ELEMENT)) {
|
|
/*
|
|
* XML object is complex (has elements) so return full XML
|
|
* content.
|
|
*/
|
|
mprPutCharToBuf(buf, '\n');
|
|
indent(buf, indentLevel);
|
|
|
|
/*
|
|
* When called from toString, obj is not a property
|
|
*/
|
|
if (indentLevel >= 0) {
|
|
mprPutFmtStringToBuf(buf, "<%s",
|
|
ejsGetPropertyPtr(obj)->name);
|
|
endTag = 0;
|
|
|
|
} else {
|
|
endTag = 1;
|
|
}
|
|
|
|
sawElements = 0;
|
|
pp = ejsGetFirstProperty(obj, 0);
|
|
while (pp) {
|
|
vp = ejsGetVarPtr(pp);
|
|
|
|
if (! (vp->flags & EJS_XML_FLAGS_ATTRIBUTE)) {
|
|
if (endTag == 0) {
|
|
endTag++;
|
|
mprPutStringToBuf(buf, ">");
|
|
}
|
|
}
|
|
if (vp->flags & EJS_XML_FLAGS_ELEMENT) {
|
|
if (strcmp(ejsGetPropertyPtr(vp)->name,
|
|
E4X_PARENT_PROPERTY) != 0) {
|
|
sawElements++;
|
|
}
|
|
}
|
|
|
|
if (xmlToString(ep, buf, ejsGetVarPtr(pp),
|
|
indentLevel + 1) < 0){
|
|
return -1;
|
|
}
|
|
|
|
pp = ejsGetNextProperty(pp, 0);
|
|
}
|
|
if (indentLevel >= 0) {
|
|
if (sawElements) {
|
|
mprPutCharToBuf(buf, '\n');
|
|
indent(buf, indentLevel);
|
|
}
|
|
mprPutFmtStringToBuf(buf, "</%s>",
|
|
ejsGetPropertyPtr(obj)->name);
|
|
}
|
|
}
|
|
obj->objectState->visited = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (obj->objectState->baseClass == ejsGetClass(ep, 0, "XMLList")) {
|
|
indent(buf, indentLevel);
|
|
/* MOB -- TBD */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* All other objects. Allow other objects to override toString
|
|
*/
|
|
if (ejsRunMethod(ep, obj->objectState->baseClass, "toString",
|
|
0) < 0) {
|
|
return -1;
|
|
}
|
|
if (ejsVarIsString(ep->result)) {
|
|
indent(buf, indentLevel);
|
|
mprPutStringToBuf(buf, obj->string);
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static void indent(MprBuf *bp, int level)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < level; i++) {
|
|
mprPutCharToBuf(bp, '\t');
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int valueOf(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
if (argc != 0) {
|
|
mprAssert(0);
|
|
return -1;
|
|
}
|
|
|
|
switch (thisObj->type) {
|
|
default:
|
|
case EJS_TYPE_UNDEFINED:
|
|
case EJS_TYPE_NULL:
|
|
case EJS_TYPE_CMETHOD:
|
|
case EJS_TYPE_OBJECT:
|
|
case EJS_TYPE_METHOD:
|
|
case EJS_TYPE_STRING_CMETHOD:
|
|
ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY);
|
|
break;
|
|
|
|
case EJS_TYPE_STRING:
|
|
ejsWriteVarAsInteger(ep, ep->result, atoi(thisObj->string));
|
|
break;
|
|
|
|
case EJS_TYPE_BOOL:
|
|
case EJS_TYPE_INT:
|
|
#if BLD_FEATURE_INT64
|
|
case EJS_TYPE_INT64:
|
|
#endif
|
|
#if BLD_FEATURE_FLOATING_POINT
|
|
case EJS_TYPE_FLOAT:
|
|
#endif
|
|
ejsWriteVar(ep, ep->result, thisObj, EJS_SHALLOW_COPY);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int getList(Ejs *ep, EjsVar *thisObj, int argc, EjsVar **argv)
|
|
{
|
|
const char *nodeName;
|
|
EjsProperty *pp;
|
|
EjsVar *list, *vp;
|
|
|
|
if (argc != 1) {
|
|
nodeName = 0;
|
|
} else {
|
|
nodeName = argv[0]->string;
|
|
}
|
|
|
|
list = ejsCreateArray(ep, 0);
|
|
|
|
pp = ejsGetFirstProperty(thisObj, EJS_ENUM_ALL);
|
|
while (pp) {
|
|
vp = ejsGetVarPtr(pp);
|
|
if (vp->type == EJS_TYPE_OBJECT) {
|
|
if (strcmp(ejsGetPropertyPtr(vp)->name, E4X_PARENT_PROPERTY) != 0) {
|
|
if (vp->flags & EJS_XML_FLAGS_ELEMENT &&
|
|
(nodeName == 0 || strcmp(nodeName, pp->name) == 0)) {
|
|
ejsAddArrayElt(ep, list, vp, EJS_SHALLOW_COPY);
|
|
}
|
|
}
|
|
}
|
|
pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
|
|
}
|
|
|
|
ejsSetReturnValueAndFree(ep, list);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int getNumElements(EjsVar *obj)
|
|
{
|
|
EjsProperty *pp;
|
|
int count;
|
|
|
|
count = 0;
|
|
pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
|
|
while (pp) {
|
|
if (ejsGetVarPtr(pp)->flags & EJS_XML_FLAGS_ELEMENT) {
|
|
count++;
|
|
}
|
|
pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* MOB - This needs to be a public method */
|
|
|
|
static int getText(MprBuf *buf, EjsVar *obj)
|
|
{
|
|
EjsProperty *pp;
|
|
EjsVar *vp;
|
|
|
|
pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
|
|
while (pp) {
|
|
vp = ejsGetVarPtr(pp);
|
|
if (vp->flags & EJS_XML_FLAGS_TEXT) {
|
|
/* MOB -- should test for overflow */
|
|
mprPutStringToBuf(buf, vp->string);
|
|
}
|
|
pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/******************************************************************************/
|
|
/******************************** Internal Methods ****************************/
|
|
/******************************************************************************/
|
|
|
|
static EjsVar *createXmlListProperty(Ejs *ep, EjsVar *obj, const char *property)
|
|
{
|
|
return ejsGetVarPtr(ejsCreateProperty(ep, obj, property));
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int deleteXmlListProperty(Ejs *ep, EjsVar *obj, const char *property)
|
|
{
|
|
return ejsDeleteProperty(ep, obj, property);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static EjsVar *getXmlListProperty(Ejs *ep, EjsVar *obj, const char *property)
|
|
{
|
|
// Must always return XML or XMLList event for comments and attributes
|
|
return ejsGetVarPtr(ejsGetSimpleProperty(ep, obj, property));
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static EjsVar *setXmlListProperty(Ejs *ep, EjsVar *obj, const char *property,
|
|
const EjsVar *value)
|
|
{
|
|
EjsProperty *pp;
|
|
EjsVar *vp;
|
|
|
|
pp = ejsGetSimpleProperty(ep, obj, property);
|
|
if (pp == 0) {
|
|
mprAssert(pp);
|
|
return 0;
|
|
}
|
|
vp = ejsGetVarPtr(pp);
|
|
if (ejsWriteVar(ep, vp, value, EJS_SHALLOW_COPY) < 0){
|
|
mprAssert(0);
|
|
return 0;
|
|
}
|
|
return ejsGetVarPtr(pp);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
NEW
|
|
|
|
static EjsVar *putXmlListProperty(EjsVar *op, const char *property,
|
|
EjsVar *value)
|
|
{
|
|
|
|
if ((value->objectState->baseClass != XML &&
|
|
value->objectState->baseClass != XMLList) ||
|
|
value->string[0] != '<') {
|
|
c = value.toString();
|
|
} else {
|
|
value = ejsDupVar(value);
|
|
??
|
|
}
|
|
if (isdigit(*property)) {
|
|
// ERROR
|
|
return 0;
|
|
}
|
|
if (*property == '@') {
|
|
if (op->objectState->baseClass == XMLList) {
|
|
if (op->obj.LENGTH_PROPERTY == 0) {
|
|
c = "";
|
|
} else {
|
|
// Catenate all result of toString on all elts in list
|
|
}
|
|
} else {
|
|
c = c.toString();
|
|
}
|
|
// Replace existing attribute of same name or insert
|
|
return;
|
|
}
|
|
for (i = op->obj.LENGTH - 1; i >= 0; i--) {
|
|
// Delete item of same name
|
|
}
|
|
if (not Found) {
|
|
Append new Xml object
|
|
- set [[name]], [[class]] == "element"
|
|
}
|
|
}
|
|
|
|
*/
|
|
|
|
/******************************************************************************/
|
|
#else
|
|
void ejs4xDummy() {}
|
|
|
|
/******************************************************************************/
|
|
#endif /* BLD_FEATURE_EJS_E4X */
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim:tw=78
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
* vim<600: sw=4 ts=4
|
|
*/
|