0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Ethan Hugg
0026: * Terry Lucas
0027: * Milen Nankov
0028: *
0029: * Alternatively, the contents of this file may be used under the terms of
0030: * the GNU General Public License Version 2 or later (the "GPL"), in which
0031: * case the provisions of the GPL are applicable instead of those above. If
0032: * you wish to allow use of your version of this file only under the terms of
0033: * the GPL and not to allow others to use your version of this file under the
0034: * MPL, indicate your decision by deleting the provisions above and replacing
0035: * them with the notice and other provisions required by the GPL. If you do
0036: * not delete the provisions above, a recipient may use your version of this
0037: * file under either the MPL or the GPL.
0038: *
0039: * ***** END LICENSE BLOCK ***** */
0040:
0041: package org.mozilla.javascript.xml.impl.xmlbeans;
0042:
0043: import java.io.Serializable;
0044: import java.util.*;
0045:
0046: import org.mozilla.javascript.*;
0047:
0048: import org.apache.xmlbeans.XmlCursor;
0049: import org.apache.xmlbeans.XmlCursor.XmlBookmark;
0050: import org.apache.xmlbeans.XmlCursor.TokenType;
0051: import org.apache.xmlbeans.XmlException;
0052: import org.apache.xmlbeans.XmlObject;
0053: import org.apache.xmlbeans.XmlOptions;
0054:
0055: class XML extends XMLObjectImpl {
0056: static final long serialVersionUID = -630969919086449092L;
0057:
0058: final static class XScriptAnnotation extends XmlBookmark implements
0059: Serializable {
0060: private static final long serialVersionUID = 1L;
0061:
0062: javax.xml.namespace.QName _name;
0063: XML _xScriptXML;
0064:
0065: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0066: //
0067: // Constructurs
0068: //
0069: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0070:
0071: XScriptAnnotation(XmlCursor curs) {
0072: _name = curs.getName();
0073: }
0074:
0075: }
0076:
0077: /**
0078: *
0079: */
0080: final static class NamespaceDeclarations {
0081: private int _prefixIdx;
0082: private StringBuffer _namespaceDecls;
0083: private String _defaultNSURI;
0084:
0085: NamespaceDeclarations(XmlCursor curs) {
0086: _prefixIdx = 0;
0087: _namespaceDecls = new StringBuffer();
0088:
0089: skipNonElements(curs);
0090: _defaultNSURI = curs.namespaceForPrefix("");
0091:
0092: if (isAnyDefaultNamespace()) {
0093: addDecl("", _defaultNSURI);
0094: }
0095: }
0096:
0097: private void addDecl(String prefix, String ns) {
0098: _namespaceDecls
0099: .append((prefix.length() > 0 ? "declare namespace "
0100: + prefix : "default element namespace")
0101: + " = \"" + ns + "\"" + "\n");
0102: }
0103:
0104: String getNextPrefix(String ns) {
0105: String prefix = "NS" + _prefixIdx++;
0106:
0107: _namespaceDecls.append("declare namespace " + prefix
0108: + " = " + "\"" + ns + "\"" + "\n");
0109:
0110: return prefix;
0111: }
0112:
0113: boolean isAnyDefaultNamespace() {
0114: return _defaultNSURI != null ? _defaultNSURI.length() > 0
0115: : false;
0116: }
0117:
0118: String getDeclarations() {
0119: return _namespaceDecls.toString();
0120: }
0121: }
0122:
0123: // Fields
0124: //static final XML prototype = new XML();
0125: private XScriptAnnotation _anno;
0126:
0127: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0128: //
0129: // Constructors
0130: //
0131: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0132:
0133: /**
0134: *
0135: * @param anno
0136: */
0137: private XML(XMLLibImpl lib, XScriptAnnotation anno) {
0138: super (lib, lib.xmlPrototype);
0139: _anno = anno;
0140: _anno._xScriptXML = this ;
0141: }
0142:
0143: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0144: //
0145: // Public factories for creating a XScript XML object given an XBean cursor.
0146: //
0147: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0148:
0149: static XML createEmptyXML(XMLLibImpl lib) {
0150: XScriptAnnotation anno;
0151:
0152: XmlObject xo = XmlObject.Factory.newInstance();
0153: XmlCursor curs = xo.newCursor();
0154: try {
0155: anno = new XScriptAnnotation(curs);
0156: curs.setBookmark(anno);
0157: } finally {
0158: curs.dispose();
0159: }
0160:
0161: return new XML(lib, anno);
0162: }
0163:
0164: private static XML createXML(XMLLibImpl lib, XmlCursor curs) {
0165: if (curs.currentTokenType().isStartdoc()) {
0166: curs.toFirstContentToken();
0167: }
0168:
0169: XScriptAnnotation anno = findAnnotation(curs);
0170:
0171: return new XML(lib, anno);
0172: }
0173:
0174: /**
0175: * Special constructor for making an attribute
0176: *
0177: */
0178: private static XML createAttributeXML(XMLLibImpl lib,
0179: XmlCursor cursor) {
0180: if (!cursor.isAttr())
0181: throw new IllegalArgumentException();
0182:
0183: XScriptAnnotation anno = new XScriptAnnotation(cursor);
0184: cursor.setBookmark(anno);
0185:
0186: return new XML(lib, anno);
0187: }
0188:
0189: /**
0190: *
0191: * @param qname
0192: * @param value
0193: * @return
0194: */
0195: static XML createTextElement(XMLLibImpl lib,
0196: javax.xml.namespace.QName qname, String value) {
0197: XScriptAnnotation anno;
0198:
0199: XmlObject xo = XmlObject.Factory.newInstance();
0200: XmlCursor cursor = xo.newCursor();
0201: try {
0202: cursor.toNextToken();
0203:
0204: cursor.beginElement(qname.getLocalPart(), qname
0205: .getNamespaceURI());
0206: //if(namespace.length() > 0)
0207: // cursor.insertNamespace("", namespace);
0208: cursor.insertChars(value);
0209:
0210: cursor.toStartDoc();
0211: cursor.toNextToken();
0212: anno = new XScriptAnnotation(cursor);
0213: cursor.setBookmark(anno);
0214: } finally {
0215: cursor.dispose();
0216: }
0217:
0218: return new XML(lib, anno);
0219: }
0220:
0221: static XML createFromXmlObject(XMLLibImpl lib, XmlObject xo) {
0222: XScriptAnnotation anno;
0223: XmlCursor curs = xo.newCursor();
0224: if (curs.currentTokenType().isStartdoc()) {
0225: curs.toFirstContentToken();
0226: }
0227: try {
0228: anno = new XScriptAnnotation(curs);
0229: curs.setBookmark(anno);
0230: } finally {
0231: curs.dispose();
0232: }
0233: return new XML(lib, anno);
0234: }
0235:
0236: static XML createFromJS(XMLLibImpl lib, Object inputObject) {
0237: XmlObject xo;
0238: boolean isText = false;
0239: String frag;
0240:
0241: if (inputObject == null || inputObject == Undefined.instance) {
0242: frag = "";
0243: } else if (inputObject instanceof XMLObjectImpl) {
0244: // todo: faster way for XMLObjects?
0245: frag = ((XMLObjectImpl) inputObject).toXMLString(0);
0246: } else {
0247: if (inputObject instanceof Wrapper) {
0248: Object wrapped = ((Wrapper) inputObject).unwrap();
0249: if (wrapped instanceof XmlObject) {
0250: return createFromXmlObject(lib, (XmlObject) wrapped);
0251: }
0252: }
0253: frag = ScriptRuntime.toString(inputObject);
0254: }
0255:
0256: if (frag.trim().startsWith("<>")) {
0257: throw ScriptRuntime
0258: .typeError("Invalid use of XML object anonymous tags <></>.");
0259: }
0260:
0261: if (frag.indexOf("<") == -1) {
0262: // Must be solo text node, wrap in XML fragment
0263: isText = true;
0264: frag = "<textFragment>" + frag + "</textFragment>";
0265: }
0266:
0267: XmlOptions options = new XmlOptions();
0268:
0269: if (lib.ignoreComments) {
0270: options.put(XmlOptions.LOAD_STRIP_COMMENTS);
0271: }
0272:
0273: if (lib.ignoreProcessingInstructions) {
0274: options.put(XmlOptions.LOAD_STRIP_PROCINSTS);
0275: }
0276:
0277: if (lib.ignoreWhitespace) {
0278: options.put(XmlOptions.LOAD_STRIP_WHITESPACE);
0279: }
0280:
0281: try {
0282: xo = XmlObject.Factory.parse(frag, options);
0283:
0284: // Apply the default namespace
0285: Context cx = Context.getCurrentContext();
0286: String defaultURI = lib.getDefaultNamespaceURI(cx);
0287:
0288: if (defaultURI.length() > 0) {
0289: XmlCursor cursor = xo.newCursor();
0290: boolean isRoot = true;
0291: while (!cursor.toNextToken().isEnddoc()) {
0292: if (!cursor.isStart())
0293: continue;
0294:
0295: // Check if this element explicitly sets the
0296: // default namespace
0297: boolean defaultNSDeclared = false;
0298: cursor.push();
0299: while (cursor.toNextToken().isAnyAttr()) {
0300: if (cursor.isNamespace()) {
0301: if (cursor.getName().getLocalPart()
0302: .length() == 0) {
0303: defaultNSDeclared = true;
0304: break;
0305: }
0306: }
0307: }
0308: cursor.pop();
0309: if (defaultNSDeclared) {
0310: cursor.toEndToken();
0311: continue;
0312: }
0313:
0314: // Check if this element's name is in no namespace
0315: javax.xml.namespace.QName qname = cursor.getName();
0316: if (qname.getNamespaceURI().length() == 0) {
0317: // Change the namespace
0318: qname = new javax.xml.namespace.QName(
0319: defaultURI, qname.getLocalPart());
0320: cursor.setName(qname);
0321: }
0322:
0323: if (isRoot) {
0324: // Declare the default namespace
0325: cursor.push();
0326: cursor.toNextToken();
0327: cursor.insertNamespace("", defaultURI);
0328: cursor.pop();
0329:
0330: isRoot = false;
0331: }
0332: }
0333: cursor.dispose();
0334: }
0335: } catch (XmlException xe) {
0336: /*
0337: todo need to handle namespace prefix not found in XML look for namespace type in the scope change.
0338:
0339: String errorMsg = "Use of undefined namespace prefix: ";
0340: String msg = xe.getError().getMessage();
0341: if (msg.startsWith(errorMsg))
0342: {
0343: String prefix = msg.substring(errorMsg.length());
0344: }
0345: */
0346: String errMsg = xe.getMessage();
0347: if (errMsg
0348: .equals("error: Unexpected end of file after null")) {
0349: // Create an empty document.
0350: xo = XmlObject.Factory.newInstance();
0351: } else {
0352: throw ScriptRuntime.typeError(xe.getMessage());
0353: }
0354: } catch (Throwable e) {
0355: // todo: TLL Catch specific exceptions during parse.
0356: throw ScriptRuntime.typeError("Not Parsable as XML");
0357: }
0358:
0359: XmlCursor curs = xo.newCursor();
0360: if (curs.currentTokenType().isStartdoc()) {
0361: curs.toFirstContentToken();
0362: }
0363:
0364: if (isText) {
0365: // Move it to point to the text node
0366: curs.toFirstContentToken();
0367: }
0368:
0369: XScriptAnnotation anno;
0370: try {
0371: anno = new XScriptAnnotation(curs);
0372: curs.setBookmark(anno);
0373: } finally {
0374: curs.dispose();
0375: }
0376:
0377: return new XML(lib, anno);
0378: }
0379:
0380: static XML getFromAnnotation(XMLLibImpl lib, XScriptAnnotation anno) {
0381: if (anno._xScriptXML == null) {
0382: anno._xScriptXML = new XML(lib, anno);
0383: }
0384:
0385: return anno._xScriptXML;
0386: }
0387:
0388: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0389: //
0390: // Private functions:
0391: //
0392: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0393:
0394: /**
0395: *
0396: * @param curs
0397: * @return
0398: */
0399: private static TokenType skipNonElements(XmlCursor curs) {
0400: TokenType tt = curs.currentTokenType();
0401: while (tt.isComment() || tt.isProcinst()) {
0402: tt = curs.toNextToken();
0403: }
0404:
0405: return tt;
0406: }
0407:
0408: /**
0409: *
0410: * @param curs
0411: * @return
0412: */
0413: protected static XScriptAnnotation findAnnotation(XmlCursor curs) {
0414: XmlBookmark anno = curs.getBookmark(XScriptAnnotation.class);
0415: if (anno == null) {
0416: anno = new XScriptAnnotation(curs);
0417: curs.setBookmark(anno);
0418: }
0419:
0420: return (XScriptAnnotation) anno;
0421: }
0422:
0423: /**
0424: *
0425: * @return
0426: */
0427: private XmlOptions getOptions() {
0428: XmlOptions options = new XmlOptions();
0429:
0430: if (lib.ignoreComments) {
0431: options.put(XmlOptions.LOAD_STRIP_COMMENTS);
0432: }
0433:
0434: if (lib.ignoreProcessingInstructions) {
0435: options.put(XmlOptions.LOAD_STRIP_PROCINSTS);
0436: }
0437:
0438: if (lib.ignoreWhitespace) {
0439: options.put(XmlOptions.LOAD_STRIP_WHITESPACE);
0440: }
0441:
0442: if (lib.prettyPrinting) {
0443: options.put(XmlOptions.SAVE_PRETTY_PRINT, null);
0444: options.put(XmlOptions.SAVE_PRETTY_PRINT_INDENT,
0445: new Integer(lib.prettyIndent));
0446: }
0447:
0448: return options;
0449: }
0450:
0451: /**
0452: *
0453: * @param cursor
0454: * @param opts
0455: * @return
0456: */
0457: private static String dumpNode(XmlCursor cursor, XmlOptions opts) {
0458: if (cursor.isText())
0459: return cursor.getChars();
0460:
0461: if (cursor.isFinish())
0462: return "";
0463:
0464: cursor.push();
0465: boolean wanRawText = cursor.isStartdoc()
0466: && !cursor.toFirstChild();
0467: cursor.pop();
0468:
0469: return wanRawText ? cursor.getTextValue() : cursor
0470: .xmlText(opts);
0471: }
0472:
0473: /**
0474: *
0475: * @return
0476: */
0477: private XmlCursor newCursor() {
0478: XmlCursor curs;
0479:
0480: if (_anno != null) {
0481: curs = _anno.createCursor();
0482: if (curs == null) {
0483: // Orphaned case.
0484: XmlObject doc = XmlObject.Factory.newInstance();
0485: curs = doc.newCursor();
0486:
0487: if (_anno._name != null) {
0488: curs.toNextToken();
0489: curs.insertElement(_anno._name);
0490: curs.toPrevSibling();
0491: }
0492:
0493: curs.setBookmark(_anno);
0494: }
0495: } else {
0496: XmlObject doc = XmlObject.Factory.newInstance();
0497: curs = doc.newCursor();
0498: }
0499:
0500: return curs;
0501: }
0502:
0503: /*
0504: * fUseStartDoc used by child(int index) the index is at startDoc is the element at the top-level
0505: * otherwise we always want to drill in.
0506: */
0507: private boolean moveToChild(XmlCursor curs, long index,
0508: boolean fFirstChild, boolean fUseStartDoc) {
0509: if (index < 0)
0510: throw new IllegalArgumentException();
0511:
0512: long idxChild = 0;
0513:
0514: if (!fUseStartDoc && curs.currentTokenType().isStartdoc()) {
0515: // We always move to the children of the top node.
0516: // todo: This assumes that we want have multiple top-level nodes. Which we should be able tohave.
0517: curs.toFirstContentToken();
0518: }
0519:
0520: TokenType tt = curs.toFirstContentToken();
0521: if (!tt.isNone() && !tt.isEnd()) {
0522: while (true) {
0523: if (index == idxChild) {
0524: return true;
0525: }
0526:
0527: tt = curs.currentTokenType();
0528: if (tt.isText()) {
0529: curs.toNextToken();
0530: } else if (tt.isStart()) {
0531: // Need to do this we want to be pointing at the text if that after the end token.
0532: curs.toEndToken();
0533: curs.toNextToken();
0534: } else if (tt.isComment() || tt.isProcinst()) {
0535: continue;
0536: } else {
0537: break;
0538: }
0539:
0540: idxChild++;
0541: }
0542: } else if (fFirstChild && index == 0) {
0543: // Drill into where first child would be.
0544: // curs.toFirstContentToken();
0545: return true;
0546: }
0547:
0548: return false;
0549: }
0550:
0551: /**
0552: *
0553: * @return
0554: */
0555: XmlCursor.TokenType tokenType() {
0556: XmlCursor.TokenType result;
0557:
0558: XmlCursor curs = newCursor();
0559:
0560: if (curs.isStartdoc()) {
0561: curs.toFirstContentToken();
0562: }
0563:
0564: result = curs.currentTokenType();
0565:
0566: curs.dispose();
0567:
0568: return result;
0569: }
0570:
0571: /**
0572: *
0573: * @param srcCurs
0574: * @param destCurs
0575: * @param fDontMoveIfSame
0576: * @return
0577: */
0578: private boolean moveSrcToDest(XmlCursor srcCurs,
0579: XmlCursor destCurs, boolean fDontMoveIfSame) {
0580: boolean fMovedSomething = true;
0581: TokenType tt;
0582: do {
0583: if (fDontMoveIfSame && srcCurs.isInSameDocument(destCurs)
0584: && (srcCurs.comparePosition(destCurs) == 0)) {
0585: // If the source and destination are pointing at the same place then there's nothing to move.
0586: fMovedSomething = false;
0587: break;
0588: }
0589:
0590: // todo ***TLL*** Use replaceContents (when added) and eliminate children removes (see above todo).
0591: if (destCurs.currentTokenType().isStartdoc()) {
0592: destCurs.toNextToken();
0593: }
0594:
0595: // todo ***TLL*** Can Eric support notion of copy instead of me copying then moving???
0596: XmlCursor copyCurs = copy(srcCurs);
0597:
0598: copyCurs.moveXml(destCurs);
0599:
0600: copyCurs.dispose();
0601:
0602: tt = srcCurs.currentTokenType();
0603: } while (!tt.isStart() && !tt.isEnd() && !tt.isEnddoc());
0604:
0605: return fMovedSomething;
0606: }
0607:
0608: /**
0609: *
0610: * @param cursToCopy
0611: * @return
0612: */
0613: private XmlCursor copy(XmlCursor cursToCopy) {
0614: XmlObject xo = XmlObject.Factory.newInstance();
0615:
0616: XmlCursor copyCurs = null;
0617:
0618: if (cursToCopy.currentTokenType().isText()) {
0619: try {
0620: // Try just as a textnode, to do that we need to wrap the text in a special fragment tag
0621: // that is not visible from the XmlCursor.
0622: copyCurs = XmlObject.Factory.parse(
0623: "<x:fragment xmlns:x=\"http://www.openuri.org/fragment\">"
0624: + cursToCopy.getChars()
0625: + "</x:fragment>").newCursor();
0626: if (!cursToCopy.toNextSibling()) {
0627: if (cursToCopy.currentTokenType().isText()) {
0628: cursToCopy.toNextToken(); // It's not an element it's text so skip it.
0629: }
0630: }
0631: } catch (Exception ex) {
0632: throw ScriptRuntime.typeError(ex.getMessage());
0633: }
0634: } else {
0635: copyCurs = xo.newCursor();
0636: copyCurs.toFirstContentToken();
0637: if (cursToCopy.currentTokenType() == XmlCursor.TokenType.STARTDOC) {
0638: cursToCopy.toNextToken();
0639: }
0640:
0641: cursToCopy.copyXml(copyCurs);
0642: if (!cursToCopy.toNextSibling()) // If element skip element.
0643: {
0644: if (cursToCopy.currentTokenType().isText()) {
0645: cursToCopy.toNextToken(); // It's not an element it's text so skip it.
0646: }
0647: }
0648:
0649: }
0650:
0651: copyCurs.toStartDoc();
0652: copyCurs.toFirstContentToken();
0653:
0654: return copyCurs;
0655: }
0656:
0657: private static final int APPEND_CHILD = 1;
0658: private static final int PREPEND_CHILD = 2;
0659:
0660: /**
0661: *
0662: * @param curs
0663: * @param xmlToInsert
0664: */
0665: private void insertChild(XmlCursor curs, Object xmlToInsert) {
0666: if (xmlToInsert == null || xmlToInsert instanceof Undefined) {
0667: // Do nothing
0668: } else if (xmlToInsert instanceof XmlCursor) {
0669: moveSrcToDest((XmlCursor) xmlToInsert, curs, true);
0670: } else if (xmlToInsert instanceof XML) {
0671: XML xmlValue = (XML) xmlToInsert;
0672:
0673: // If it's an attribute, then change to text node
0674: if (xmlValue.tokenType() == XmlCursor.TokenType.ATTR) {
0675: insertChild(curs, xmlValue.toString());
0676: } else {
0677: XmlCursor cursToInsert = ((XML) xmlToInsert)
0678: .newCursor();
0679:
0680: moveSrcToDest(cursToInsert, curs, true);
0681:
0682: cursToInsert.dispose();
0683: }
0684: } else if (xmlToInsert instanceof XMLList) {
0685: XMLList list = (XMLList) xmlToInsert;
0686:
0687: for (int i = 0; i < list.length(); i++) {
0688: insertChild(curs, list.item(i));
0689: }
0690: } else {
0691: // Convert to string and make XML out of it
0692: String xmlStr = ScriptRuntime.toString(xmlToInsert);
0693: XmlObject xo = XmlObject.Factory.newInstance(); // Create an empty document.
0694:
0695: XmlCursor sourceCurs = xo.newCursor();
0696: sourceCurs.toNextToken();
0697:
0698: // To hold the text.
0699: sourceCurs.insertChars(xmlStr);
0700:
0701: sourceCurs.toPrevToken();
0702:
0703: // Call us again with the cursor.
0704: moveSrcToDest(sourceCurs, curs, true);
0705: }
0706: }
0707:
0708: /**
0709: *
0710: * @param childToMatch
0711: * @param xmlToInsert
0712: * @param addToType
0713: */
0714: private void insertChild(XML childToMatch, Object xmlToInsert,
0715: int addToType) {
0716: XmlCursor curs = newCursor();
0717: TokenType tt = curs.currentTokenType();
0718: XmlCursor xmlChildCursor = childToMatch.newCursor();
0719:
0720: if (tt.isStartdoc()) {
0721: tt = curs.toFirstContentToken();
0722: }
0723:
0724: if (tt.isContainer()) {
0725: tt = curs.toNextToken();
0726:
0727: while (!tt.isEnd()) {
0728: if (tt.isStart()) {
0729: // See if this child is the same as the one thep passed in
0730: if (curs.comparePosition(xmlChildCursor) == 0) {
0731: // Found it
0732: if (addToType == APPEND_CHILD) {
0733: // Move the cursor to just past the end of this element
0734: curs.toEndToken();
0735: curs.toNextToken();
0736: }
0737:
0738: insertChild(curs, xmlToInsert);
0739: break;
0740: }
0741: }
0742:
0743: // Skip over child elements
0744: if (tt.isStart()) {
0745: tt = curs.toEndToken();
0746: }
0747:
0748: tt = curs.toNextToken();
0749: }
0750:
0751: }
0752:
0753: xmlChildCursor.dispose();
0754: curs.dispose();
0755: }
0756:
0757: /**
0758: *
0759: * @param curs
0760: */
0761: protected void removeToken(XmlCursor curs) {
0762: XmlObject xo = XmlObject.Factory.newInstance();
0763:
0764: // Don't delete anything move to another document so it gets orphaned nicely.
0765: XmlCursor tmpCurs = xo.newCursor();
0766: tmpCurs.toFirstContentToken();
0767:
0768: curs.moveXml(tmpCurs);
0769:
0770: tmpCurs.dispose();
0771: }
0772:
0773: /**
0774: *
0775: * @param index
0776: */
0777: protected void removeChild(long index) {
0778: XmlCursor curs = newCursor();
0779:
0780: if (moveToChild(curs, index, false, false)) {
0781: removeToken(curs);
0782: }
0783:
0784: curs.dispose();
0785: }
0786:
0787: /**
0788: *
0789: * @param name
0790: * @return
0791: */
0792: protected static javax.xml.namespace.QName computeQName(Object name) {
0793: if (name instanceof String) {
0794: String ns = null;
0795: String localName = null;
0796:
0797: String fullName = (String) name;
0798: localName = fullName;
0799: if (fullName.startsWith("\"")) {
0800: int idx = fullName.indexOf(":");
0801: if (idx != -1) {
0802: ns = fullName.substring(1, idx - 1); // Don't include the "" around the namespace
0803: localName = fullName.substring(idx + 1);
0804: }
0805: }
0806:
0807: if (ns == null) {
0808: return new javax.xml.namespace.QName(localName);
0809: } else {
0810: return new javax.xml.namespace.QName(ns, localName);
0811: }
0812: }
0813:
0814: return null;
0815: }
0816:
0817: /**
0818: *
0819: * @param destCurs
0820: * @param newValue
0821: */
0822: private void replace(XmlCursor destCurs, XML newValue) {
0823: if (destCurs.isStartdoc()) {
0824: // Can't overwrite a whole document (user really wants to overwrite the contents of).
0825: destCurs.toFirstContentToken();
0826: }
0827:
0828: // Orphan the token -- don't delete it outright on the XmlCursor.
0829: removeToken(destCurs);
0830:
0831: XmlCursor srcCurs = newValue.newCursor();
0832: if (srcCurs.currentTokenType().isStartdoc()) {
0833: // Cann't append a whole document (user really wants to append the contents of).
0834: srcCurs.toFirstContentToken();
0835: }
0836:
0837: moveSrcToDest(srcCurs, destCurs, false);
0838:
0839: // Re-link a new annotation to this cursor -- we just deleted the previous annotation on entrance to replace.
0840: if (!destCurs.toPrevSibling()) {
0841: destCurs.toPrevToken();
0842: }
0843: destCurs.setBookmark(new XScriptAnnotation(destCurs));
0844:
0845: // 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.
0846: destCurs.toEndToken();
0847: destCurs.toNextToken();
0848:
0849: srcCurs.dispose();
0850: }
0851:
0852: /**
0853: *
0854: * @param currXMLNode
0855: * @param xmlValue
0856: * @return
0857: */
0858: private boolean doPut(XMLName name, XML currXMLNode,
0859: XMLObjectImpl xmlValue) {
0860: boolean result = false;
0861: XmlCursor curs = currXMLNode.newCursor();
0862:
0863: try {
0864: // Replace the node with this new xml value.
0865: XML xml;
0866:
0867: int toAssignLen = xmlValue.length();
0868:
0869: for (int i = 0; i < toAssignLen; i++) {
0870: if (xmlValue instanceof XMLList) {
0871: xml = ((XMLList) xmlValue).item(i);
0872: } else {
0873: xml = (XML) xmlValue;
0874: }
0875:
0876: // If it's an attribute or text node, make text node.
0877: XmlCursor.TokenType tt = xml.tokenType();
0878: if (tt == XmlCursor.TokenType.ATTR
0879: || tt == XmlCursor.TokenType.TEXT) {
0880: xml = makeXmlFromString(lib, name, xml.toString());
0881: }
0882:
0883: if (i == 0) {
0884: // 1st assignment is replaceChild all others are appendChild
0885: replace(curs, xml);
0886: } else {
0887: insertChild(curs, xml);
0888: }
0889: }
0890:
0891: // We're done we've blown away the node because the rvalue was XML...
0892: result = true;
0893: } catch (Exception ex) {
0894: ex.printStackTrace();
0895: throw ScriptRuntime.typeError(ex.getMessage());
0896: } finally {
0897: curs.dispose();
0898: }
0899:
0900: return result;
0901: }
0902:
0903: /**
0904: * Make a text node element with this element name and text value.
0905: *
0906: * @param name
0907: * @param value
0908: * @return
0909: */
0910: private XML makeXmlFromString(XMLLibImpl lib, XMLName name,
0911: String value) {
0912: XML result;
0913:
0914: javax.xml.namespace.QName qname;
0915:
0916: try {
0917: qname = new javax.xml.namespace.QName(name.uri(), name
0918: .localName());
0919: } catch (Exception e) {
0920: throw ScriptRuntime.typeError(e.getMessage());
0921: }
0922:
0923: result = createTextElement(lib, qname, value);
0924:
0925: return result;
0926: }
0927:
0928: /**
0929: *
0930: * @param name
0931: * @return
0932: */
0933: private XMLList matchAttributes(XMLName xmlName) {
0934: XMLList result = new XMLList(lib);
0935: XmlCursor curs = newCursor();
0936:
0937: if (curs.currentTokenType().isStartdoc()) {
0938: curs.toFirstContentToken();
0939: }
0940:
0941: if (curs.isStart()) {
0942: if (curs.toFirstAttribute()) {
0943: do {
0944: if (qnameMatches(xmlName, curs.getName())) {
0945: result.addToList(createAttributeObject(curs));
0946: }
0947: } while (curs.toNextAttribute());
0948: }
0949: }
0950:
0951: curs.dispose();
0952:
0953: return result;
0954: }
0955:
0956: /**
0957: *
0958: * @param attrCurs
0959: * @return
0960: */
0961: private XML createAttributeObject(XmlCursor attrCurs) {
0962: XML result = null;
0963:
0964: if (attrCurs.currentTokenType().isAttr()) {
0965: result = createAttributeXML(lib, attrCurs);
0966: }
0967:
0968: return result;
0969: }
0970:
0971: //
0972: //
0973: // methods overriding ScriptableObject
0974: //
0975: //
0976:
0977: public String getClassName() {
0978: return "XML";
0979: }
0980:
0981: //
0982: //
0983: // methods overriding IdScriptableObject
0984: //
0985: //
0986:
0987: /**
0988: * XML[0] should return this, all other indexes are Undefined
0989: *
0990: * @param index
0991: * @param start
0992: * @return
0993: */
0994: public Object get(int index, Scriptable start) {
0995: //Log("get index: " + index);
0996:
0997: if (index == 0) {
0998: return this ;
0999: } else {
1000: return Scriptable.NOT_FOUND;
1001: }
1002: }
1003:
1004: /**
1005: * Does the named property exist
1006: *
1007: * @param name
1008: * @param start
1009: * @return
1010: */
1011: boolean hasXMLProperty(XMLName xmlName) {
1012: boolean result = false;
1013:
1014: if (prototypeFlag) {
1015: String name = xmlName.localName();
1016:
1017: if (getMethod(name) != NOT_FOUND) {
1018: result = true;
1019: }
1020: } else {
1021: // Has now should return true if the property would have results > 0 or
1022: // if it's a method name
1023: String name = xmlName.localName();
1024: if ((getPropertyList(xmlName).length() > 0)
1025: || (getMethod(name) != NOT_FOUND)) {
1026: result = true;
1027: }
1028: }
1029:
1030: return result;
1031: }
1032:
1033: /**
1034: *
1035: * @param index
1036: * @param start
1037: * @return
1038: */
1039: public boolean has(int index, Scriptable start) {
1040: return (index == 0);
1041: }
1042:
1043: /**
1044: *
1045: * @return
1046: */
1047: public Object[] getIds() {
1048: Object[] enumObjs;
1049:
1050: if (prototypeFlag) {
1051: enumObjs = new Object[0];
1052: } else {
1053: enumObjs = new Object[1];
1054:
1055: enumObjs[0] = new Integer(0);
1056: }
1057:
1058: return enumObjs;
1059: }
1060:
1061: /**
1062: *
1063: * @return
1064: */
1065: public Object[] getIdsForDebug() {
1066: return getIds();
1067: }
1068:
1069: /**
1070: *
1071: * @param name
1072: * @param start
1073: * @return
1074: */
1075: Object getXMLProperty(XMLName xmlName) {
1076: Object result = NOT_FOUND;
1077:
1078: if (prototypeFlag) {
1079: String name = xmlName.localName();
1080:
1081: result = getMethod(name);
1082: } else {
1083: result = getPropertyList(xmlName);
1084: }
1085:
1086: return result;
1087: }
1088:
1089: /**
1090: *
1091: * @param name
1092: * @param start
1093: * @param value
1094: */
1095: void putXMLProperty(XMLName xmlName, Object value) {
1096: //Log("put property: " + name + " value: " + value.getClass());
1097:
1098: if (prototypeFlag) {
1099: } else {
1100: // Special-case checks for undefined and null
1101: if (value == null) {
1102: value = "null";
1103: } else if (value instanceof Undefined) {
1104: value = "undefined";
1105: }
1106:
1107: // Get the named property
1108: if (xmlName.isAttributeName()) {
1109: setAttribute(xmlName, value);
1110: } else if (xmlName.uri() == null
1111: && xmlName.localName().equals("*")) {
1112: setChildren(value);
1113: } else {
1114: // Convert text into XML if needed.
1115: XMLObjectImpl xmlValue = null;
1116:
1117: if (value instanceof XMLObjectImpl) {
1118: xmlValue = (XMLObjectImpl) value;
1119:
1120: // Check for attribute type and convert to textNode
1121: if (xmlValue instanceof XML) {
1122: if (((XML) xmlValue).tokenType() == XmlCursor.TokenType.ATTR) {
1123: xmlValue = makeXmlFromString(lib, xmlName,
1124: xmlValue.toString());
1125: }
1126: }
1127:
1128: if (xmlValue instanceof XMLList) {
1129: for (int i = 0; i < xmlValue.length(); i++) {
1130: XML xml = ((XMLList) xmlValue).item(i);
1131:
1132: if (xml.tokenType() == XmlCursor.TokenType.ATTR) {
1133: ((XMLList) xmlValue).replace(i,
1134: makeXmlFromString(lib, xmlName,
1135: xml.toString()));
1136: }
1137: }
1138: }
1139: } else {
1140: xmlValue = makeXmlFromString(lib, xmlName,
1141: ScriptRuntime.toString(value));
1142: }
1143:
1144: XMLList matches = getPropertyList(xmlName);
1145:
1146: if (matches.length() == 0) {
1147: appendChild(xmlValue);
1148: } else {
1149: // Remove all other matches
1150: for (int i = 1; i < matches.length(); i++) {
1151: removeChild(matches.item(i).childIndex());
1152: }
1153:
1154: // Replace first match with new value.
1155: doPut(xmlName, matches.item(0), xmlValue);
1156: }
1157: }
1158: }
1159: }
1160:
1161: /**
1162: *
1163: * @param index
1164: * @param start
1165: * @param value
1166: */
1167: public void put(int index, Scriptable start, Object value) {
1168: // Spec says assignment to indexed XML object should return type error
1169: throw ScriptRuntime
1170: .typeError("Assignment to indexed XML is not allowed");
1171: }
1172:
1173: /**
1174: *
1175: * @param name
1176: */
1177: void deleteXMLProperty(XMLName name) {
1178: if (!name.isDescendants() && name.isAttributeName()) {
1179: XmlCursor curs = newCursor();
1180:
1181: // TODO: Cover the case *::name
1182: if (name.localName().equals("*")) {
1183: // Delete all attributes.
1184: if (curs.toFirstAttribute()) {
1185: while (curs.currentTokenType().isAttr()) {
1186: curs.removeXml();
1187: }
1188: }
1189: } else {
1190: // Delete an attribute.
1191: javax.xml.namespace.QName qname = new javax.xml.namespace.QName(
1192: name.uri(), name.localName());
1193: curs.removeAttribute(qname);
1194: }
1195:
1196: curs.dispose();
1197: } else {
1198: XMLList matches = getPropertyList(name);
1199:
1200: matches.remove();
1201: }
1202: }
1203:
1204: /**
1205: *
1206: * @param index
1207: */
1208: public void delete(int index) {
1209: if (index == 0) {
1210: remove();
1211: }
1212: }
1213:
1214: //
1215: //
1216: // package utility functions:
1217: //
1218: //
1219:
1220: protected XScriptAnnotation getAnnotation() {
1221: return _anno;
1222: }
1223:
1224: protected void changeNS(String oldURI, String newURI) {
1225: XmlCursor curs = newCursor();
1226: while (curs.toParent()) {
1227: /* Goto the top of the document */
1228: }
1229:
1230: TokenType tt = curs.currentTokenType();
1231: if (tt.isStartdoc()) {
1232: tt = curs.toFirstContentToken();
1233: }
1234:
1235: if (tt.isStart()) {
1236: do {
1237: if (tt.isStart() || tt.isAttr() || tt.isNamespace()) {
1238: javax.xml.namespace.QName currQName = curs
1239: .getName();
1240: if (oldURI.equals(currQName.getNamespaceURI())) {
1241: curs.setName(new javax.xml.namespace.QName(
1242: newURI, currQName.getLocalPart()));
1243: }
1244: }
1245:
1246: tt = curs.toNextToken();
1247: } while (!tt.isEnddoc() && !tt.isNone());
1248: }
1249:
1250: curs.dispose();
1251: }
1252:
1253: /**
1254: *
1255: */
1256: void remove() {
1257: XmlCursor childCurs = newCursor();
1258:
1259: if (childCurs.currentTokenType().isStartdoc()) {
1260: // Remove on the document removes all children.
1261: TokenType tt = childCurs.toFirstContentToken();
1262: while (!tt.isEnd() && !tt.isEnddoc()) {
1263: removeToken(childCurs);
1264: tt = childCurs.currentTokenType(); // Now see where we're pointing after the delete -- next token.
1265: }
1266: } else {
1267: removeToken(childCurs);
1268: }
1269:
1270: childCurs.dispose();
1271: }
1272:
1273: /**
1274: *
1275: * @param value
1276: */
1277: void replaceAll(XML value) {
1278: XmlCursor curs = newCursor();
1279:
1280: replace(curs, value);
1281: _anno = value._anno;
1282:
1283: curs.dispose();
1284: }
1285:
1286: /**
1287: *
1288: * @param attrName
1289: * @param value
1290: */
1291: void setAttribute(XMLName xmlName, Object value) {
1292: if (xmlName.uri() == null && xmlName.localName().equals("*")) {
1293: throw ScriptRuntime
1294: .typeError("@* assignment not supported.");
1295: }
1296:
1297: XmlCursor curs = newCursor();
1298:
1299: String strValue = ScriptRuntime.toString(value);
1300: if (curs.currentTokenType().isStartdoc()) {
1301: curs.toFirstContentToken();
1302: }
1303:
1304: javax.xml.namespace.QName qName;
1305:
1306: try {
1307: qName = new javax.xml.namespace.QName(xmlName.uri(),
1308: xmlName.localName());
1309: } catch (Exception e) {
1310: throw ScriptRuntime.typeError(e.getMessage());
1311: }
1312:
1313: if (!curs.setAttributeText(qName, strValue)) {
1314: if (curs.currentTokenType().isStart()) {
1315: // Can only add attributes inside of a start.
1316: curs.toNextToken();
1317: }
1318: curs.insertAttributeWithValue(qName, strValue);
1319: }
1320:
1321: curs.dispose();
1322: }
1323:
1324: /**
1325: *
1326: * @param namespace
1327: * @return
1328: */
1329: private XMLList allChildNodes(String namespace) {
1330: XMLList result = new XMLList(lib);
1331: XmlCursor curs = newCursor();
1332: TokenType tt = curs.currentTokenType();
1333: javax.xml.namespace.QName targetProperty = new javax.xml.namespace.QName(
1334: namespace, "*");
1335:
1336: if (tt.isStartdoc()) {
1337: tt = curs.toFirstContentToken();
1338: }
1339:
1340: if (tt.isContainer()) {
1341: tt = curs.toFirstContentToken();
1342:
1343: while (!tt.isEnd()) {
1344: if (!tt.isStart()) {
1345: // Not an element
1346: result.addToList(findAnnotation(curs));
1347:
1348: // Reset target property to null in this case
1349: targetProperty = null;
1350: } else {
1351: // Match namespace as well if specified
1352: if (namespace == null
1353: || namespace.length() == 0
1354: || namespace.equals("*")
1355: || curs.getName().getNamespaceURI().equals(
1356: namespace)) {
1357: // Add it to the list
1358: result.addToList(findAnnotation(curs));
1359:
1360: // Set target property if target name is "*",
1361: // Otherwise if target property does not match current, then
1362: // set to null
1363: if (targetProperty != null) {
1364: if (targetProperty.getLocalPart().equals(
1365: "*")) {
1366: targetProperty = curs.getName();
1367: } else if (!targetProperty.getLocalPart()
1368: .equals(
1369: curs.getName()
1370: .getLocalPart())) {
1371: // Not a match, unset target property
1372: targetProperty = null;
1373: }
1374: }
1375: }
1376: }
1377:
1378: // Skip over child elements
1379: if (tt.isStart()) {
1380: tt = curs.toEndToken();
1381: }
1382:
1383: tt = curs.toNextToken();
1384: }
1385: }
1386:
1387: curs.dispose();
1388:
1389: // Set the targets for this XMLList.
1390: result.setTargets(this , targetProperty);
1391:
1392: return result;
1393: }
1394:
1395: /**
1396: *
1397: * @return
1398: */
1399: private XMLList matchDescendantAttributes(XMLName xmlName) {
1400: XMLList result = new XMLList(lib);
1401: XmlCursor curs = newCursor();
1402: TokenType tt = curs.currentTokenType();
1403:
1404: // Set the targets for this XMLList.
1405: result.setTargets(this , null);
1406:
1407: if (tt.isStartdoc()) {
1408: tt = curs.toFirstContentToken();
1409: }
1410:
1411: if (tt.isContainer()) {
1412: int nestLevel = 1;
1413:
1414: while (nestLevel > 0) {
1415: tt = curs.toNextToken();
1416:
1417: // Only try to match names for attributes
1418: if (tt.isAttr()) {
1419: if (qnameMatches(xmlName, curs.getName())) {
1420: result.addToList(findAnnotation(curs));
1421: }
1422: }
1423:
1424: if (tt.isStart()) {
1425: nestLevel++;
1426: } else if (tt.isEnd()) {
1427: nestLevel--;
1428: } else if (tt.isEnddoc()) {
1429: // Shouldn't get here, but just in case.
1430: break;
1431: }
1432: }
1433: }
1434:
1435: curs.dispose();
1436:
1437: return result;
1438: }
1439:
1440: /**
1441: *
1442: * @return
1443: */
1444: private XMLList matchDescendantChildren(XMLName xmlName) {
1445: XMLList result = new XMLList(lib);
1446: XmlCursor curs = newCursor();
1447: TokenType tt = curs.currentTokenType();
1448:
1449: // Set the targets for this XMLList.
1450: result.setTargets(this , null);
1451:
1452: if (tt.isStartdoc()) {
1453: tt = curs.toFirstContentToken();
1454: }
1455:
1456: if (tt.isContainer()) {
1457: int nestLevel = 1;
1458:
1459: while (nestLevel > 0) {
1460: tt = curs.toNextToken();
1461:
1462: if (!tt.isAttr() && !tt.isEnd() && !tt.isEnddoc()) {
1463: // Only try to match names for elements or processing instructions.
1464: if (!tt.isStart() && !tt.isProcinst()) {
1465: // Not an element or procinst, only add if qname is all
1466: if (xmlName.localName().equals("*")) {
1467: result.addToList(findAnnotation(curs));
1468: }
1469: } else {
1470: if (qnameMatches(xmlName, curs.getName())) {
1471: result.addToList(findAnnotation(curs));
1472: }
1473: }
1474: }
1475:
1476: if (tt.isStart()) {
1477: nestLevel++;
1478: } else if (tt.isEnd()) {
1479: nestLevel--;
1480: } else if (tt.isEnddoc()) {
1481: // Shouldn't get here, but just in case.
1482: break;
1483: }
1484: }
1485: }
1486:
1487: curs.dispose();
1488:
1489: return result;
1490: }
1491:
1492: /**
1493: *
1494: * @param tokenType
1495: * @return
1496: */
1497: private XMLList matchChildren(XmlCursor.TokenType tokenType) {
1498: return matchChildren(tokenType, XMLName.formStar());
1499: }
1500:
1501: /**
1502: *
1503: * @return
1504: */
1505: private XMLList matchChildren(XmlCursor.TokenType tokenType,
1506: XMLName name) {
1507: XMLList result = new XMLList(lib);
1508: XmlCursor curs = newCursor();
1509: TokenType tt = curs.currentTokenType();
1510: javax.xml.namespace.QName qname = new javax.xml.namespace.QName(
1511: name.uri(), name.localName());
1512: javax.xml.namespace.QName targetProperty = qname;
1513:
1514: if (tt.isStartdoc()) {
1515: tt = curs.toFirstContentToken();
1516: }
1517:
1518: if (tt.isContainer()) {
1519: tt = curs.toFirstContentToken();
1520:
1521: while (!tt.isEnd()) {
1522: if (tt == tokenType) {
1523: // Only try to match names for elements or processing instructions.
1524: if (!tt.isStart() && !tt.isProcinst()) {
1525: // Not an element or no name specified.
1526: result.addToList(findAnnotation(curs));
1527:
1528: // Reset target property to null in this case
1529: targetProperty = null;
1530: } else {
1531: // Match names as well
1532: if (qnameMatches(name, curs.getName())) {
1533: // Add it to the list
1534: result.addToList(findAnnotation(curs));
1535:
1536: // Set target property if target name is "*",
1537: // Otherwise if target property does not match current, then
1538: // set to null
1539: if (targetProperty != null) {
1540: if (targetProperty.getLocalPart()
1541: .equals("*")) {
1542: targetProperty = curs.getName();
1543: } else if (!targetProperty
1544: .getLocalPart()
1545: .equals(
1546: curs.getName()
1547: .getLocalPart())) {
1548: // Not a match, unset target property
1549: targetProperty = null;
1550: }
1551: }
1552: }
1553: }
1554: }
1555:
1556: // Skip over child elements
1557: if (tt.isStart()) {
1558: tt = curs.toEndToken();
1559: }
1560:
1561: tt = curs.toNextToken();
1562: }
1563: }
1564:
1565: curs.dispose();
1566:
1567: if (tokenType == XmlCursor.TokenType.START) {
1568: // Set the targets for this XMLList.
1569: result.setTargets(this , targetProperty);
1570: }
1571:
1572: return result;
1573:
1574: }
1575:
1576: /**
1577: *
1578: * @param template
1579: * @param match
1580: * @return
1581: */
1582: private boolean qnameMatches(XMLName template,
1583: javax.xml.namespace.QName match) {
1584: boolean matches = false;
1585:
1586: if (template.uri() == null
1587: || template.uri().equals(match.getNamespaceURI())) {
1588: // URI OK, test name
1589: if (template.localName().equals("*")
1590: || template.localName()
1591: .equals(match.getLocalPart())) {
1592: matches = true;
1593: }
1594: }
1595:
1596: return matches;
1597: }
1598:
1599: //
1600: //
1601: // Methods from section 12.4.4 in the spec
1602: //
1603: //
1604:
1605: /**
1606: * The addNamespace method adds a namespace declaration to the in scope
1607: * namespaces for this XML object and returns this XML object.
1608: *
1609: * @param toAdd
1610: */
1611: XML addNamespace(Namespace ns) {
1612: // When a namespace is used it will be added automatically
1613: // to the inScopeNamespaces set. There is no need to add
1614: // Namespaces with undefined prefixes.
1615: String nsPrefix = ns.prefix();
1616: if (nsPrefix == null)
1617: return this ;
1618:
1619: XmlCursor cursor = newCursor();
1620:
1621: try {
1622: if (!cursor.isContainer())
1623: return this ;
1624:
1625: javax.xml.namespace.QName qname = cursor.getName();
1626: // Don't add a default namespace declarations to containers
1627: // with QNames in no namespace.
1628: if (qname.getNamespaceURI().equals("")
1629: && nsPrefix.equals(""))
1630: return this ;
1631:
1632: // Get all declared namespaces that are in scope
1633: Map prefixToURI = NamespaceHelper.getAllNamespaces(lib,
1634: cursor);
1635:
1636: String uri = (String) prefixToURI.get(nsPrefix);
1637: if (uri != null) {
1638: // Check if the Namespace is not already in scope
1639: if (uri.equals(ns.uri()))
1640: return this ;
1641:
1642: cursor.push();
1643:
1644: // Let's see if we have to delete a namespace declaration
1645: while (cursor.toNextToken().isAnyAttr()) {
1646: if (cursor.isNamespace()) {
1647: qname = cursor.getName();
1648: String prefix = qname.getLocalPart();
1649: if (prefix.equals(nsPrefix)) {
1650: // Delete the current Namespace declaration
1651: cursor.removeXml();
1652: break;
1653: }
1654: }
1655: }
1656:
1657: cursor.pop();
1658: }
1659:
1660: cursor.toNextToken();
1661: cursor.insertNamespace(nsPrefix, ns.uri());
1662: } finally {
1663: cursor.dispose();
1664: }
1665:
1666: return this ;
1667: }
1668:
1669: /**
1670: *
1671: * @param xml
1672: * @return
1673: */
1674: XML appendChild(Object xml) {
1675: XmlCursor curs = newCursor();
1676:
1677: if (curs.isStartdoc()) {
1678: curs.toFirstContentToken();
1679: }
1680:
1681: // Move the cursor to the end of this element
1682: if (curs.isStart()) {
1683: curs.toEndToken();
1684: }
1685:
1686: insertChild(curs, xml);
1687:
1688: curs.dispose();
1689:
1690: return this ;
1691: }
1692:
1693: /**
1694: *
1695: * @param name
1696: * @return
1697: */
1698: XMLList attribute(XMLName xmlName) {
1699: return matchAttributes(xmlName);
1700: }
1701:
1702: /**
1703: *
1704: * @return
1705: */
1706: XMLList attributes() {
1707: XMLName xmlName = XMLName.formStar();
1708: return matchAttributes(xmlName);
1709: }
1710:
1711: XMLList child(long index) {
1712: XMLList result = new XMLList(lib);
1713: result.setTargets(this , null);
1714: result.addToList(getXmlChild(index));
1715: return result;
1716: }
1717:
1718: XMLList child(XMLName xmlName) {
1719: if (xmlName == null)
1720: return new XMLList(lib);
1721:
1722: XMLList result;
1723: if (xmlName.localName().equals("*")) {
1724: result = allChildNodes(xmlName.uri());
1725: } else {
1726: result = matchChildren(XmlCursor.TokenType.START, xmlName);
1727: }
1728:
1729: return result;
1730: }
1731:
1732: /**
1733: *
1734: * @param index
1735: * @return
1736: */
1737: XML getXmlChild(long index) {
1738: XML result = null;
1739: XmlCursor curs = newCursor();
1740:
1741: if (moveToChild(curs, index, false, true)) {
1742: result = createXML(lib, curs);
1743: }
1744:
1745: curs.dispose();
1746:
1747: return result;
1748: }
1749:
1750: /**
1751: *
1752: * @return
1753: */
1754: int childIndex() {
1755: int index = 0;
1756:
1757: XmlCursor curs = newCursor();
1758:
1759: TokenType tt = curs.currentTokenType();
1760: while (true) {
1761: if (tt.isText()) {
1762: index++;
1763: if (!curs.toPrevSibling()) {
1764: break;
1765: }
1766: } else if (tt.isStart()) {
1767: tt = curs.toPrevToken();
1768: if (tt.isEnd()) {
1769: curs.toNextToken();
1770: if (!curs.toPrevSibling()) {
1771: break;
1772: }
1773:
1774: index++;
1775: } else {
1776: // Hit the parent start tag so get out we're down counting children.
1777: break;
1778: }
1779: } else if (tt.isComment() || tt.isProcinst()) {
1780: curs.toPrevToken();
1781: } else {
1782: break;
1783: }
1784:
1785: tt = curs.currentTokenType();
1786: }
1787:
1788: index = curs.currentTokenType().isStartdoc() ? -1 : index;
1789:
1790: curs.dispose();
1791:
1792: return index;
1793: }
1794:
1795: /**
1796: *
1797: * @return
1798: */
1799: XMLList children() {
1800: return allChildNodes(null);
1801: }
1802:
1803: /**
1804: *
1805: * @return
1806: */
1807: XMLList comments() {
1808: return matchChildren(XmlCursor.TokenType.COMMENT);
1809: }
1810:
1811: /**
1812: *
1813: * @param xml
1814: * @return
1815: */
1816: boolean contains(Object xml) {
1817: boolean result = false;
1818:
1819: if (xml instanceof XML) {
1820: result = equivalentXml(xml);
1821: }
1822:
1823: return result;
1824: }
1825:
1826: /**
1827: *
1828: * @return
1829: */
1830: Object copy() {
1831: XmlCursor srcCurs = newCursor();
1832:
1833: if (srcCurs.isStartdoc()) {
1834: srcCurs.toFirstContentToken();
1835: }
1836:
1837: XML xml = createEmptyXML(lib);
1838:
1839: XmlCursor destCurs = xml.newCursor();
1840: destCurs.toFirstContentToken();
1841:
1842: srcCurs.copyXml(destCurs);
1843:
1844: destCurs.dispose();
1845: srcCurs.dispose();
1846:
1847: return xml;
1848: }
1849:
1850: /**
1851: *
1852: * @param name
1853: * @return
1854: */
1855: XMLList descendants(XMLName xmlName) {
1856: XMLList result;
1857: if (xmlName.isAttributeName()) {
1858: result = matchDescendantAttributes(xmlName);
1859: } else {
1860: result = matchDescendantChildren(xmlName);
1861: }
1862:
1863: return result;
1864: }
1865:
1866: /**
1867: * The inScopeNamespaces method returns an Array of Namespace objects
1868: * representing the namespaces in scope for this XML object in the
1869: * context of its parent.
1870: *
1871: * @return Array of all Namespaces in scope for this XML Object.
1872: */
1873: Object[] inScopeNamespaces() {
1874: XmlCursor cursor = newCursor();
1875: Object[] namespaces = NamespaceHelper.inScopeNamespaces(lib,
1876: cursor);
1877: cursor.dispose();
1878: return namespaces;
1879: }
1880:
1881: /**
1882: *
1883: * @param child
1884: * @param xml
1885: */
1886: XML insertChildAfter(Object child, Object xml) {
1887: if (child == null) {
1888: // Spec says inserting after nothing is the same as prepending
1889: prependChild(xml);
1890: } else if (child instanceof XML) {
1891: insertChild((XML) child, xml, APPEND_CHILD);
1892: }
1893:
1894: return this ;
1895: }
1896:
1897: /**
1898: *
1899: * @param child
1900: * @param xml
1901: */
1902: XML insertChildBefore(Object child, Object xml) {
1903: if (child == null) {
1904: // Spec says inserting before nothing is the same as appending
1905: appendChild(xml);
1906: } else if (child instanceof XML) {
1907: insertChild((XML) child, xml, PREPEND_CHILD);
1908: }
1909:
1910: return this ;
1911: }
1912:
1913: /**
1914: *
1915: * @return
1916: */
1917: boolean hasOwnProperty(XMLName xmlName) {
1918: boolean hasProperty = false;
1919:
1920: if (prototypeFlag) {
1921: String property = xmlName.localName();
1922: hasProperty = (0 != findPrototypeId(property));
1923: } else {
1924: hasProperty = (getPropertyList(xmlName).length() > 0);
1925: }
1926:
1927: return hasProperty;
1928: }
1929:
1930: /**
1931: *
1932: * @return
1933: */
1934: boolean hasComplexContent() {
1935: return !hasSimpleContent();
1936: }
1937:
1938: /**
1939: *
1940: * @return
1941: */
1942: boolean hasSimpleContent() {
1943: boolean simpleContent = false;
1944:
1945: XmlCursor curs = newCursor();
1946:
1947: if (curs.isAttr() || curs.isText()) {
1948: return true;
1949: }
1950:
1951: if (curs.isStartdoc()) {
1952: curs.toFirstContentToken();
1953: }
1954:
1955: simpleContent = !(curs.toFirstChild());
1956:
1957: curs.dispose();
1958:
1959: return simpleContent;
1960: }
1961:
1962: /**
1963: * Length of an XML object is always 1, it's a list of XML objects of size 1.
1964: *
1965: * @return
1966: */
1967: int length() {
1968: return 1;
1969: }
1970:
1971: /**
1972: *
1973: * @return
1974: */
1975: String localName() {
1976: XmlCursor cursor = newCursor();
1977: if (cursor.isStartdoc())
1978: cursor.toFirstContentToken();
1979:
1980: String name = null;
1981:
1982: if (cursor.isStart() || cursor.isAttr() || cursor.isProcinst()) {
1983: javax.xml.namespace.QName qname = cursor.getName();
1984: name = qname.getLocalPart();
1985: }
1986: cursor.dispose();
1987:
1988: return name;
1989: }
1990:
1991: /**
1992: * The name method returns the qualified name associated with this XML object.
1993: *
1994: * @return The qualified name associated with this XML object.
1995: */
1996: QName name() {
1997: XmlCursor cursor = newCursor();
1998: if (cursor.isStartdoc())
1999: cursor.toFirstContentToken();
2000:
2001: QName name = null;
2002:
2003: if (cursor.isStart() || cursor.isAttr() || cursor.isProcinst()) {
2004: javax.xml.namespace.QName qname = cursor.getName();
2005: if (cursor.isProcinst()) {
2006: name = new QName(lib, "", qname.getLocalPart(), "");
2007: } else {
2008: String uri = qname.getNamespaceURI();
2009: String prefix = qname.getPrefix();
2010: name = new QName(lib, uri, qname.getLocalPart(), prefix);
2011: }
2012: }
2013:
2014: cursor.dispose();
2015:
2016: return name;
2017: }
2018:
2019: /**
2020: *
2021: * @param prefix
2022: * @return
2023: */
2024: Object namespace(String prefix) {
2025: XmlCursor cursor = newCursor();
2026: if (cursor.isStartdoc()) {
2027: cursor.toFirstContentToken();
2028: }
2029:
2030: Object result = null;
2031:
2032: if (prefix == null) {
2033: if (cursor.isStart() || cursor.isAttr()) {
2034: Object[] inScopeNS = NamespaceHelper.inScopeNamespaces(
2035: lib, cursor);
2036: // XXX Is it reaaly necessary to create the second cursor?
2037: XmlCursor cursor2 = newCursor();
2038: if (cursor2.isStartdoc())
2039: cursor2.toFirstContentToken();
2040:
2041: result = NamespaceHelper.getNamespace(lib, cursor2,
2042: inScopeNS);
2043:
2044: cursor2.dispose();
2045: }
2046: } else {
2047: Map prefixToURI = NamespaceHelper.getAllNamespaces(lib,
2048: cursor);
2049: String uri = (String) prefixToURI.get(prefix);
2050: result = (uri == null) ? Undefined.instance
2051: : new Namespace(lib, prefix, uri);
2052: }
2053:
2054: cursor.dispose();
2055:
2056: return result;
2057: }
2058:
2059: /**
2060: *
2061: * @return
2062: */
2063: Object[] namespaceDeclarations() {
2064: XmlCursor cursor = newCursor();
2065: Object[] namespaces = NamespaceHelper.namespaceDeclarations(
2066: lib, cursor);
2067: cursor.dispose();
2068: return namespaces;
2069: }
2070:
2071: /**
2072: *
2073: * @return
2074: */
2075: Object nodeKind() {
2076: String result;
2077: XmlCursor.TokenType tt = tokenType();
2078:
2079: if (tt == XmlCursor.TokenType.ATTR) {
2080: result = "attribute";
2081: } else if (tt == XmlCursor.TokenType.TEXT) {
2082: result = "text";
2083: } else if (tt == XmlCursor.TokenType.COMMENT) {
2084: result = "comment";
2085: } else if (tt == XmlCursor.TokenType.PROCINST) {
2086: result = "processing-instruction";
2087: } else if (tt == XmlCursor.TokenType.START) {
2088: result = "element";
2089: } else {
2090: // A non-existant node has the nodeKind() of text
2091: result = "text";
2092: }
2093:
2094: return result;
2095: }
2096:
2097: /**
2098: *
2099: */
2100: void normalize() {
2101: XmlCursor curs = newCursor();
2102: TokenType tt = curs.currentTokenType();
2103:
2104: // Walk through the tokens removing empty text nodes and merging adjacent text nodes.
2105: if (tt.isStartdoc()) {
2106: tt = curs.toFirstContentToken();
2107: }
2108:
2109: if (tt.isContainer()) {
2110: int nestLevel = 1;
2111: String previousText = null;
2112:
2113: while (nestLevel > 0) {
2114: tt = curs.toNextToken();
2115:
2116: if (tt == XmlCursor.TokenType.TEXT) {
2117: String currentText = curs.getChars().trim();
2118:
2119: if (currentText.trim().length() == 0) {
2120: // Empty text node, remove.
2121: removeToken(curs);
2122: curs.toPrevToken();
2123: } else if (previousText == null) {
2124: // No previous text node, reset to trimmed version
2125: previousText = currentText;
2126: } else {
2127: // It appears that this case never happens with XBeans.
2128: // Previous text node exists, concatenate
2129: String newText = previousText + currentText;
2130:
2131: curs.toPrevToken();
2132: removeToken(curs);
2133: removeToken(curs);
2134: curs.insertChars(newText);
2135: }
2136: } else {
2137: previousText = null;
2138: }
2139:
2140: if (tt.isStart()) {
2141: nestLevel++;
2142: } else if (tt.isEnd()) {
2143: nestLevel--;
2144: } else if (tt.isEnddoc()) {
2145: // Shouldn't get here, but just in case.
2146: break;
2147: }
2148: }
2149: }
2150:
2151: curs.dispose();
2152: }
2153:
2154: /**
2155: *
2156: * @return
2157: */
2158: Object parent() {
2159: Object parent;
2160:
2161: XmlCursor curs = newCursor();
2162:
2163: if (curs.isStartdoc()) {
2164: // At doc level - no parent
2165: parent = Undefined.instance;
2166: } else {
2167: if (curs.toParent()) {
2168: if (curs.isStartdoc()) {
2169: // Was top-level - no parent
2170: parent = Undefined.instance;
2171: } else {
2172: parent = getFromAnnotation(lib,
2173: findAnnotation(curs));
2174: }
2175: } else {
2176: // No parent
2177: parent = Undefined.instance;
2178: }
2179: }
2180:
2181: curs.dispose();
2182:
2183: return parent;
2184: }
2185:
2186: /**
2187: *
2188: * @param xml
2189: * @return
2190: */
2191: XML prependChild(Object xml) {
2192: XmlCursor curs = newCursor();
2193:
2194: if (curs.isStartdoc()) {
2195: curs.toFirstContentToken();
2196: }
2197:
2198: // Move the cursor to the first content token
2199: curs.toFirstContentToken();
2200:
2201: insertChild(curs, xml);
2202:
2203: curs.dispose();
2204:
2205: return this ;
2206: }
2207:
2208: /**
2209: *
2210: * @return
2211: */
2212: Object processingInstructions(XMLName xmlName) {
2213: return matchChildren(XmlCursor.TokenType.PROCINST, xmlName);
2214: }
2215:
2216: /**
2217: *
2218: * @param name
2219: * @return
2220: */
2221: boolean propertyIsEnumerable(Object name) {
2222: boolean result;
2223: if (name instanceof Integer) {
2224: result = (((Integer) name).intValue() == 0);
2225: } else if (name instanceof Number) {
2226: double x = ((Number) name).doubleValue();
2227: // Check that number is posotive 0
2228: result = (x == 0.0 && 1.0 / x > 0);
2229: } else {
2230: result = ScriptRuntime.toString(name).equals("0");
2231: }
2232: return result;
2233: }
2234:
2235: /**
2236: *
2237: * @param namespace
2238: */
2239: XML removeNamespace(Namespace ns) {
2240: XmlCursor cursor = newCursor();
2241:
2242: try {
2243: if (cursor.isStartdoc())
2244: cursor.toFirstContentToken();
2245: if (!cursor.isStart())
2246: return this ;
2247:
2248: String nsPrefix = ns.prefix();
2249: String nsURI = ns.uri();
2250: Map prefixToURI = new HashMap();
2251: int depth = 1;
2252:
2253: while (!(cursor.isEnd() && depth == 0)) {
2254: if (cursor.isStart()) {
2255: // Get the namespaces declared in this element.
2256: // The ones with undefined prefixes are not candidates
2257: // for removal because they are used.
2258: prefixToURI.clear();
2259: NamespaceHelper.getNamespaces(cursor, prefixToURI);
2260: ObjArray inScopeNSBag = new ObjArray();
2261: Iterator i = prefixToURI.entrySet().iterator();
2262: while (i.hasNext()) {
2263: Map.Entry entry = (Map.Entry) i.next();
2264: ns = new Namespace(lib,
2265: (String) entry.getKey(), (String) entry
2266: .getValue());
2267: inScopeNSBag.add(ns);
2268: }
2269:
2270: // Add the URI we are looking for to avoid matching
2271: // non-existing Namespaces.
2272: ns = new Namespace(lib, nsURI);
2273: inScopeNSBag.add(ns);
2274:
2275: Object[] inScopeNS = inScopeNSBag.toArray();
2276:
2277: // Check the element name
2278: Namespace n = NamespaceHelper.getNamespace(lib,
2279: cursor, inScopeNS);
2280: if (nsURI.equals(n.uri())
2281: && (nsPrefix == null || nsPrefix.equals(n
2282: .prefix()))) {
2283: // This namespace is used
2284: return this ;
2285: }
2286:
2287: // Check the attributes
2288: cursor.push();
2289: boolean hasNext = cursor.toFirstAttribute();
2290: while (hasNext) {
2291: n = NamespaceHelper.getNamespace(lib, cursor,
2292: inScopeNS);
2293: if (nsURI.equals(n.uri())
2294: && (nsPrefix == null || nsPrefix
2295: .equals(n.prefix()))) {
2296: // This namespace is used
2297: return this ;
2298: }
2299:
2300: hasNext = cursor.toNextAttribute();
2301: }
2302: cursor.pop();
2303:
2304: if (nsPrefix == null) {
2305: // Remove all namespaces declarations that match nsURI
2306: i = prefixToURI.entrySet().iterator();
2307: while (i.hasNext()) {
2308: Map.Entry entry = (Map.Entry) i.next();
2309: if (entry.getValue().equals(nsURI))
2310: NamespaceHelper.removeNamespace(cursor,
2311: (String) entry.getKey());
2312: }
2313: } else if (nsURI.equals(prefixToURI.get(nsPrefix))) {
2314: // Remove the namespace declaration that matches nsPrefix
2315: NamespaceHelper.removeNamespace(cursor, String
2316: .valueOf(nsPrefix));
2317: }
2318: }
2319:
2320: switch (cursor.toNextToken().intValue()) {
2321: case XmlCursor.TokenType.INT_START:
2322: depth++;
2323: break;
2324: case XmlCursor.TokenType.INT_END:
2325: depth--;
2326: break;
2327: }
2328: }
2329: } finally {
2330: cursor.dispose();
2331: }
2332:
2333: return this ;
2334: }
2335:
2336: XML replace(long index, Object xml) {
2337: XMLList xlChildToReplace = child(index);
2338: if (xlChildToReplace.length() > 0) {
2339: // One exists an that index
2340: XML childToReplace = xlChildToReplace.item(0);
2341: insertChildAfter(childToReplace, xml);
2342: removeChild(index);
2343: }
2344: return this ;
2345: }
2346:
2347: /**
2348: *
2349: * @param propertyName
2350: * @param xml
2351: * @return
2352: */
2353: XML replace(XMLName xmlName, Object xml) {
2354: putXMLProperty(xmlName, xml);
2355: return this ;
2356: }
2357:
2358: /**
2359: *
2360: * @param xml
2361: */
2362: XML setChildren(Object xml) {
2363: // remove all children
2364: XMLName xmlName = XMLName.formStar();
2365: XMLList matches = getPropertyList(xmlName);
2366: matches.remove();
2367:
2368: // append new children
2369: appendChild(xml);
2370:
2371: return this ;
2372: }
2373:
2374: /**
2375: *
2376: * @param name
2377: */
2378: void setLocalName(String localName) {
2379: XmlCursor cursor = newCursor();
2380:
2381: try {
2382: if (cursor.isStartdoc())
2383: cursor.toFirstContentToken();
2384:
2385: if (cursor.isText() || cursor.isComment())
2386: return;
2387:
2388: javax.xml.namespace.QName qname = cursor.getName();
2389: cursor.setName(new javax.xml.namespace.QName(qname
2390: .getNamespaceURI(), localName, qname.getPrefix()));
2391: } finally {
2392: cursor.dispose();
2393: }
2394: }
2395:
2396: /**
2397: *
2398: * @param name
2399: */
2400: void setName(QName qname) {
2401: XmlCursor cursor = newCursor();
2402:
2403: try {
2404: if (cursor.isStartdoc())
2405: cursor.toFirstContentToken();
2406:
2407: if (cursor.isText() || cursor.isComment())
2408: return;
2409:
2410: if (cursor.isProcinst()) {
2411: String localName = qname.localName();
2412: cursor
2413: .setName(new javax.xml.namespace.QName(
2414: localName));
2415: } else {
2416: String prefix = qname.prefix();
2417: if (prefix == null) {
2418: prefix = "";
2419: }
2420: cursor.setName(new javax.xml.namespace.QName(qname
2421: .uri(), qname.localName(), prefix));
2422: }
2423: } finally {
2424: cursor.dispose();
2425: }
2426: }
2427:
2428: /**
2429: *
2430: * @param ns
2431: */
2432: void setNamespace(Namespace ns) {
2433: XmlCursor cursor = newCursor();
2434:
2435: try {
2436: if (cursor.isStartdoc())
2437: cursor.toFirstContentToken();
2438:
2439: if (cursor.isText() || cursor.isComment()
2440: || cursor.isProcinst())
2441: return;
2442:
2443: String prefix = ns.prefix();
2444: if (prefix == null) {
2445: prefix = "";
2446: }
2447: cursor.setName(new javax.xml.namespace.QName(ns.uri(),
2448: localName(), prefix));
2449: } finally {
2450: cursor.dispose();
2451: }
2452: }
2453:
2454: /**
2455: *
2456: * @return
2457: */
2458: XMLList text() {
2459: return matchChildren(XmlCursor.TokenType.TEXT);
2460: }
2461:
2462: /**
2463: *
2464: * @return
2465: */
2466: public String toString() {
2467: String result;
2468: XmlCursor curs = newCursor();
2469:
2470: if (curs.isStartdoc()) {
2471: curs.toFirstContentToken();
2472: }
2473:
2474: if (curs.isText()) {
2475: result = curs.getChars();
2476: } else if (curs.isStart() && hasSimpleContent()) {
2477: result = curs.getTextValue();
2478: } else {
2479: result = toXMLString(0);
2480: }
2481:
2482: return result;
2483: }
2484:
2485: String toSource(int indent) {
2486: // XXX Does toXMLString always return valid XML literal?
2487: return toXMLString(indent);
2488: }
2489:
2490: /**
2491: *
2492: * @return
2493: */
2494: String toXMLString(int indent) {
2495: // XXX indent is ignored
2496:
2497: String result;
2498:
2499: XmlCursor curs = newCursor();
2500:
2501: if (curs.isStartdoc()) {
2502: curs.toFirstContentToken();
2503: }
2504:
2505: try {
2506: if (curs.isText()) {
2507: result = curs.getChars();
2508: } else if (curs.isAttr()) {
2509: result = curs.getTextValue();
2510: } else if (curs.isComment() || curs.isProcinst()) {
2511: result = XML.dumpNode(curs, getOptions());
2512:
2513: // todo: XBeans-dependent hack here
2514: // If it's a comment or PI, take off the xml-frament stuff
2515: String start = "<xml-fragment>";
2516: String end = "</xml-fragment>";
2517:
2518: if (result.startsWith(start)) {
2519: result = result.substring(start.length());
2520: }
2521:
2522: if (result.endsWith(end)) {
2523: result = result.substring(0, result.length()
2524: - end.length());
2525: }
2526: } else {
2527: result = XML.dumpNode(curs, getOptions());
2528: }
2529: } finally {
2530: curs.dispose();
2531: }
2532:
2533: return result;
2534: }
2535:
2536: /**
2537: *
2538: * @return
2539: */
2540: Object valueOf() {
2541: return this ;
2542: }
2543:
2544: //
2545: // Other public Functions from XMLObject
2546: //
2547:
2548: /**
2549: *
2550: * @param target
2551: * @return
2552: */
2553: boolean equivalentXml(Object target) {
2554: boolean result = false;
2555:
2556: if (target instanceof XML) {
2557: XML otherXml = (XML) target;
2558:
2559: // Compare with toString() if either side is text node or attribute
2560: // otherwise compare as XML
2561: XmlCursor.TokenType this TT = tokenType();
2562: XmlCursor.TokenType otherTT = otherXml.tokenType();
2563: if (this TT == XmlCursor.TokenType.ATTR
2564: || otherTT == XmlCursor.TokenType.ATTR
2565: || this TT == XmlCursor.TokenType.TEXT
2566: || otherTT == XmlCursor.TokenType.TEXT) {
2567: result = toString().equals(otherXml.toString());
2568: } else {
2569: XmlCursor cursOne = newCursor();
2570: XmlCursor cursTwo = otherXml.newCursor();
2571:
2572: result = LogicalEquality.nodesEqual(cursOne, cursTwo);
2573:
2574: cursOne.dispose();
2575: cursTwo.dispose();
2576:
2577: // Old way of comparing by string.
2578: // boolean orgPrettyPrinting = prototype.prettyPrinting;
2579: // prototype.prettyPrinting = true;
2580: // result = toXMLString(0).equals(otherXml.toXMLString(0));
2581: // prototype.prettyPrinting = orgPrettyPrinting;
2582: }
2583: } else if (target instanceof XMLList) {
2584: XMLList otherList = (XMLList) target;
2585:
2586: if (otherList.length() == 1) {
2587: result = equivalentXml(otherList
2588: .getXmlFromAnnotation(0));
2589: }
2590: } else if (hasSimpleContent()) {
2591: String otherStr = ScriptRuntime.toString(target);
2592:
2593: result = toString().equals(otherStr);
2594: }
2595:
2596: return result;
2597: }
2598:
2599: /**
2600: *
2601: * @param name
2602: * @param start
2603: * @return
2604: */
2605: XMLList getPropertyList(XMLName name) {
2606: XMLList result;
2607:
2608: // Get the named property
2609: if (name.isDescendants()) {
2610: result = descendants(name);
2611: } else if (name.isAttributeName()) {
2612: result = attribute(name);
2613: } else {
2614: result = child(name);
2615: }
2616:
2617: return result;
2618: }
2619:
2620: protected Object jsConstructor(Context cx, boolean inNewExpr,
2621: Object[] args) {
2622: if (args.length == 0) {
2623: return createFromJS(lib, "");
2624: } else {
2625: Object arg0 = args[0];
2626: if (!inNewExpr && arg0 instanceof XML) {
2627: // XML(XML) returns the same object.
2628: return arg0;
2629: }
2630: return createFromJS(lib, arg0);
2631: }
2632: }
2633:
2634: /**
2635: * See ECMA 357, 11_2_2_1, Semantics, 3_f.
2636: */
2637: public Scriptable getExtraMethodSource(Context cx) {
2638: if (hasSimpleContent()) {
2639: String src = toString();
2640: return ScriptRuntime.toObjectOrNull(cx, src);
2641: }
2642: return null;
2643: }
2644:
2645: XmlObject getXmlObject() {
2646: XmlObject xo;
2647: XmlCursor cursor = newCursor();
2648: try {
2649: xo = cursor.getObject();
2650: } finally {
2651: cursor.dispose();
2652: }
2653: return xo;
2654: }
2655: }
|