001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Igor Bukanov
026: *
027: * Alternatively, the contents of this file may be used under the terms of
028: * the GNU General Public License Version 2 or later (the "GPL"), in which
029: * case the provisions of the GPL are applicable instead of those above. If
030: * you wish to allow use of your version of this file only under the terms of
031: * the GPL and not to allow others to use your version of this file under the
032: * MPL, indicate your decision by deleting the provisions above and replacing
033: * them with the notice and other provisions required by the GPL. If you do
034: * not delete the provisions above, a recipient may use your version of this
035: * file under either the MPL or the GPL.
036: *
037: * ***** END LICENSE BLOCK ***** */
038:
039: package org.mozilla.javascript.xml.impl.xmlbeans;
040:
041: import java.io.Serializable;
042:
043: import org.mozilla.javascript.*;
044: import org.mozilla.javascript.xml.*;
045:
046: import org.apache.xmlbeans.XmlCursor;
047: import org.apache.xmlbeans.XmlObject;
048:
049: public final class XMLLibImpl extends XMLLib implements Serializable {
050: private static final long serialVersionUID = 1L;
051:
052: private Scriptable globalScope;
053:
054: XML xmlPrototype;
055: XMLList xmlListPrototype;
056: Namespace namespacePrototype;
057: QName qnamePrototype;
058:
059: // Environment settings...
060: boolean ignoreComments;
061: boolean ignoreProcessingInstructions;
062: boolean ignoreWhitespace;
063: boolean prettyPrinting;
064: int prettyIndent;
065:
066: Scriptable globalScope() {
067: return globalScope;
068: }
069:
070: private XMLLibImpl(Scriptable globalScope) {
071: this .globalScope = globalScope;
072: defaultSettings();
073: }
074:
075: public static void init(Context cx, Scriptable scope, boolean sealed) {
076: // To force LinkageError if XmlObject is not available
077: XmlObject.class.getName();
078:
079: XMLLibImpl lib = new XMLLibImpl(scope);
080: XMLLib bound = lib.bindToScope(scope);
081: if (bound == lib) {
082: lib.exportToScope(sealed);
083: }
084: }
085:
086: private void exportToScope(boolean sealed) {
087: xmlPrototype = XML.createEmptyXML(this );
088: xmlListPrototype = new XMLList(this );
089: namespacePrototype = new Namespace(this , "", "");
090: qnamePrototype = new QName(this , "", "", "");
091:
092: xmlPrototype.exportAsJSClass(sealed);
093: xmlListPrototype.exportAsJSClass(sealed);
094: namespacePrototype.exportAsJSClass(sealed);
095: qnamePrototype.exportAsJSClass(sealed);
096: }
097:
098: void defaultSettings() {
099: ignoreComments = true;
100: ignoreProcessingInstructions = true;
101: ignoreWhitespace = true;
102: prettyPrinting = true;
103: prettyIndent = 2;
104: }
105:
106: XMLName toAttributeName(Context cx, Object nameValue) {
107: String uri;
108: String localName;
109:
110: if (nameValue instanceof String) {
111: uri = "";
112: localName = (String) nameValue;
113: } else if (nameValue instanceof XMLName) {
114: XMLName xmlName = (XMLName) nameValue;
115: if (!xmlName.isAttributeName()) {
116: xmlName.setAttributeName();
117: }
118: return xmlName;
119: } else if (nameValue instanceof QName) {
120: QName qname = (QName) nameValue;
121: uri = qname.uri();
122: localName = qname.localName();
123: } else if (nameValue instanceof Boolean
124: || nameValue instanceof Number
125: || nameValue == Undefined.instance || nameValue == null) {
126: throw badXMLName(nameValue);
127: } else {
128: uri = "";
129: localName = ScriptRuntime.toString(nameValue);
130: }
131: XMLName xmlName = XMLName.formProperty(uri, localName);
132: xmlName.setAttributeName();
133: return xmlName;
134: }
135:
136: private static RuntimeException badXMLName(Object value) {
137: String msg;
138: if (value instanceof Number) {
139: msg = "Can not construct XML name from number: ";
140: } else if (value instanceof Boolean) {
141: msg = "Can not construct XML name from boolean: ";
142: } else if (value == Undefined.instance || value == null) {
143: msg = "Can not construct XML name from ";
144: } else {
145: throw new IllegalArgumentException(value.toString());
146: }
147: return ScriptRuntime.typeError(msg
148: + ScriptRuntime.toString(value));
149: }
150:
151: XMLName toXMLName(Context cx, Object nameValue) {
152: XMLName result;
153:
154: if (nameValue instanceof XMLName) {
155: result = (XMLName) nameValue;
156: } else if (nameValue instanceof QName) {
157: QName qname = (QName) nameValue;
158: result = XMLName.formProperty(qname.uri(), qname
159: .localName());
160: } else if (nameValue instanceof String) {
161: result = toXMLNameFromString(cx, (String) nameValue);
162: } else if (nameValue instanceof Boolean
163: || nameValue instanceof Number
164: || nameValue == Undefined.instance || nameValue == null) {
165: throw badXMLName(nameValue);
166: } else {
167: String name = ScriptRuntime.toString(nameValue);
168: result = toXMLNameFromString(cx, name);
169: }
170:
171: return result;
172: }
173:
174: /**
175: * If value represents Uint32 index, make it available through
176: * ScriptRuntime.lastUint32Result(cx) and return null.
177: * Otherwise return the same value as toXMLName(cx, value).
178: */
179: XMLName toXMLNameOrIndex(Context cx, Object value) {
180: XMLName result;
181:
182: if (value instanceof XMLName) {
183: result = (XMLName) value;
184: } else if (value instanceof String) {
185: String str = (String) value;
186: long test = ScriptRuntime.testUint32String(str);
187: if (test >= 0) {
188: ScriptRuntime.storeUint32Result(cx, test);
189: result = null;
190: } else {
191: result = toXMLNameFromString(cx, str);
192: }
193: } else if (value instanceof Number) {
194: double d = ((Number) value).doubleValue();
195: long l = (long) d;
196: if (l == d && 0 <= l && l <= 0xFFFFFFFFL) {
197: ScriptRuntime.storeUint32Result(cx, l);
198: result = null;
199: } else {
200: throw badXMLName(value);
201: }
202: } else if (value instanceof QName) {
203: QName qname = (QName) value;
204: String uri = qname.uri();
205: boolean number = false;
206: result = null;
207: if (uri != null && uri.length() == 0) {
208: // Only in this case qname.toString() can resemble uint32
209: long test = ScriptRuntime.testUint32String(uri);
210: if (test >= 0) {
211: ScriptRuntime.storeUint32Result(cx, test);
212: number = true;
213: }
214: }
215: if (!number) {
216: result = XMLName.formProperty(uri, qname.localName());
217: }
218: } else if (value instanceof Boolean
219: || value == Undefined.instance || value == null) {
220: throw badXMLName(value);
221: } else {
222: String str = ScriptRuntime.toString(value);
223: long test = ScriptRuntime.testUint32String(str);
224: if (test >= 0) {
225: ScriptRuntime.storeUint32Result(cx, test);
226: result = null;
227: } else {
228: result = toXMLNameFromString(cx, str);
229: }
230: }
231:
232: return result;
233: }
234:
235: XMLName toXMLNameFromString(Context cx, String name) {
236: if (name == null)
237: throw new IllegalArgumentException();
238:
239: int l = name.length();
240: if (l != 0) {
241: char firstChar = name.charAt(0);
242: if (firstChar == '*') {
243: if (l == 1) {
244: return XMLName.formStar();
245: }
246: } else if (firstChar == '@') {
247: XMLName xmlName = XMLName.formProperty("", name
248: .substring(1));
249: xmlName.setAttributeName();
250: return xmlName;
251: }
252: }
253:
254: String uri = getDefaultNamespaceURI(cx);
255:
256: return XMLName.formProperty(uri, name);
257: }
258:
259: Namespace constructNamespace(Context cx, Object uriValue) {
260: String prefix;
261: String uri;
262:
263: if (uriValue instanceof Namespace) {
264: Namespace ns = (Namespace) uriValue;
265: prefix = ns.prefix();
266: uri = ns.uri();
267: } else if (uriValue instanceof QName) {
268: QName qname = (QName) uriValue;
269: uri = qname.uri();
270: if (uri != null) {
271: prefix = qname.prefix();
272: } else {
273: uri = qname.toString();
274: prefix = null;
275: }
276: } else {
277: uri = ScriptRuntime.toString(uriValue);
278: prefix = (uri.length() == 0) ? "" : null;
279: }
280:
281: return new Namespace(this , prefix, uri);
282: }
283:
284: Namespace castToNamespace(Context cx, Object namescapeObj) {
285: if (namescapeObj instanceof Namespace) {
286: return (Namespace) namescapeObj;
287: }
288: return constructNamespace(cx, namescapeObj);
289: }
290:
291: Namespace constructNamespace(Context cx) {
292: return new Namespace(this , "", "");
293: }
294:
295: public Namespace constructNamespace(Context cx, Object prefixValue,
296: Object uriValue) {
297: String prefix;
298: String uri;
299:
300: if (uriValue instanceof QName) {
301: QName qname = (QName) uriValue;
302: uri = qname.uri();
303: if (uri == null) {
304: uri = qname.toString();
305: }
306: } else {
307: uri = ScriptRuntime.toString(uriValue);
308: }
309:
310: if (uri.length() == 0) {
311: if (prefixValue == Undefined.instance) {
312: prefix = "";
313: } else {
314: prefix = ScriptRuntime.toString(prefixValue);
315: if (prefix.length() != 0) {
316: throw ScriptRuntime.typeError("Illegal prefix '"
317: + prefix + "' for 'no namespace'.");
318: }
319: }
320: } else if (prefixValue == Undefined.instance) {
321: prefix = "";
322: } else if (!isXMLName(cx, prefixValue)) {
323: prefix = "";
324: } else {
325: prefix = ScriptRuntime.toString(prefixValue);
326: }
327:
328: return new Namespace(this , prefix, uri);
329: }
330:
331: String getDefaultNamespaceURI(Context cx) {
332: String uri = "";
333: if (cx == null) {
334: cx = Context.getCurrentContext();
335: }
336: if (cx != null) {
337: Object ns = ScriptRuntime.searchDefaultNamespace(cx);
338: if (ns != null) {
339: if (ns instanceof Namespace) {
340: uri = ((Namespace) ns).uri();
341: } else {
342: // Should not happen but for now it could
343: // due to bad searchDefaultNamespace implementation.
344: }
345: }
346: }
347: return uri;
348: }
349:
350: Namespace getDefaultNamespace(Context cx) {
351: if (cx == null) {
352: cx = Context.getCurrentContext();
353: if (cx == null) {
354: return namespacePrototype;
355: }
356: }
357:
358: Namespace result;
359: Object ns = ScriptRuntime.searchDefaultNamespace(cx);
360: if (ns == null) {
361: result = namespacePrototype;
362: } else {
363: if (ns instanceof Namespace) {
364: result = (Namespace) ns;
365: } else {
366: // Should not happen but for now it could
367: // due to bad searchDefaultNamespace implementation.
368: result = namespacePrototype;
369: }
370: }
371: return result;
372: }
373:
374: QName castToQName(Context cx, Object qnameValue) {
375: if (qnameValue instanceof QName) {
376: return (QName) qnameValue;
377: }
378: return constructQName(cx, qnameValue);
379: }
380:
381: QName constructQName(Context cx, Object nameValue) {
382: QName result;
383:
384: if (nameValue instanceof QName) {
385: QName qname = (QName) nameValue;
386: result = new QName(this , qname.uri(), qname.localName(),
387: qname.prefix());
388: } else {
389: String localName = ScriptRuntime.toString(nameValue);
390: result = constructQNameFromString(cx, localName);
391: }
392:
393: return result;
394: }
395:
396: /**
397: * Optimized version of constructQName for String type
398: */
399: QName constructQNameFromString(Context cx, String localName) {
400: if (localName == null)
401: throw new IllegalArgumentException();
402:
403: String uri;
404: String prefix;
405:
406: if ("*".equals(localName)) {
407: uri = null;
408: prefix = null;
409: } else {
410: Namespace ns = getDefaultNamespace(cx);
411: uri = ns.uri();
412: prefix = ns.prefix();
413: }
414:
415: return new QName(this , uri, localName, prefix);
416: }
417:
418: QName constructQName(Context cx, Object namespaceValue,
419: Object nameValue) {
420: String uri;
421: String localName;
422: String prefix;
423:
424: if (nameValue instanceof QName) {
425: QName qname = (QName) nameValue;
426: localName = qname.localName();
427: } else {
428: localName = ScriptRuntime.toString(nameValue);
429: }
430:
431: Namespace ns;
432: if (namespaceValue == Undefined.instance) {
433: if ("*".equals(localName)) {
434: ns = null;
435: } else {
436: ns = getDefaultNamespace(cx);
437: }
438: } else if (namespaceValue == null) {
439: ns = null;
440: } else if (namespaceValue instanceof Namespace) {
441: ns = (Namespace) namespaceValue;
442: } else {
443: ns = constructNamespace(cx, namespaceValue);
444: }
445:
446: if (ns == null) {
447: uri = null;
448: prefix = null;
449: } else {
450: uri = ns.uri();
451: prefix = ns.prefix();
452: }
453:
454: return new QName(this , uri, localName, prefix);
455: }
456:
457: Object addXMLObjects(Context cx, XMLObject obj1, XMLObject obj2) {
458: XMLList listToAdd = new XMLList(this );
459:
460: if (obj1 instanceof XMLList) {
461: XMLList list1 = (XMLList) obj1;
462: if (list1.length() == 1) {
463: listToAdd.addToList(list1.item(0));
464: } else {
465: // Might be xmlFragment + xmlFragment + xmlFragment + ...;
466: // then the result will be an XMLList which we want to be an
467: // rValue and allow it to be assigned to an lvalue.
468: listToAdd = new XMLList(this , obj1);
469: }
470: } else {
471: listToAdd.addToList(obj1);
472: }
473:
474: if (obj2 instanceof XMLList) {
475: XMLList list2 = (XMLList) obj2;
476: for (int i = 0; i < list2.length(); i++) {
477: listToAdd.addToList(list2.item(i));
478: }
479: } else if (obj2 instanceof XML) {
480: listToAdd.addToList(obj2);
481: }
482:
483: return listToAdd;
484: }
485:
486: //
487: //
488: // Overriding XMLLib methods
489: //
490: //
491:
492: /**
493: * See E4X 13.1.2.1.
494: */
495: public boolean isXMLName(Context cx, Object nameObj) {
496: String name;
497: try {
498: name = ScriptRuntime.toString(nameObj);
499: } catch (EcmaError ee) {
500: if ("TypeError".equals(ee.getName())) {
501: return false;
502: }
503: throw ee;
504: }
505:
506: // See http://w3.org/TR/xml-names11/#NT-NCName
507: int length = name.length();
508: if (length != 0) {
509: if (isNCNameStartChar(name.charAt(0))) {
510: for (int i = 1; i != length; ++i) {
511: if (!isNCNameChar(name.charAt(i))) {
512: return false;
513: }
514: }
515: return true;
516: }
517: }
518:
519: return false;
520: }
521:
522: private static boolean isNCNameStartChar(int c) {
523: if ((c & ~0x7F) == 0) {
524: // Optimize for ASCII and use A..Z < _ < a..z
525: if (c >= 'a') {
526: return c <= 'z';
527: } else if (c >= 'A') {
528: if (c <= 'Z') {
529: return true;
530: }
531: return c == '_';
532: }
533: } else if ((c & ~0x1FFF) == 0) {
534: return (0xC0 <= c && c <= 0xD6) || (0xD8 <= c && c <= 0xF6)
535: || (0xF8 <= c && c <= 0x2FF)
536: || (0x370 <= c && c <= 0x37D) || 0x37F <= c;
537: }
538: return (0x200C <= c && c <= 0x200D)
539: || (0x2070 <= c && c <= 0x218F)
540: || (0x2C00 <= c && c <= 0x2FEF)
541: || (0x3001 <= c && c <= 0xD7FF)
542: || (0xF900 <= c && c <= 0xFDCF)
543: || (0xFDF0 <= c && c <= 0xFFFD)
544: || (0x10000 <= c && c <= 0xEFFFF);
545: }
546:
547: private static boolean isNCNameChar(int c) {
548: if ((c & ~0x7F) == 0) {
549: // Optimize for ASCII and use - < . < 0..9 < A..Z < _ < a..z
550: if (c >= 'a') {
551: return c <= 'z';
552: } else if (c >= 'A') {
553: if (c <= 'Z') {
554: return true;
555: }
556: return c == '_';
557: } else if (c >= '0') {
558: return c <= '9';
559: } else {
560: return c == '-' || c == '.';
561: }
562: } else if ((c & ~0x1FFF) == 0) {
563: return isNCNameStartChar(c) || c == 0xB7
564: || (0x300 <= c && c <= 0x36F);
565: }
566: return isNCNameStartChar(c) || (0x203F <= c && c <= 0x2040);
567: }
568:
569: XMLName toQualifiedName(Context cx, Object namespaceValue,
570: Object nameValue) {
571: // This is duplication of constructQName(cx, namespaceValue, nameValue)
572: // but for XMLName
573:
574: String uri;
575: String localName;
576:
577: if (nameValue instanceof QName) {
578: QName qname = (QName) nameValue;
579: localName = qname.localName();
580: } else {
581: localName = ScriptRuntime.toString(nameValue);
582: }
583:
584: Namespace ns;
585: if (namespaceValue == Undefined.instance) {
586: if ("*".equals(localName)) {
587: ns = null;
588: } else {
589: ns = getDefaultNamespace(cx);
590: }
591: } else if (namespaceValue == null) {
592: ns = null;
593: } else if (namespaceValue instanceof Namespace) {
594: ns = (Namespace) namespaceValue;
595: } else {
596: ns = constructNamespace(cx, namespaceValue);
597: }
598:
599: if (ns == null) {
600: uri = null;
601: } else {
602: uri = ns.uri();
603: }
604:
605: return XMLName.formProperty(uri, localName);
606: }
607:
608: public Ref nameRef(Context cx, Object name, Scriptable scope,
609: int memberTypeFlags) {
610: if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) == 0) {
611: // should only be called foir cases like @name or @[expr]
612: throw Kit.codeBug();
613: }
614: XMLName xmlName = toAttributeName(cx, name);
615: return xmlPrimaryReference(cx, xmlName, scope);
616: }
617:
618: public Ref nameRef(Context cx, Object namespace, Object name,
619: Scriptable scope, int memberTypeFlags) {
620: XMLName xmlName = toQualifiedName(cx, namespace, name);
621: if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) != 0) {
622: if (!xmlName.isAttributeName()) {
623: xmlName.setAttributeName();
624: }
625: }
626: return xmlPrimaryReference(cx, xmlName, scope);
627: }
628:
629: private Ref xmlPrimaryReference(Context cx, XMLName xmlName,
630: Scriptable scope) {
631: XMLObjectImpl xmlObj;
632: XMLObjectImpl firstXmlObject = null;
633: for (;;) {
634: // XML object can only present on scope chain as a wrapper
635: // of XMLWithScope
636: if (scope instanceof XMLWithScope) {
637: xmlObj = (XMLObjectImpl) scope.getPrototype();
638: if (xmlObj.hasXMLProperty(xmlName)) {
639: break;
640: }
641: if (firstXmlObject == null) {
642: firstXmlObject = xmlObj;
643: }
644: }
645: scope = scope.getParentScope();
646: if (scope == null) {
647: xmlObj = firstXmlObject;
648: break;
649: }
650: }
651:
652: // xmlObj == null corresponds to undefined as the target of
653: // the reference
654: if (xmlObj != null) {
655: xmlName.initXMLObject(xmlObj);
656: }
657: return xmlName;
658: }
659:
660: /**
661: * Escapes the reserved characters in a value of an attribute
662: *
663: * @param value Unescaped text
664: * @return The escaped text
665: */
666: public String escapeAttributeValue(Object value) {
667: String text = ScriptRuntime.toString(value);
668:
669: if (text.length() == 0)
670: return "";
671:
672: XmlObject xo = XmlObject.Factory.newInstance();
673:
674: XmlCursor cursor = xo.newCursor();
675: cursor.toNextToken();
676: cursor.beginElement("a");
677: cursor.insertAttributeWithValue("a", text);
678: cursor.dispose();
679:
680: String elementText = xo.toString();
681: int begin = elementText.indexOf('"');
682: int end = elementText.lastIndexOf('"');
683: return elementText.substring(begin + 1, end);
684: }
685:
686: /**
687: * Escapes the reserved characters in a value of a text node
688: *
689: * @param value Unescaped text
690: * @return The escaped text
691: */
692: public String escapeTextValue(Object value) {
693: if (value instanceof XMLObjectImpl) {
694: return ((XMLObjectImpl) value).toXMLString(0);
695: }
696:
697: String text = ScriptRuntime.toString(value);
698:
699: if (text.length() == 0)
700: return text;
701:
702: XmlObject xo = XmlObject.Factory.newInstance();
703:
704: XmlCursor cursor = xo.newCursor();
705: cursor.toNextToken();
706: cursor.beginElement("a");
707: cursor.insertChars(text);
708: cursor.dispose();
709:
710: String elementText = xo.toString();
711: int begin = elementText.indexOf('>') + 1;
712: int end = elementText.lastIndexOf('<');
713: return (begin < end) ? elementText.substring(begin, end) : "";
714: }
715:
716: public Object toDefaultXmlNamespace(Context cx, Object uriValue) {
717: return constructNamespace(cx, uriValue);
718: }
719: }
|