/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript.xml.impl.xmlbeans;
import java.io.Serializable;
import java.util.*;
import org.mozilla.javascript.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlCursor.XmlBookmark;
import org.apache.xmlbeans.XmlCursor.TokenType;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
class XML extends XMLObjectImpl
{
static final long serialVersionUID = -630969919086449092L;
final static class XScriptAnnotation extends XmlBookmark implements Serializable
{
private static final long serialVersionUID = 1L;
javax.xml.namespace.QName _name;
XML _xScriptXML;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Constructurs
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
XScriptAnnotation (XmlCursor curs)
{
_name = curs.getName();
}
}
/**
*
*/
final static class NamespaceDeclarations
{
private int _prefixIdx;
private StringBuffer _namespaceDecls;
private String _defaultNSURI;
NamespaceDeclarations (XmlCursor curs)
{
_prefixIdx = 0;
_namespaceDecls = new StringBuffer();
skipNonElements(curs);
_defaultNSURI = curs.namespaceForPrefix("");
if (isAnyDefaultNamespace())
{
addDecl("", _defaultNSURI);
}
}
private void addDecl (String prefix, String ns)
{
_namespaceDecls.append((prefix.length() > 0 ?
"declare namespace " + prefix :
"default element namespace") +
" = \"" + ns + "\"" + "\n");
}
String getNextPrefix (String ns)
{
String prefix = "NS" + _prefixIdx++;
_namespaceDecls.append("declare namespace " + prefix + " = " + "\"" + ns + "\"" + "\n");
return prefix;
}
boolean isAnyDefaultNamespace ()
{
return _defaultNSURI != null ?_defaultNSURI.length() > 0 : false;
}
String getDeclarations()
{
return _namespaceDecls.toString();
}
}
// Fields
//static final XML prototype = new XML();
private XScriptAnnotation _anno;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Constructors
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* @param anno
*/
private XML(XMLLibImpl lib, XScriptAnnotation anno)
{
super(lib, lib.xmlPrototype);
_anno = anno;
_anno._xScriptXML = this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Public factories for creating a XScript XML object given an XBean cursor.
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static XML createEmptyXML(XMLLibImpl lib)
{
XScriptAnnotation anno;
XmlObject xo = XmlObject.Factory.newInstance();
XmlCursor curs = xo.newCursor();
try {
anno = new XScriptAnnotation(curs);
curs.setBookmark(anno);
} finally {
curs.dispose();
}
return new XML(lib, anno);
}
private static XML createXML (XMLLibImpl lib, XmlCursor curs)
{
if (curs.currentTokenType().isStartdoc())
{
curs.toFirstContentToken();
}
XScriptAnnotation anno = findAnnotation(curs);
return new XML(lib, anno);
}
/**
* Special constructor for making an attribute
*
*/
private static XML createAttributeXML(XMLLibImpl lib, XmlCursor cursor)
{
if (!cursor.isAttr())
throw new IllegalArgumentException();
XScriptAnnotation anno = new XScriptAnnotation(cursor);
cursor.setBookmark(anno);
return new XML(lib, anno);
}
/**
*
* @param qname
* @param value
* @return
*/
static XML createTextElement(XMLLibImpl lib, javax.xml.namespace.QName qname, String value)
{
XScriptAnnotation anno;
XmlObject xo = XmlObject.Factory.newInstance();
XmlCursor cursor = xo.newCursor();
try {
cursor.toNextToken();
cursor.beginElement(qname.getLocalPart(), qname.getNamespaceURI());
//if(namespace.length() > 0)
// cursor.insertNamespace("", namespace);
cursor.insertChars(value);
cursor.toStartDoc();
cursor.toNextToken();
anno = new XScriptAnnotation(cursor);
cursor.setBookmark(anno);
} finally {
cursor.dispose();
}
return new XML(lib, anno);
}
static XML createFromXmlObject(XMLLibImpl lib, XmlObject xo)
{
XScriptAnnotation anno;
XmlCursor curs = xo.newCursor();
if (curs.currentTokenType().isStartdoc())
{
curs.toFirstContentToken();
}
try {
anno = new XScriptAnnotation(curs);
curs.setBookmark(anno);
} finally {
curs.dispose();
}
return new XML(lib, anno);
}
static XML createFromJS(XMLLibImpl lib, Object inputObject)
{
XmlObject xo;
boolean isText = false;
String frag;
if (inputObject == null || inputObject == Undefined.instance) {
frag = "";
} else if (inputObject instanceof XMLObjectImpl) {
// todo: faster way for XMLObjects?
frag = ((XMLObjectImpl) inputObject).toXMLString(0);
} else {
if (inputObject instanceof Wrapper) {
Object wrapped = ((Wrapper)inputObject).unwrap();
if (wrapped instanceof XmlObject) {
return createFromXmlObject(lib, (XmlObject)wrapped);
}
}
frag = ScriptRuntime.toString(inputObject);
}
if (frag.trim().startsWith("<>"))
{
throw ScriptRuntime.typeError("Invalid use of XML object anonymous tags <></>.");
}
if (frag.indexOf("<") == -1)
{
// Must be solo text node, wrap in XML fragment
isText = true;
frag = "<textFragment>" + frag + "</textFragment>";
}
XmlOptions options = new XmlOptions();
if (lib.ignoreComments)
{
options.put(XmlOptions.LOAD_STRIP_COMMENTS);
}
if (lib.ignoreProcessingInstructions)
{
options.put(XmlOptions.LOAD_STRIP_PROCINSTS);
}
if (lib.ignoreWhitespace)
{
options.put(XmlOptions.LOAD_STRIP_WHITESPACE);
}
try
{
xo = XmlObject.Factory.parse(frag, options);
// Apply the default namespace
Context cx = Context.getCurrentContext();
String defaultURI = lib.getDefaultNamespaceURI(cx);
if(defaultURI.length() > 0)
{
XmlCursor cursor = xo.newCursor();
boolean isRoot = true;
while(!cursor.toNextToken().isEnddoc())
{
if(!cursor.isStart()) continue;
// Check if this element explicitly sets the
// default namespace
boolean defaultNSDeclared = false;
cursor.push();
while(cursor.toNextToken().isAnyAttr())
{
if(cursor.isNamespace())
{
if(cursor.getName().getLocalPart().length() == 0)
{
defaultNSDeclared = true;
break;
}
}
}
cursor.pop();
if(defaultNSDeclared)
{
cursor.toEndToken();
continue;
}
// Check if this element's name is in no namespace
javax.xml.namespace.QName qname = cursor.getName();
if(qname.getNamespaceURI().length() == 0)
{
// Change the namespace
qname = new javax.xml.namespace.QName(defaultURI,
qname.getLocalPart());
cursor.setName(qname);
}
if(isRoot)
{
// Declare the default namespace
cursor.push();
cursor.toNextToken();
cursor.insertNamespace("", defaultURI);
cursor.pop();
isRoot = false;
}
}
cursor.dispose();
}
}
catch (XmlException xe)
{
/*
todo need to handle namespace prefix not found in XML look for namespace type in the scope change.
String errorMsg = "Use of undefined namespace prefix: ";
String msg = xe.getError().getMessage();
if (msg.startsWith(errorMsg))
{
String prefix = msg.substring(errorMsg.length());
}
*/
String errMsg = xe.getMessage();
if (errMsg.equals("error: Unexpected end of file after null"))
{
// Create an empty document.
xo = XmlObject.Factory.newInstance();
}
else
{
throw ScriptRuntime.typeError(xe.getMessage());
}
}
catch (Throwable e)
{
// todo: TLL Catch specific exceptions during parse.
throw ScriptRuntime.typeError("Not Parsable as XML");
}
XmlCursor curs = xo.newCursor();
if (curs.currentTokenType().isStartdoc())
{
curs.toFirstContentToken();
}
if (isText)
{
// Move it to point to the text node
curs.toFirstContentToken();
}
XScriptAnnotation anno;
try
{
anno = new XScriptAnnotation(curs);
curs.setBookmark(anno);
}
finally
{
curs.dispose();
}
return new XML(lib, anno);
}
static XML getFromAnnotation(XMLLibImpl lib, XScriptAnnotation anno)
{
if (anno._xScriptXML == null)
{
anno._xScriptXML = new XML(lib, anno);
}
return anno._xScriptXML;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Private functions:
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* @param curs
* @return
*/
private static TokenType skipNonElements (XmlCursor curs)
{
TokenType tt = curs.currentTokenType();
while (tt.isComment() || tt.isProcinst())
{
tt = curs.toNextToken();
}
return tt;
}
/**
*
* @param curs
* @return
*/
protected static XScriptAnnotation findAnnotation(XmlCursor curs)
{
XmlBookmark anno = curs.getBookmark(XScriptAnnotation.class);
if (anno == null)
{
anno = new XScriptAnnotation(curs);
curs.setBookmark(anno);
}
return (XScriptAnnotation)anno;
}
/**
*
* @return
*/
private XmlOptions getOptions()
{
XmlOptions options = new XmlOptions();
if (lib.ignoreComments)
{
options.put(XmlOptions.LOAD_STRIP_COMMENTS);
}
if (lib.ignoreProcessingInstructions)
{
options.put(XmlOptions.LOAD_STRIP_PROCINSTS);
}
if (lib.ignoreWhitespace)
{
options.put(XmlOptions.LOAD_STRIP_WHITESPACE);
}
if (lib.prettyPrinting)
{
options.put(XmlOptions.SAVE_PRETTY_PRINT, null);
options.put(XmlOptions.SAVE_PRETTY_PRINT_INDENT, new Integer(lib.prettyIndent));
}
return options;
}
/**
*
* @param cursor
* @param opts
* @return
*/
private static String dumpNode(XmlCursor cursor, XmlOptions opts)
{
if (cursor.isText())
return cursor.getChars();
if (cursor.isFinish())
return "";
cursor.push();
boolean wanRawText = cursor.isStartdoc() && !cursor.toFirstChild();
cursor.pop();
return wanRawText ? cursor.getTextValue() : cursor.xmlText( opts );
}
/**
*
* @return
*/
private XmlCursor newCursor ()
{
XmlCursor curs;
if (_anno != null)
{
curs = _anno.createCursor();
if (curs == null)
{
// Orphaned case.
XmlObject doc = XmlObject.Factory.newInstance();
curs = doc.newCursor();
if (_anno._name != null)
{
curs.toNextToken();
curs.insertElement(_anno._name);
curs.toPrevSibling();
}
curs.setBookmark(_anno);
}
}
else
{
XmlObject doc = XmlObject.Factory.newInstance();
curs = doc.newCursor();
}
return curs;
}
/*
* fUseStartDoc used by child(int index) the index is at startDoc is the element at the top-level
* otherwise we always want to drill in.
*/
private boolean moveToChild(XmlCursor curs, long index, boolean fFirstChild, boolean fUseStartDoc)
{
if (index < 0)
throw new IllegalArgumentException();
long idxChild = 0;
if (!fUseStartDoc && curs.currentTokenType().isStartdoc())
{
// We always move to the children of the top node.
// todo: This assumes that we want have multiple top-level nodes. Which we should be able tohave.
curs.toFirstContentToken();
}
TokenType tt = curs.toFirstContentToken();
if (!tt.isNone() && !tt.isEnd())
{
while (true)
{
if (index == idxChild)
{
return true;
}
tt = curs.currentTokenType();
if (tt.isText())
{
curs.toNextToken();
}
else if (tt.isStart())
{
// Need to do this we want to be pointing at the text if that after the end token.
curs.toEndToken();
curs.toNextToken();
}
else if (tt.isComment() || tt.isProcinst())
{
continue;
}
else
{
break;
}
idxChild++;
}
}
else if (fFirstChild && index == 0)
{
// Drill into where first child would be.
// curs.toFirstContentToken();
return true;
}
return false;
}
/**
*
* @return
*/
XmlCursor.TokenType tokenType()
{
XmlCursor.TokenType result;
XmlCursor curs = newCursor();
if (curs.isStartdoc())
{
curs.toFirstContentToken();
}
result = curs.currentTokenType();
curs.dispose();
return result;
}
/**
*
* @param srcCurs
* @param destCurs
* @param fDontMoveIfSame
* @return
*/
private boolean moveSrcToDest (XmlCursor srcCurs, XmlCursor destCurs, boolean fDontMoveIfSame)
{
boolean fMovedSomething = true;
TokenType tt;
do
{
if (fDontMoveIfSame && srcCurs.isInSameDocument(destCurs) && (srcCurs.comparePosition(destCurs) == 0))
{
// If the source and destination are pointing at the same place then there's nothing to move.
fMovedSomething = false;
break;
}
// todo ***TLL*** Use replaceContents (when added) and eliminate children removes (see above todo).
if (destCurs.currentTokenType().isStartdoc())
{
destCurs.toNextToken();
}
// todo ***TLL*** Can Eric support notion of copy instead of me copying then moving???
XmlCursor copyCurs = copy(srcCurs);
copyCurs.moveXml(destCurs);
copyCurs.dispose();
tt = srcCurs.currentTokenType();
} while (!tt.isStart() && !tt.isEnd() && !tt.isEnddoc());
return fMovedSomething;
}
/**
*
* @param cursToCopy
* @return
*/
private XmlCursor copy (XmlCursor cursToCopy)
{
XmlObject xo = XmlObject.Factory.newInstance();
XmlCursor copyCurs = null;
if (cursToCopy.currentTokenType().isText())
{
try
{
// Try just as a textnode, to do that we need to wrap the text in a special fragment tag
// that is not visible from the XmlCursor.
copyCurs = XmlObject.Factory.parse("<x:fragment xmlns:x=\"http://www.openuri.org/fragment\">" +
cursToCopy.getChars() +
"</x:fragment>").newCursor();
if (!cursToCopy.toNextSibling())
{
if (cursToCopy.currentTokenType().isText())
{
cursToCopy.toNextToken(); // It's not an element it's text so skip it.
}
}
}
catch (Exception ex)
{
throw ScriptRuntime.typeError(ex.getMessage());
}
}
else
{
copyCurs = xo.newCursor();
copyCurs.toFirstContentToken();
if (cursToCopy.currentTokenType() == XmlCursor.TokenType.STARTDOC)
{
cursToCopy.toNextToken();
}
cursToCopy.copyXml(copyCurs);
if (!cursToCopy.toNextSibling()) // If element skip element.
{
if (cursToCopy.currentTokenType().isText())
{
cursToCopy.toNextToken(); // It's not an element it's text so skip it.
}
}
}
copyCurs.toStartDoc();
copyCurs.toFirstContentToken();
return copyCurs;
}
private static final int APPEND_CHILD = 1;
private static final int PREPEND_CHILD = 2;
/**
*
* @param curs
* @param xmlToInsert
*/
private void insertChild(XmlCursor curs, Object xmlToInsert)
{
if (xmlToInsert == null || xmlToInsert instanceof Undefined)
{
// Do nothing
}
else if (xmlToInsert instanceof XmlCursor)
{
moveSrcToDest((XmlCursor)xmlToInsert, curs, true);
}
else if (xmlToInsert instanceof XML)
{
XML xmlValue = (XML) xmlToInsert;
// If it's an attribute, then change to text node
if (xmlValue.tokenType() == XmlCursor.TokenType.ATTR)
{
insertChild(curs, xmlValue.toString());
}
else
{
XmlCursor cursToInsert = ((XML) xmlToInsert).newCursor();
moveSrcToDest(cursToInsert, curs, true);
cursToInsert.dispose();
}
}
else if (xmlToInsert instanceof XMLList)
{
XMLList list = (XMLList) xmlToInsert;
for (int i = 0; i < list.length(); i++)
{
insertChild(curs, list.item(i));
}
}
else
{
// Convert to string and make XML out of it
String xmlStr = ScriptRuntime.toString(xmlToInsert);
XmlObject xo = XmlObject.Factory.newInstance(); // Create an empty document.
XmlCursor sourceCurs = xo.newCursor();
sourceCurs.toNextToken();
// To hold the text.
sourceCurs.insertChars(xmlStr);
sourceCurs.toPrevToken();
// Call us again with the cursor.
moveSrcToDest(sourceCurs, curs, true);
}
}
/**
*
* @param childToMatch
* @param xmlToInsert
* @param addToType
*/
private void insertChild(XML childToMatch, Object xmlToInsert, int addToType)
{
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
XmlCursor xmlChildCursor = childToMatch.newCursor();
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isContainer())
{
tt = curs.toNextToken();
while (!tt.isEnd())
{
if (tt.isStart())
{
// See if this child is the same as the one thep passed in
if (curs.comparePosition(xmlChildCursor) == 0)
{
// Found it
if (addToType == APPEND_CHILD)
{
// Move the cursor to just past the end of this element
curs.toEndToken();
curs.toNextToken();
}
insertChild(curs, xmlToInsert);
break;
}
}
// Skip over child elements
if (tt.isStart())
{
tt = curs.toEndToken();
}
tt = curs.toNextToken();
}
}
xmlChildCursor.dispose();
curs.dispose();
}
/**
*
* @param curs
*/
protected void removeToken (XmlCursor curs)
{
XmlObject xo = XmlObject.Factory.newInstance();
// Don't delete anything move to another document so it gets orphaned nicely.
XmlCursor tmpCurs = xo.newCursor();
tmpCurs.toFirstContentToken();
curs.moveXml(tmpCurs);
tmpCurs.dispose();
}
/**
*
* @param index
*/
protected void removeChild(long index)
{
XmlCursor curs = newCursor();
if (moveToChild(curs, index, false, false))
{
removeToken(curs);
}
curs.dispose();
}
/**
*
* @param name
* @return
*/
protected static javax.xml.namespace.QName computeQName (Object name)
{
if (name instanceof String)
{
String ns = null;
String localName = null;
String fullName = (String)name;
localName = fullName;
if (fullName.startsWith("\""))
{
int idx = fullName.indexOf(":");
if (idx != -1)
{
ns = fullName.substring(1, idx - 1); // Don't include the "" around the namespace
localName = fullName.substring(idx + 1);
}
}
if (ns == null)
{
return new javax.xml.namespace.QName(localName);
}
else
{
return new javax.xml.namespace.QName(ns, localName);
}
}
return null;
}
/**
*
* @param destCurs
* @param newValue
*/
private void replace(XmlCursor destCurs, XML newValue)
{
if (destCurs.isStartdoc())
{
// Can't overwrite a whole document (user really wants to overwrite the contents of).
destCurs.toFirstContentToken();
}
// Orphan the token -- don't delete it outright on the XmlCursor.
removeToken(destCurs);
XmlCursor srcCurs = newValue.newCursor();
if (srcCurs.currentTokenType().isStartdoc())
{
// Cann't append a whole document (user really wants to append the contents of).
srcCurs.toFirstContentToken();
}
moveSrcToDest(srcCurs, destCurs, false);
// Re-link a new annotation to this cursor -- we just deleted the previous annotation on entrance to replace.
if (!destCurs.toPrevSibling())
{
destCurs.toPrevToken();
}
destCurs.setBookmark(new XScriptAnnotation(destCurs));
// todo would be nice if destCurs.toNextSibling went to where the next token if the cursor was pointing at the last token in the stream.
destCurs.toEndToken();
destCurs.toNextToken();
srcCurs.dispose();
}
/**
*
* @param currXMLNode
* @param xmlValue
* @return
*/
private boolean doPut(XMLName name, XML currXMLNode, XMLObjectImpl xmlValue)
{
boolean result = false;
XmlCursor curs = currXMLNode.newCursor();
try
{
// Replace the node with this new xml value.
XML xml;
int toAssignLen = xmlValue.length();
for (int i = 0; i < toAssignLen; i++)
{
if (xmlValue instanceof XMLList)
{
xml = ((XMLList) xmlValue).item(i);
}
else
{
xml = (XML) xmlValue;
}
// If it's an attribute or text node, make text node.
XmlCursor.TokenType tt = xml.tokenType();
if (tt == XmlCursor.TokenType.ATTR || tt == XmlCursor.TokenType.TEXT)
{
xml = makeXmlFromString(lib, name, xml.toString());
}
if (i == 0)
{
// 1st assignment is replaceChild all others are appendChild
replace(curs, xml);
}
else
{
insertChild(curs, xml);
}
}
// We're done we've blown away the node because the rvalue was XML...
result = true;
}
catch (Exception ex)
{
ex.printStackTrace();
throw ScriptRuntime.typeError(ex.getMessage());
}
finally
{
curs.dispose();
}
return result;
}
/**
* Make a text node element with this element name and text value.
*
* @param name
* @param value
* @return
*/
private XML makeXmlFromString(XMLLibImpl lib, XMLName name,
String value)
{
XML result;
javax.xml.namespace.QName qname;
try
{
qname = new javax.xml.namespace.QName(name.uri(), name.localName());
}
catch(Exception e)
{
throw ScriptRuntime.typeError(e.getMessage());
}
result = createTextElement(lib, qname, value);
return result;
}
/**
*
* @param name
* @return
*/
private XMLList matchAttributes(XMLName xmlName)
{
XMLList result = new XMLList(lib);
XmlCursor curs = newCursor();
if (curs.currentTokenType().isStartdoc())
{
curs.toFirstContentToken();
}
if (curs.isStart())
{
if (curs.toFirstAttribute())
{
do
{
if (qnameMatches(xmlName, curs.getName()))
{
result.addToList(createAttributeObject(curs));
}
} while (curs.toNextAttribute());
}
}
curs.dispose();
return result;
}
/**
*
* @param attrCurs
* @return
*/
private XML createAttributeObject (XmlCursor attrCurs)
{
XML result = null;
if (attrCurs.currentTokenType().isAttr())
{
result = createAttributeXML(lib, attrCurs);
}
return result;
}
//
//
// methods overriding ScriptableObject
//
//
public String getClassName ()
{
return "XML";
}
//
//
// methods overriding IdScriptableObject
//
//
/**
* XML[0] should return this, all other indexes are Undefined
*
* @param index
* @param start
* @return
*/
public Object get(int index, Scriptable start)
{
//Log("get index: " + index);
if (index == 0)
{
return this;
}
else
{
return Scriptable.NOT_FOUND;
}
}
/**
* Does the named property exist
*
* @param xmlName
* @return
*/
boolean hasXMLProperty(XMLName xmlName)
{
// Has now should return true if the property would have results > 0
return (getPropertyList(xmlName).length() > 0);
}
/**
*
* @param index
* @param start
* @return
*/
public boolean has(int index, Scriptable start)
{
return (index == 0);
}
/**
*
* @return
*/
public Object[] getIds()
{
Object[] enumObjs;
if (prototypeFlag)
{
enumObjs = new Object[0];
}
else
{
enumObjs = new Object[1];
enumObjs[0] = new Integer(0);
}
return enumObjs;
}
/**
*
* @return
*/
public Object [] getIdsForDebug()
{
return getIds();
}
/**
*
* @param xmlName
* @return
*/
Object getXMLProperty(XMLName xmlName)
{
return getPropertyList(xmlName);
}
/**
*
* @param xmlName
* @param value
*/
void putXMLProperty(XMLName xmlName, Object value)
{
//Log("put property: " + name + " value: " + value.getClass());
if (prototypeFlag)
{
}
else
{
// Special-case checks for undefined and null
if (value == null)
{
value = "null";
}
else if (value instanceof Undefined)
{
value = "undefined";
}
// Get the named property
if (xmlName.isAttributeName())
{
setAttribute(xmlName, value);
}
else if (xmlName.uri() == null &&
xmlName.localName().equals("*"))
{
setChildren(value);
}
else
{
// Convert text into XML if needed.
XMLObjectImpl xmlValue = null;
if (value instanceof XMLObjectImpl)
{
xmlValue = (XMLObjectImpl) value;
// Check for attribute type and convert to textNode
if (xmlValue instanceof XML)
{
if (((XML) xmlValue).tokenType() == XmlCursor.TokenType.ATTR)
{
xmlValue = makeXmlFromString(lib, xmlName, xmlValue.toString());
}
}
if (xmlValue instanceof XMLList)
{
for (int i = 0; i < xmlValue.length(); i++)
{
XML xml = ((XMLList) xmlValue).item(i);
if (xml.tokenType() == XmlCursor.TokenType.ATTR)
{
((XMLList) xmlValue).replace(i, makeXmlFromString(lib, xmlName, xml.toString()));
}
}
}
}
else
{
xmlValue = makeXmlFromString(lib, xmlName, ScriptRuntime.toString(value));
}
XMLList matches = getPropertyList(xmlName);
if (matches.length() == 0)
{
appendChild(xmlValue);
}
else
{
// Remove all other matches
for (int i = 1; i < matches.length(); i++)
{
removeChild(matches.item(i).childIndex());
}
// Replace first match with new value.
doPut(xmlName, matches.item(0), xmlValue);
}
}
}
}
/**
*
* @param index
* @param start
* @param value
*/
public void put(int index, Scriptable start, Object value)
{
// Spec says assignment to indexed XML object should return type error
throw ScriptRuntime.typeError("Assignment to indexed XML is not allowed");
}
/**
*
* @param name
*/
void deleteXMLProperty(XMLName name)
{
if (!name.isDescendants() && name.isAttributeName())
{
XmlCursor curs = newCursor();
// TODO: Cover the case *::name
if (name.localName().equals("*"))
{
// Delete all attributes.
if (curs.toFirstAttribute())
{
while (curs.currentTokenType().isAttr())
{
curs.removeXml();
}
}
}
else
{
// Delete an attribute.
javax.xml.namespace.QName qname = new javax.xml.namespace.QName(
name.uri(), name.localName());
curs.removeAttribute(qname);
}
curs.dispose();
}
else
{
XMLList matches = getPropertyList(name);
matches.remove();
}
}
/**
*
* @param index
*/
public void delete(int index)
{
if (index == 0)
{
remove();
}
}
//
//
// package utility functions:
//
//
protected XScriptAnnotation getAnnotation ()
{ return _anno; }
protected void changeNS (String oldURI, String newURI)
{
XmlCursor curs = newCursor();
while (curs.toParent()) {
/* Goto the top of the document */
}
TokenType tt = curs.currentTokenType();
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isStart())
{
do
{
if (tt.isStart() || tt.isAttr() || tt.isNamespace())
{
javax.xml.namespace.QName currQName = curs.getName();
if (oldURI.equals(currQName.getNamespaceURI()))
{
curs.setName(new javax.xml.namespace.QName(newURI, currQName.getLocalPart()));
}
}
tt = curs.toNextToken();
} while (!tt.isEnddoc() && !tt.isNone());
}
curs.dispose();
}
/**
*
*/
void remove ()
{
XmlCursor childCurs = newCursor();
if (childCurs.currentTokenType().isStartdoc())
{
// Remove on the document removes all children.
TokenType tt = childCurs.toFirstContentToken();
while (!tt.isEnd() && !tt.isEnddoc())
{
removeToken(childCurs);
tt = childCurs.currentTokenType(); // Now see where we're pointing after the delete -- next token.
}
}
else
{
removeToken(childCurs);
}
childCurs.dispose();
}
/**
*
* @param value
*/
void replaceAll(XML value)
{
XmlCursor curs = newCursor();
replace(curs, value);
_anno = value._anno;
curs.dispose();
}
/**
*
* @param attrName
* @param value
*/
void setAttribute(XMLName xmlName, Object value)
{
if (xmlName.uri() == null &&
xmlName.localName().equals("*"))
{
throw ScriptRuntime.typeError("@* assignment not supported.");
}
XmlCursor curs = newCursor();
String strValue = ScriptRuntime.toString(value);
if (curs.currentTokenType().isStartdoc())
{
curs.toFirstContentToken();
}
javax.xml.namespace.QName qName;
try
{
qName = new javax.xml.namespace.QName(xmlName.uri(), xmlName.localName());
}
catch(Exception e)
{
throw ScriptRuntime.typeError(e.getMessage());
}
if (!curs.setAttributeText(qName, strValue))
{
if (curs.currentTokenType().isStart())
{
// Can only add attributes inside of a start.
curs.toNextToken();
}
curs.insertAttributeWithValue(qName, strValue);
}
curs.dispose();
}
/**
*
* @param namespace
* @return
*/
private XMLList allChildNodes(String namespace)
{
XMLList result = new XMLList(lib);
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
javax.xml.namespace.QName targetProperty = new javax.xml.namespace.QName(namespace, "*");
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isContainer())
{
tt = curs.toFirstContentToken();
while (!tt.isEnd())
{
if (!tt.isStart())
{
// Not an element
result.addToList(findAnnotation(curs));
// Reset target property to null in this case
targetProperty = null;
}
else
{
// Match namespace as well if specified
if (namespace == null ||
namespace.length() == 0 ||
namespace.equals("*") ||
curs.getName().getNamespaceURI().equals(namespace))
{
// Add it to the list
result.addToList(findAnnotation(curs));
// Set target property if target name is "*",
// Otherwise if target property does not match current, then
// set to null
if (targetProperty != null)
{
if (targetProperty.getLocalPart().equals("*"))
{
targetProperty = curs.getName();
}
else if (!targetProperty.getLocalPart().equals(curs.getName().getLocalPart()))
{
// Not a match, unset target property
targetProperty = null;
}
}
}
}
// Skip over child elements
if (tt.isStart())
{
tt = curs.toEndToken();
}
tt = curs.toNextToken();
}
}
curs.dispose();
// Set the targets for this XMLList.
result.setTargets(this, targetProperty);
return result;
}
/**
*
* @return
*/
private XMLList matchDescendantAttributes(XMLName xmlName)
{
XMLList result = new XMLList(lib);
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
// Set the targets for this XMLList.
result.setTargets(this, null);
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isContainer())
{
int nestLevel = 1;
while (nestLevel > 0)
{
tt = curs.toNextToken();
// Only try to match names for attributes
if (tt.isAttr())
{
if (qnameMatches(xmlName, curs.getName()))
{
result.addToList(findAnnotation(curs));
}
}
if (tt.isStart())
{
nestLevel++;
}
else if (tt.isEnd())
{
nestLevel--;
}
else if (tt.isEnddoc())
{
// Shouldn't get here, but just in case.
break;
}
}
}
curs.dispose();
return result;
}
/**
*
* @return
*/
private XMLList matchDescendantChildren(XMLName xmlName)
{
XMLList result = new XMLList(lib);
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
// Set the targets for this XMLList.
result.setTargets(this, null);
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isContainer())
{
int nestLevel = 1;
while (nestLevel > 0)
{
tt = curs.toNextToken();
if (!tt.isAttr() && !tt.isEnd() && !tt.isEnddoc())
{
// Only try to match names for elements or processing instructions.
if (!tt.isStart() && !tt.isProcinst())
{
// Not an element or procinst, only add if qname is all
if (xmlName.localName().equals("*"))
{
result.addToList(findAnnotation(curs));
}
}
else
{
if (qnameMatches(xmlName, curs.getName()))
{
result.addToList(findAnnotation(curs));
}
}
}
if (tt.isStart())
{
nestLevel++;
}
else if (tt.isEnd())
{
nestLevel--;
}
else if (tt.isEnddoc())
{
// Shouldn't get here, but just in case.
break;
}
}
}
curs.dispose();
return result;
}
/**
*
* @param tokenType
* @return
*/
private XMLList matchChildren(XmlCursor.TokenType tokenType)
{
return matchChildren(tokenType, XMLName.formStar());
}
/**
*
* @return
*/
private XMLList matchChildren(XmlCursor.TokenType tokenType, XMLName name)
{
XMLList result = new XMLList(lib);
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
javax.xml.namespace.QName qname = new javax.xml.namespace.QName(name.uri(), name.localName());
javax.xml.namespace.QName targetProperty = qname;
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isContainer())
{
tt = curs.toFirstContentToken();
while (!tt.isEnd())
{
if (tt == tokenType)
{
// Only try to match names for elements or processing instructions.
if (!tt.isStart() && !tt.isProcinst())
{
// Not an element or no name specified.
result.addToList(findAnnotation(curs));
// Reset target property to null in this case
targetProperty = null;
}
else
{
// Match names as well
if (qnameMatches(name, curs.getName()))
{
// Add it to the list
result.addToList(findAnnotation(curs));
// Set target property if target name is "*",
// Otherwise if target property does not match current, then
// set to null
if (targetProperty != null)
{
if (targetProperty.getLocalPart().equals("*"))
{
targetProperty = curs.getName();
}
else if (!targetProperty.getLocalPart().equals(curs.getName().getLocalPart()))
{
// Not a match, unset target property
targetProperty = null;
}
}
}
}
}
// Skip over child elements
if (tt.isStart())
{
tt = curs.toEndToken();
}
tt = curs.toNextToken();
}
}
curs.dispose();
if (tokenType == XmlCursor.TokenType.START)
{
// Set the targets for this XMLList.
result.setTargets(this, targetProperty);
}
return result;
}
/**
*
* @param template
* @param match
* @return
*/
private boolean qnameMatches(XMLName template, javax.xml.namespace.QName match)
{
boolean matches = false;
if (template.uri() == null ||
template.uri().equals(match.getNamespaceURI()))
{
// URI OK, test name
if (template.localName().equals("*") ||
template.localName().equals(match.getLocalPart()))
{
matches = true;
}
}
return matches;
}
//
//
// Methods from section 12.4.4 in the spec
//
//
/**
* The addNamespace method adds a namespace declaration to the in scope
* namespaces for this XML object and returns this XML object.
*
* @param toAdd
*/
XML addNamespace(Namespace ns)
{
// When a namespace is used it will be added automatically
// to the inScopeNamespaces set. There is no need to add
// Namespaces with undefined prefixes.
String nsPrefix = ns.prefix();
if (nsPrefix == null) return this;
XmlCursor cursor = newCursor();
try
{
if(!cursor.isContainer()) return this;
javax.xml.namespace.QName qname = cursor.getName();
// Don't add a default namespace declarations to containers
// with QNames in no namespace.
if(qname.getNamespaceURI().equals("") &&
nsPrefix.equals("")) return this;
// Get all declared namespaces that are in scope
Map prefixToURI = NamespaceHelper.getAllNamespaces(lib, cursor);
String uri = (String)prefixToURI.get(nsPrefix);
if(uri != null)
{
// Check if the Namespace is not already in scope
if(uri.equals(ns.uri())) return this;
cursor.push();
// Let's see if we have to delete a namespace declaration
while(cursor.toNextToken().isAnyAttr())
{
if(cursor.isNamespace())
{
qname = cursor.getName();
String prefix = qname.getLocalPart();
if(prefix.equals(nsPrefix))
{
// Delete the current Namespace declaration
cursor.removeXml();
break;
}
}
}
cursor.pop();
}
cursor.toNextToken();
cursor.insertNamespace(nsPrefix, ns.uri());
}
finally
{
cursor.dispose();
}
return this;
}
/**
*
* @param xml
* @return
*/
XML appendChild(Object xml)
{
XmlCursor curs = newCursor();
if (curs.isStartdoc())
{
curs.toFirstContentToken();
}
// Move the cursor to the end of this element
if (curs.isStart())
{
curs.toEndToken();
}
insertChild(curs, xml);
curs.dispose();
return this;
}
/**
*
* @param name
* @return
*/
XMLList attribute(XMLName xmlName)
{
return matchAttributes(xmlName);
}
/**
*
* @return
*/
XMLList attributes()
{
XMLName xmlName = XMLName.formStar();
return matchAttributes(xmlName);
}
XMLList child(long index)
{
XMLList result = new XMLList(lib);
result.setTargets(this, null);
result.addToList(getXmlChild(index));
return result;
}
XMLList child(XMLName xmlName)
{
if (xmlName == null)
return new XMLList(lib);
XMLList result;
if (xmlName.localName().equals("*"))
{
result = allChildNodes(xmlName.uri());
}
else
{
result = matchChildren(XmlCursor.TokenType.START, xmlName);
}
return result;
}
/**
*
* @param index
* @return
*/
XML getXmlChild(long index)
{
XML result = null;
XmlCursor curs = newCursor();
if (moveToChild(curs, index, false, true))
{
result = createXML(lib, curs);
}
curs.dispose();
return result;
}
/**
*
* @return
*/
int childIndex()
{
int index = 0;
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
while (true)
{
if (tt.isText())
{
index++;
if (!curs.toPrevSibling())
{
break;
}
}
else if (tt.isStart())
{
tt = curs.toPrevToken();
if (tt.isEnd())
{
curs.toNextToken();
if (!curs.toPrevSibling())
{
break;
}
index++;
}
else
{
// Hit the parent start tag so get out we're down counting children.
break;
}
}
else if (tt.isComment() || tt.isProcinst())
{
curs.toPrevToken();
}
else
{
break;
}
tt = curs.currentTokenType();
}
index = curs.currentTokenType().isStartdoc() ? -1 : index;
curs.dispose();
return index;
}
/**
*
* @return
*/
XMLList children()
{
return allChildNodes(null);
}
/**
*
* @return
*/
XMLList comments()
{
return matchChildren(XmlCursor.TokenType.COMMENT);
}
/**
*
* @param xml
* @return
*/
boolean contains(Object xml)
{
boolean result = false;
if (xml instanceof XML)
{
result = equivalentXml(xml);
}
return result;
}
/**
*
* @return
*/
Object copy()
{
XmlCursor srcCurs = newCursor();
if (srcCurs.isStartdoc())
{
srcCurs.toFirstContentToken();
}
XML xml = createEmptyXML(lib);
XmlCursor destCurs = xml.newCursor();
destCurs.toFirstContentToken();
srcCurs.copyXml(destCurs);
destCurs.dispose();
srcCurs.dispose();
return xml;
}
/**
*
* @param name
* @return
*/
XMLList descendants(XMLName xmlName)
{
XMLList result;
if (xmlName.isAttributeName())
{
result = matchDescendantAttributes(xmlName);
}
else
{
result = matchDescendantChildren(xmlName);
}
return result;
}
/**
* The inScopeNamespaces method returns an Array of Namespace objects
* representing the namespaces in scope for this XML object in the
* context of its parent.
*
* @return Array of all Namespaces in scope for this XML Object.
*/
Object[] inScopeNamespaces()
{
XmlCursor cursor = newCursor();
Object[] namespaces = NamespaceHelper.inScopeNamespaces(lib, cursor);
cursor.dispose();
return namespaces;
}
/**
*
* @param child
* @param xml
*/
XML insertChildAfter(Object child, Object xml)
{
if (child == null)
{
// Spec says inserting after nothing is the same as prepending
prependChild(xml);
}
else if (child instanceof XML)
{
insertChild((XML) child, xml, APPEND_CHILD);
}
return this;
}
/**
*
* @param child
* @param xml
*/
XML insertChildBefore(Object child, Object xml)
{
if (child == null)
{
// Spec says inserting before nothing is the same as appending
appendChild(xml);
}
else if (child instanceof XML)
{
insertChild((XML) child, xml, PREPEND_CHILD);
}
return this;
}
/**
*
* @return
*/
boolean hasOwnProperty(XMLName xmlName)
{
boolean hasProperty = false;
if (prototypeFlag)
{
String property = xmlName.localName();
hasProperty = (0 != findPrototypeId(property));
}
else
{
hasProperty = (getPropertyList(xmlName).length() > 0);
}
return hasProperty;
}
/**
*
* @return
*/
boolean hasComplexContent()
{
return !hasSimpleContent();
}
/**
*
* @return
*/
boolean hasSimpleContent()
{
boolean simpleContent = false;
XmlCursor curs = newCursor();
if (curs.isAttr() || curs.isText()) {
return true;
}
if (curs.isStartdoc())
{
curs.toFirstContentToken();
}
simpleContent = !(curs.toFirstChild());
curs.dispose();
return simpleContent;
}
/**
* Length of an XML object is always 1, it's a list of XML objects of size 1.
*
* @return
*/
int length()
{
return 1;
}
/**
*
* @return
*/
String localName()
{
XmlCursor cursor = newCursor();
if (cursor.isStartdoc())
cursor.toFirstContentToken();
String name = null;
if(cursor.isStart() ||
cursor.isAttr() ||
cursor.isProcinst())
{
javax.xml.namespace.QName qname = cursor.getName();
name = qname.getLocalPart();
}
cursor.dispose();
return name;
}
/**
* The name method returns the qualified name associated with this XML object.
*
* @return The qualified name associated with this XML object.
*/
QName name()
{
XmlCursor cursor = newCursor();
if (cursor.isStartdoc())
cursor.toFirstContentToken();
QName name = null;
if(cursor.isStart() ||
cursor.isAttr() ||
cursor.isProcinst())
{
javax.xml.namespace.QName qname = cursor.getName();
if(cursor.isProcinst())
{
name = new QName(lib, "", qname.getLocalPart(), "");
}
else
{
String uri = qname.getNamespaceURI();
String prefix = qname.getPrefix();
name = new QName(lib, uri, qname.getLocalPart(), prefix);
}
}
cursor.dispose();
return name;
}
/**
*
* @param prefix
* @return
*/
Object namespace(String prefix)
{
XmlCursor cursor = newCursor();
if (cursor.isStartdoc())
{
cursor.toFirstContentToken();
}
Object result = null;
if (prefix == null)
{
if(cursor.isStart() ||
cursor.isAttr())
{
Object[] inScopeNS = NamespaceHelper.inScopeNamespaces(lib, cursor);
// XXX Is it reaaly necessary to create the second cursor?
XmlCursor cursor2 = newCursor();
if (cursor2.isStartdoc())
cursor2.toFirstContentToken();
result = NamespaceHelper.getNamespace(lib, cursor2, inScopeNS);
cursor2.dispose();
}
}
else
{
Map prefixToURI = NamespaceHelper.getAllNamespaces(lib, cursor);
String uri = (String)prefixToURI.get(prefix);
result = (uri == null) ? Undefined.instance : new Namespace(lib, prefix, uri);
}
cursor.dispose();
return result;
}
/**
*
* @return
*/
Object[] namespaceDeclarations()
{
XmlCursor cursor = newCursor();
Object[] namespaces = NamespaceHelper.namespaceDeclarations(lib, cursor);
cursor.dispose();
return namespaces;
}
/**
*
* @return
*/
Object nodeKind()
{
String result;
XmlCursor.TokenType tt = tokenType();
if (tt == XmlCursor.TokenType.ATTR)
{
result = "attribute";
}
else if (tt == XmlCursor.TokenType.TEXT)
{
result = "text";
}
else if (tt == XmlCursor.TokenType.COMMENT)
{
result = "comment";
}
else if (tt == XmlCursor.TokenType.PROCINST)
{
result = "processing-instruction";
}
else if (tt == XmlCursor.TokenType.START)
{
result = "element";
}
else
{
// A non-existant node has the nodeKind() of text
result = "text";
}
return result;
}
/**
*
*/
void normalize()
{
XmlCursor curs = newCursor();
TokenType tt = curs.currentTokenType();
// Walk through the tokens removing empty text nodes and merging adjacent text nodes.
if (tt.isStartdoc())
{
tt = curs.toFirstContentToken();
}
if (tt.isContainer())
{
int nestLevel = 1;
String previousText = null;
while (nestLevel > 0)
{
tt = curs.toNextToken();
if (tt == XmlCursor.TokenType.TEXT)
{
String currentText = curs.getChars().trim();
if (currentText.trim().length() == 0)
{
// Empty text node, remove.
removeToken(curs);
curs.toPrevToken();
}
else if (previousText == null)
{
// No previous text node, reset to trimmed version
previousText = currentText;
}
else
{
// It appears that this case never happens with XBeans.
// Previous text node exists, concatenate
String newText = previousText + currentText;
curs.toPrevToken();
removeToken(curs);
removeToken(curs);
curs.insertChars(newText);
}
}
else
{
previousText = null;
}
if (tt.isStart())
{
nestLevel++;
}
else if (tt.isEnd())
{
nestLevel--;
}
else if (tt.isEnddoc())
{
// Shouldn't get here, but just in case.
break;
}
}
}
curs.dispose();
}
/**
*
* @return
*/
Object parent()
{
Object parent;
XmlCursor curs = newCursor();
if (curs.isStartdoc())
{
// At doc level - no parent
parent = Undefined.instance;
}
else
{
if (curs.toParent())
{
if (curs.isStartdoc())
{
// Was top-level - no parent
parent = Undefined.instance;
}
else
{
parent = getFromAnnotation(lib, findAnnotation(curs));
}
}
else
{
// No parent
parent = Undefined.instance;
}
}
curs.dispose();
return parent;
}
/**
*
* @param xml
* @return
*/
XML prependChild (Object xml)
{
XmlCursor curs = newCursor();
if (curs.isStartdoc())
{
curs.toFirstContentToken();
}
// Move the cursor to the first content token
curs.toFirstContentToken();
insertChild(curs, xml);
curs.dispose();
return this;
}
/**
*
* @return
*/
Object processingInstructions(XMLName xmlName)
{
return matchChildren(XmlCursor.TokenType.PROCINST, xmlName);
}
/**
*
* @param name
* @return
*/
boolean propertyIsEnumerable(Object name)
{
boolean result;
if (name instanceof Integer) {
result = (((Integer)name).intValue() == 0);
} else if (name instanceof Number) {
double x = ((Number)name).doubleValue();
// Check that number is posotive 0
result = (x == 0.0 && 1.0 / x > 0);
} else {
result = ScriptRuntime.toString(name).equals("0");
}
return result;
}
/**
*
* @param namespace
*/
XML removeNamespace(Namespace ns)
{
XmlCursor cursor = newCursor();
try
{
if(cursor.isStartdoc())
cursor.toFirstContentToken();
if(!cursor.isStart()) return this;
String nsPrefix = ns.prefix();
String nsURI = ns.uri();
Map prefixToURI = new HashMap();
int depth = 1;
while(!(cursor.isEnd() && depth == 0))
{
if(cursor.isStart())
{
// Get the namespaces declared in this element.
// The ones with undefined prefixes are not candidates
// for removal because they are used.
prefixToURI.clear();
NamespaceHelper.getNamespaces(cursor, prefixToURI);
ObjArray inScopeNSBag = new ObjArray();
Iterator i = prefixToURI.entrySet().iterator();
while(i.hasNext())
{
Map.Entry entry = (Map.Entry)i.next();
ns = new Namespace(lib, (String)entry.getKey(), (String)entry.getValue());
inScopeNSBag.add(ns);
}
// Add the URI we are looking for to avoid matching
// non-existing Namespaces.
ns = new Namespace(lib, nsURI);
inScopeNSBag.add(ns);
Object[] inScopeNS = inScopeNSBag.toArray();
// Check the element name
Namespace n = NamespaceHelper.getNamespace(lib, cursor,
inScopeNS);
if(nsURI.equals(n.uri()) &&
(nsPrefix == null ||
nsPrefix.equals(n.prefix())))
{
// This namespace is used
return this;
}
// Check the attributes
cursor.push();
boolean hasNext = cursor.toFirstAttribute();
while(hasNext)
{
n = NamespaceHelper.getNamespace(lib, cursor, inScopeNS);
if(nsURI.equals(n.uri()) &&
(nsPrefix == null ||
nsPrefix.equals(n.prefix())))
{
// This namespace is used
return this;
}
hasNext = cursor.toNextAttribute();
}
cursor.pop();
if(nsPrefix == null)
{
// Remove all namespaces declarations that match nsURI
i = prefixToURI.entrySet().iterator();
while(i.hasNext())
{
Map.Entry entry = (Map.Entry)i.next();
if(entry.getValue().equals(nsURI))
NamespaceHelper.removeNamespace(cursor, (String)entry.getKey());
}
}
else if(nsURI.equals(prefixToURI.get(nsPrefix)))
{
// Remove the namespace declaration that matches nsPrefix
NamespaceHelper.removeNamespace(cursor, String.valueOf(nsPrefix));
}
}
switch(cursor.toNextToken().intValue())
{
case XmlCursor.TokenType.INT_START:
depth++;
break;
case XmlCursor.TokenType.INT_END:
depth--;
break;
}
}
}
finally
{
cursor.dispose();
}
return this;
}
XML replace(long index, Object xml)
{
XMLList xlChildToReplace = child(index);
if (xlChildToReplace.length() > 0)
{
// One exists an that index
XML childToReplace = xlChildToReplace.item(0);
insertChildAfter(childToReplace, xml);
removeChild(index);
}
return this;
}
/**
*
* @param propertyName
* @param xml
* @return
*/
XML replace(XMLName xmlName, Object xml)
{
putXMLProperty(xmlName, xml);
return this;
}
/**
*
* @param xml
*/
XML setChildren(Object xml)
{
// remove all children
XMLName xmlName = XMLName.formStar();
XMLList matches = getPropertyList(xmlName);
matches.remove();
// append new children
appendChild(xml);
return this;
}
/**
*
* @param name
*/
void setLocalName(String localName)
{
XmlCursor cursor = newCursor();
try
{
if(cursor.isStartdoc())
cursor.toFirstContentToken();
if(cursor.isText() || cursor.isComment()) return;
javax.xml.namespace.QName qname = cursor.getName();
cursor.setName(new javax.xml.namespace.QName(
qname.getNamespaceURI(), localName, qname.getPrefix()));
}
finally
{
cursor.dispose();
}
}
/**
*
* @param name
*/
void setName(QName qname)
{
XmlCursor cursor = newCursor();
try
{
if(cursor.isStartdoc())
cursor.toFirstContentToken();
if(cursor.isText() || cursor.isComment()) return;
if(cursor.isProcinst())
{
String localName = qname.localName();
cursor.setName(new javax.xml.namespace.QName(localName));
}
else
{
String prefix = qname.prefix();
if (prefix == null) { prefix = ""; }
cursor.setName(new javax.xml.namespace.QName(
qname.uri(), qname.localName(), prefix));
}
}
finally
{
cursor.dispose();
}
}
/**
*
* @param ns
*/
void setNamespace(Namespace ns)
{
XmlCursor cursor = newCursor();
try
{
if(cursor.isStartdoc())
cursor.toFirstContentToken();
if(cursor.isText() ||
cursor.isComment() ||
cursor.isProcinst()) return;
String prefix = ns.prefix();
if (prefix == null) {
prefix = "";
}
cursor.setName(new javax.xml.namespace.QName(
ns.uri(), localName(), prefix));
}
finally
{
cursor.dispose();
}
}
/**
*
* @return
*/
XMLList text()
{
return matchChildren(XmlCursor.TokenType.TEXT);
}
/**
*
* @return
*/
public String toString()
{
String result;
XmlCursor curs = newCursor();
if (curs.isStartdoc())
{
curs.toFirstContentToken();
}
if (curs.isText())
{
result = curs.getChars();
}
else if (curs.isStart() && hasSimpleContent())
{
result = curs.getTextValue();
}
else
{
result = toXMLString(0);
}
return result;
}
String toSource(int indent)
{
// XXX Does toXMLString always return valid XML literal?
return toXMLString(indent);
}
/**
*
* @return
*/
String toXMLString(int indent)
{
// XXX indent is ignored
String result;
XmlCursor curs = newCursor();
if (curs.isStartdoc())
{
curs.toFirstContentToken();
}
try
{
if (curs.isText())
{
result = curs.getChars();
}
else if (curs.isAttr())
{
result = curs.getTextValue();
}
else if (curs.isComment() || curs.isProcinst())
{
result = XML.dumpNode(curs, getOptions());
// todo: XBeans-dependent hack here
// If it's a comment or PI, take off the xml-frament stuff
String start = "<xml-fragment>";
String end = "</xml-fragment>";
if (result.startsWith(start))
{
result = result.substring(start.length());
}
if (result.endsWith(end))
{
result = result.substring(0, result.length() - end.length());
}
}
else
{
result = XML.dumpNode(curs, getOptions());
}
}
finally
{
curs.dispose();
}
return result;
}
/**
*
* @return
*/
Object valueOf()
{
return this;
}
//
// Other public Functions from XMLObject
//
/**
*
* @param target
* @return
*/
boolean equivalentXml(Object target)
{
boolean result = false;
if (target instanceof XML)
{
XML otherXml = (XML) target;
// Compare with toString() if either side is text node or attribute
// otherwise compare as XML
XmlCursor.TokenType thisTT = tokenType();
XmlCursor.TokenType otherTT = otherXml.tokenType();
if (thisTT == XmlCursor.TokenType.ATTR || otherTT == XmlCursor.TokenType.ATTR ||
thisTT == XmlCursor.TokenType.TEXT || otherTT == XmlCursor.TokenType.TEXT)
{
result = toString().equals(otherXml.toString());
}
else
{
XmlCursor cursOne = newCursor();
XmlCursor cursTwo = otherXml.newCursor();
result = LogicalEquality.nodesEqual(cursOne, cursTwo);
cursOne.dispose();
cursTwo.dispose();
// Old way of comparing by string.
// boolean orgPrettyPrinting = prototype.prettyPrinting;
// prototype.prettyPrinting = true;
// result = toXMLString(0).equals(otherXml.toXMLString(0));
// prototype.prettyPrinting = orgPrettyPrinting;
}
}
else if (target instanceof XMLList)
{
XMLList otherList = (XMLList) target;
if (otherList.length() == 1)
{
result = equivalentXml(otherList.getXmlFromAnnotation(0));
}
}
else if (hasSimpleContent())
{
String otherStr = ScriptRuntime.toString(target);
result = toString().equals(otherStr);
}
return result;
}
/**
*
* @param name
* @param start
* @return
*/
XMLList getPropertyList(XMLName name)
{
XMLList result;
// Get the named property
if (name.isDescendants())
{
result = descendants(name);
}
else if (name.isAttributeName())
{
result = attribute(name);
}
else
{
result = child(name);
}
return result;
}
protected Object jsConstructor(Context cx, boolean inNewExpr,
Object[] args)
{
if (args.length == 0) {
return createFromJS(lib, "");
} else {
Object arg0 = args[0];
if (!inNewExpr && arg0 instanceof XML) {
// XML(XML) returns the same object.
return arg0;
}
return createFromJS(lib, arg0);
}
}
/**
* See ECMA 357, 11_2_2_1, Semantics, 3_f.
*/
public Scriptable getExtraMethodSource(Context cx)
{
if (hasSimpleContent()) {
String src = toString();
return ScriptRuntime.toObjectOrNull(cx, src);
}
return null;
}
XmlObject getXmlObject()
{
XmlObject xo;
XmlCursor cursor = newCursor();
try {
xo = cursor.getObject();
} finally {
cursor.dispose();
}
return xo;
}
}