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: * Milen Nankov
027: * David P. Caldwell <inonit@inonit.com>
028: *
029: * Alternatively, the contents of this file may be used under the terms of
030: * the GNU General Public License Version 2 or later (the "GPL"), in which
031: * case the provisions of the GPL are applicable instead of those above. If
032: * you wish to allow use of your version of this file only under the terms of
033: * the GPL and not to allow others to use your version of this file under the
034: * MPL, indicate your decision by deleting the provisions above and replacing
035: * them with the notice and other provisions required by the GPL. If you do
036: * not delete the provisions above, a recipient may use your version of this
037: * file under either the MPL or the GPL.
038: *
039: * ***** END LICENSE BLOCK ***** */
040:
041: package org.mozilla.javascript.xmlimpl;
042:
043: import org.mozilla.javascript.*;
044:
045: class XMLName extends Ref {
046: static final long serialVersionUID = 3832176310755686977L;
047:
048: private static boolean isNCNameStartChar(int c) {
049: if ((c & ~0x7F) == 0) {
050: // Optimize for ASCII and use A..Z < _ < a..z
051: if (c >= 'a') {
052: return c <= 'z';
053: } else if (c >= 'A') {
054: if (c <= 'Z') {
055: return true;
056: }
057: return c == '_';
058: }
059: } else if ((c & ~0x1FFF) == 0) {
060: return (0xC0 <= c && c <= 0xD6) || (0xD8 <= c && c <= 0xF6)
061: || (0xF8 <= c && c <= 0x2FF)
062: || (0x370 <= c && c <= 0x37D) || 0x37F <= c;
063: }
064: return (0x200C <= c && c <= 0x200D)
065: || (0x2070 <= c && c <= 0x218F)
066: || (0x2C00 <= c && c <= 0x2FEF)
067: || (0x3001 <= c && c <= 0xD7FF)
068: || (0xF900 <= c && c <= 0xFDCF)
069: || (0xFDF0 <= c && c <= 0xFFFD)
070: || (0x10000 <= c && c <= 0xEFFFF);
071: }
072:
073: private static boolean isNCNameChar(int c) {
074: if ((c & ~0x7F) == 0) {
075: // Optimize for ASCII and use - < . < 0..9 < A..Z < _ < a..z
076: if (c >= 'a') {
077: return c <= 'z';
078: } else if (c >= 'A') {
079: if (c <= 'Z') {
080: return true;
081: }
082: return c == '_';
083: } else if (c >= '0') {
084: return c <= '9';
085: } else {
086: return c == '-' || c == '.';
087: }
088: } else if ((c & ~0x1FFF) == 0) {
089: return isNCNameStartChar(c) || c == 0xB7
090: || (0x300 <= c && c <= 0x36F);
091: }
092: return isNCNameStartChar(c) || (0x203F <= c && c <= 0x2040);
093: }
094:
095: // This means "accept" in the parsing sense
096: // See ECMA357 13.1.2.1
097: static boolean accept(Object nameObj) {
098: String name;
099: try {
100: name = ScriptRuntime.toString(nameObj);
101: } catch (EcmaError ee) {
102: if ("TypeError".equals(ee.getName())) {
103: return false;
104: }
105: throw ee;
106: }
107:
108: // See http://w3.org/TR/xml-names11/#NT-NCName
109: int length = name.length();
110: if (length != 0) {
111: if (isNCNameStartChar(name.charAt(0))) {
112: for (int i = 1; i != length; ++i) {
113: if (!isNCNameChar(name.charAt(i))) {
114: return false;
115: }
116: }
117: return true;
118: }
119: }
120:
121: return false;
122: }
123:
124: private XmlNode.QName qname;
125: private boolean isAttributeName;
126: private boolean isDescendants;
127: private XMLObjectImpl xmlObject;
128:
129: private XMLName() {
130: }
131:
132: static XMLName formStar() {
133: XMLName rv = new XMLName();
134: rv.qname = XmlNode.QName.create(null, null);
135: return rv;
136: }
137:
138: /** @deprecated */
139: static XMLName formProperty(XmlNode.Namespace namespace,
140: String localName) {
141: if (localName != null && localName.equals("*"))
142: localName = null;
143: XMLName rv = new XMLName();
144: rv.qname = XmlNode.QName.create(namespace, localName);
145: return rv;
146: }
147:
148: /** @deprecated */
149: static XMLName formProperty(String uri, String localName) {
150: return formProperty(XmlNode.Namespace.create(uri), localName);
151: }
152:
153: /** @deprecated */
154: static XMLName create(String defaultNamespaceUri, String name) {
155: if (name == null)
156: throw new IllegalArgumentException();
157:
158: int l = name.length();
159: if (l != 0) {
160: char firstChar = name.charAt(0);
161: if (firstChar == '*') {
162: if (l == 1) {
163: return XMLName.formStar();
164: }
165: } else if (firstChar == '@') {
166: XMLName xmlName = XMLName.formProperty("", name
167: .substring(1));
168: xmlName.setAttributeName();
169: return xmlName;
170: }
171: }
172:
173: return XMLName.formProperty(defaultNamespaceUri, name);
174: }
175:
176: static XMLName create(XmlNode.QName qname, boolean attribute,
177: boolean descendants) {
178: XMLName rv = new XMLName();
179: rv.qname = qname;
180: rv.isAttributeName = attribute;
181: rv.isDescendants = descendants;
182: return rv;
183: }
184:
185: /** @deprecated */
186: static XMLName create(XmlNode.QName qname) {
187: return create(qname, false, false);
188: }
189:
190: void initXMLObject(XMLObjectImpl xmlObject) {
191: if (xmlObject == null)
192: throw new IllegalArgumentException();
193: if (this .xmlObject != null)
194: throw new IllegalStateException();
195: this .xmlObject = xmlObject;
196: }
197:
198: String uri() {
199: if (qname.getNamespace() == null)
200: return null;
201: return qname.getNamespace().getUri();
202: }
203:
204: String localName() {
205: if (qname.getLocalName() == null)
206: return "*";
207: return qname.getLocalName();
208: }
209:
210: private void addDescendantChildren(XMLList list, XML target) {
211: XMLName xmlName = this ;
212: if (target.isElement()) {
213: XML[] children = target.getChildren();
214: for (int i = 0; i < children.length; i++) {
215: if (xmlName.matches(children[i])) {
216: list.addToList(children[i]);
217: }
218: addDescendantChildren(list, children[i]);
219: }
220: }
221: }
222:
223: void addMatchingAttributes(XMLList list, XML target) {
224: XMLName name = this ;
225: if (target.isElement()) {
226: XML[] attributes = target.getAttributes();
227: for (int i = 0; i < attributes.length; i++) {
228: if (name.matches(attributes[i])) {
229: list.addToList(attributes[i]);
230: }
231: }
232: }
233: }
234:
235: private void addDescendantAttributes(XMLList list, XML target) {
236: if (target.isElement()) {
237: addMatchingAttributes(list, target);
238: XML[] children = target.getChildren();
239: for (int i = 0; i < children.length; i++) {
240: addDescendantAttributes(list, children[i]);
241: }
242: }
243: }
244:
245: XMLList matchDescendantAttributes(XMLList rv, XML target) {
246: rv.setTargets(target, null);
247: addDescendantAttributes(rv, target);
248: return rv;
249: }
250:
251: XMLList matchDescendantChildren(XMLList rv, XML target) {
252: rv.setTargets(target, null);
253: addDescendantChildren(rv, target);
254: return rv;
255: }
256:
257: void addDescendants(XMLList rv, XML target) {
258: XMLName xmlName = this ;
259: if (xmlName.isAttributeName()) {
260: matchDescendantAttributes(rv, target);
261: } else {
262: matchDescendantChildren(rv, target);
263: }
264: }
265:
266: private void addAttributes(XMLList rv, XML target) {
267: addMatchingAttributes(rv, target);
268: }
269:
270: void addMatches(XMLList rv, XML target) {
271: if (isDescendants()) {
272: addDescendants(rv, target);
273: } else if (isAttributeName()) {
274: addAttributes(rv, target);
275: } else {
276: XML[] children = target.getChildren();
277: if (children != null) {
278: for (int i = 0; i < children.length; i++) {
279: if (this .matches(children[i])) {
280: rv.addToList(children[i]);
281: }
282: }
283: }
284: rv.setTargets(target, this .toQname());
285: }
286: }
287:
288: XMLList getMyValueOn(XML target) {
289: XMLList rv = target.newXMLList();
290: addMatches(rv, target);
291: return rv;
292: }
293:
294: void setMyValueOn(XML target, Object value) {
295: // Special-case checks for undefined and null
296: if (value == null) {
297: value = "null";
298: } else if (value instanceof Undefined) {
299: value = "undefined";
300: }
301:
302: XMLName xmlName = this ;
303: // Get the named property
304: if (xmlName.isAttributeName()) {
305: target.setAttribute(xmlName, value);
306: } else if (xmlName.uri() == null
307: && xmlName.localName().equals("*")) {
308: target.setChildren(value);
309: } else {
310: // Convert text into XML if needed.
311: XMLObjectImpl xmlValue = null;
312:
313: if (value instanceof XMLObjectImpl) {
314: xmlValue = (XMLObjectImpl) value;
315:
316: // Check for attribute type and convert to textNode
317: if (xmlValue instanceof XML) {
318: if (((XML) xmlValue).isAttribute()) {
319: xmlValue = target.makeXmlFromString(xmlName,
320: xmlValue.toString());
321: }
322: }
323:
324: if (xmlValue instanceof XMLList) {
325: for (int i = 0; i < xmlValue.length(); i++) {
326: XML xml = ((XMLList) xmlValue).item(i);
327:
328: if (xml.isAttribute()) {
329: ((XMLList) xmlValue).replace(i, target
330: .makeXmlFromString(xmlName, xml
331: .toString()));
332: }
333: }
334: }
335: } else {
336: xmlValue = target.makeXmlFromString(xmlName,
337: ScriptRuntime.toString(value));
338: }
339:
340: XMLList matches = target.getPropertyList(xmlName);
341:
342: if (matches.length() == 0) {
343: target.appendChild(xmlValue);
344: } else {
345: // Remove all other matches
346: for (int i = 1; i < matches.length(); i++) {
347: target.removeChild(matches.item(i).childIndex());
348: }
349:
350: // Replace first match with new value.
351: XML firstMatch = matches.item(0);
352: target.replace(firstMatch.childIndex(), xmlValue);
353: }
354: }
355: }
356:
357: public boolean has(Context cx) {
358: if (xmlObject == null) {
359: return false;
360: }
361: return xmlObject.hasXMLProperty(this );
362: }
363:
364: public Object get(Context cx) {
365: if (xmlObject == null) {
366: throw ScriptRuntime.undefReadError(Undefined.instance,
367: toString());
368: }
369: return xmlObject.getXMLProperty(this );
370: }
371:
372: public Object set(Context cx, Object value) {
373: if (xmlObject == null) {
374: throw ScriptRuntime.undefWriteError(Undefined.instance,
375: toString(), value);
376: }
377: // Assignment to descendants causes parse error on bad reference
378: // and this should not be called
379: if (isDescendants)
380: throw Kit.codeBug();
381: xmlObject.putXMLProperty(this , value);
382: return value;
383: }
384:
385: public boolean delete(Context cx) {
386: if (xmlObject == null) {
387: return true;
388: }
389: xmlObject.deleteXMLProperty(this );
390: return !xmlObject.hasXMLProperty(this );
391: }
392:
393: public String toString() {
394: //return qname.localName();
395: StringBuffer buff = new StringBuffer();
396: if (isDescendants)
397: buff.append("..");
398: if (isAttributeName)
399: buff.append('@');
400: if (uri() == null) {
401: buff.append('*');
402: if (localName().equals("*")) {
403: return buff.toString();
404: }
405: } else {
406: buff.append('"').append(uri()).append('"');
407: }
408: buff.append(':').append(localName());
409: return buff.toString();
410: }
411:
412: final XmlNode.QName toQname() {
413: return this .qname;
414: }
415:
416: final boolean matchesLocalName(String localName) {
417: return localName().equals("*") || localName().equals(localName);
418: }
419:
420: final boolean matchesElement(XmlNode.QName qname) {
421: if (this .uri() == null
422: || this .uri().equals(qname.getNamespace().getUri())) {
423: if (this .localName().equals("*")
424: || this .localName().equals(qname.getLocalName())) {
425: return true;
426: }
427: }
428: return false;
429: }
430:
431: final boolean matches(XML node) {
432: XmlNode.QName qname = node.getNodeQname();
433: String nodeUri = null;
434: if (qname.getNamespace() != null) {
435: nodeUri = qname.getNamespace().getUri();
436: }
437: if (isAttributeName) {
438: if (node.isAttribute()) {
439: if (this .uri() == null || this .uri().equals(nodeUri)) {
440: if (this .localName().equals("*")
441: || this .localName().equals(
442: qname.getLocalName())) {
443: return true;
444: }
445: }
446: return false;
447: } else {
448: // TODO Could throw exception maybe, should not call this method on attribute name with arbitrary node type
449: // unless we traverse all attributes and children habitually
450: return false;
451: }
452: } else {
453: if (this .uri() == null
454: || ((node.isElement()) && this .uri()
455: .equals(nodeUri))) {
456: if (localName().equals("*"))
457: return true;
458: if (node.isElement()) {
459: if (localName().equals(qname.getLocalName()))
460: return true;
461: }
462: }
463: return false;
464: }
465: }
466:
467: /** @deprecated */
468: boolean isAttributeName() {
469: return isAttributeName;
470: }
471:
472: // TODO Fix whether this is an attribute XMLName at construction?
473: /** @deprecated */
474: void setAttributeName() {
475: // if (isAttributeName) throw new IllegalStateException();
476: isAttributeName = true;
477: }
478:
479: /** @deprecated */
480: boolean isDescendants() {
481: return isDescendants;
482: }
483:
484: // TODO Fix whether this is an descendant XMLName at construction?
485: /** @deprecated */
486: void setIsDescendants() {
487: // if (isDescendants) throw new IllegalStateException();
488: isDescendants = true;
489: }
490: }
|