jscover.mozilla.javascript.xml.impl.xmlbeans.XML Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in
Java. It is typically embedded into Java applications to provide
scripting to end users.
/* -*- 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 jscover.mozilla.javascript.xml.impl.xmlbeans;
import java.io.Serializable;
import java.util.*;
import jscover.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 = "" + frag + " ";
}
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("" +
cursToCopy.getChars() +
" ").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 = "";
String end = " ";
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;
}
}