PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/batik-1.8/sources/org/apache/batik/dom/util/DOMUtilities.java

#
Java | 1063 lines | 921 code | 31 blank | 111 comment | 72 complexity | 6fbf60a75f10f767603eec543681ba40 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, IPL-1.0
  1. /*
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. */
  15. package org.apache.batik.dom.util;
  16. import java.io.IOException;
  17. import java.io.StringReader;
  18. import java.io.StringWriter;
  19. import java.io.Writer;
  20. import java.util.ArrayList;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import org.apache.batik.dom.AbstractDocument;
  26. import org.apache.batik.util.XMLConstants;
  27. import org.apache.batik.xml.XMLUtilities;
  28. import org.w3c.dom.Attr;
  29. import org.w3c.dom.DOMException;
  30. import org.w3c.dom.DOMImplementation;
  31. import org.w3c.dom.Document;
  32. import org.w3c.dom.DocumentType;
  33. import org.w3c.dom.Element;
  34. import org.w3c.dom.NamedNodeMap;
  35. import org.w3c.dom.Node;
  36. import org.w3c.dom.NodeList;
  37. /**
  38. * A collection of utility functions for the DOM.
  39. *
  40. * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
  41. * @version $Id: DOMUtilities.java 1079677 2011-03-09 06:35:41Z cam $
  42. */
  43. public class DOMUtilities extends XMLUtilities implements XMLConstants {
  44. /**
  45. * Does not need to be instantiated.
  46. */
  47. protected DOMUtilities() {
  48. }
  49. /**
  50. * A node in a linked list of prefix to namespace name mappings.
  51. */
  52. private static class NSMap {
  53. /**
  54. * The prefix to map.
  55. */
  56. private String prefix;
  57. /**
  58. * The namespace name that the prefix maps to.
  59. * The empty string is used to represent no namespace.
  60. */
  61. private String ns;
  62. /**
  63. * The next namespace prefix mapping in the list.
  64. */
  65. private NSMap next;
  66. /**
  67. * The next number to use when generating prefixes.
  68. * A prefix of the form <code>"a" + number</code> is generated when
  69. * serializing a node whose namespace URI does not correspond to
  70. * a prefix in scope.
  71. */
  72. private int nextPrefixNumber;
  73. /**
  74. * Constructs a new namespace prefix mapping object with the
  75. * XML and XMLNS namespaces predeclared.
  76. */
  77. public static NSMap create() {
  78. return new NSMap().declare(XMLConstants.XML_PREFIX,
  79. XMLConstants.XML_NAMESPACE_URI)
  80. .declare(XMLConstants.XMLNS_PREFIX,
  81. XMLConstants.XMLNS_NAMESPACE_URI);
  82. }
  83. /**
  84. * Creates a new <code>NSMap</code> object.
  85. */
  86. private NSMap() {
  87. }
  88. /**
  89. * Declares a new prefix mapping by returning a new
  90. * <code>NSMap</code> object that links to this one.
  91. */
  92. public NSMap declare(String prefix, String ns) {
  93. NSMap m = new NSMap();
  94. m.prefix = prefix;
  95. m.ns = ns;
  96. m.next = this;
  97. m.nextPrefixNumber = this.nextPrefixNumber;
  98. return m;
  99. }
  100. /**
  101. * Returns a new, generated namespace prefix.
  102. */
  103. public String getNewPrefix() {
  104. String prefix;
  105. do {
  106. prefix = "a" + nextPrefixNumber++;
  107. } while (getNamespace(prefix) != null);
  108. return prefix;
  109. }
  110. /**
  111. * Returns the namespace URI that the specified prefix
  112. * maps to, or <code>null</code> if the prefix has not
  113. * been declared.
  114. */
  115. public String getNamespace(String prefix) {
  116. for (NSMap m = this; m.next != null; m = m.next) {
  117. if (m.prefix.equals(prefix)) {
  118. return m.ns;
  119. }
  120. }
  121. return null;
  122. }
  123. /**
  124. * Returns the prefix appropriate for an element that maps to specified
  125. * namespace URI. If the specified namespace is the default namespace
  126. * (i.e., it has an empty string prefix mapping to it), then the empty
  127. * string is returned. If there is no appropriate prefix,
  128. * <code>null</code> is returned.
  129. */
  130. public String getPrefixForElement(String ns) {
  131. for (NSMap m = this; m.next != null; m = m.next) {
  132. if (ns.equals(m.ns)) {
  133. return m.prefix;
  134. }
  135. }
  136. return null;
  137. }
  138. /**
  139. * Returns the prefix appropriate for an attribute that maps to
  140. * specified namespace URI. If there is no appropriate prefix,
  141. * <code>null</code> is returned.
  142. */
  143. public String getPrefixForAttr(String ns) {
  144. for (NSMap m = this; m.next != null; m = m.next) {
  145. if (ns.equals(m.ns) && !m.prefix.equals("")) {
  146. return m.prefix;
  147. }
  148. }
  149. return null;
  150. }
  151. }
  152. /**
  153. * Serializes the specified <code>Document</code>, writing it to the given
  154. * <code>Writer</code>.
  155. */
  156. public static void writeDocument(Document doc, Writer w) throws IOException {
  157. AbstractDocument d = (AbstractDocument) doc;
  158. if (doc.getDocumentElement() == null) {
  159. throw new IOException("No document element");
  160. }
  161. NSMap m = NSMap.create();
  162. for (Node n = doc.getFirstChild();
  163. n != null;
  164. n = n.getNextSibling()) {
  165. writeNode(n, w, m, "1.1".equals(d.getXmlVersion()));
  166. }
  167. }
  168. protected static void writeNode(Node n, Writer w, NSMap m, boolean isXML11)
  169. throws IOException {
  170. switch (n.getNodeType()) {
  171. case Node.ELEMENT_NODE: {
  172. if (n.hasAttributes()) {
  173. NamedNodeMap attr = n.getAttributes();
  174. int len = attr.getLength();
  175. for (int i = 0; i < len; i++) {
  176. Attr a = (Attr)attr.item(i);
  177. String name = a.getNodeName();
  178. if (name.startsWith("xmlns")) {
  179. if (name.length() == 5) {
  180. m = m.declare("", a.getNodeValue());
  181. } else {
  182. String prefix = name.substring(6);
  183. m = m.declare(prefix, a.getNodeValue());
  184. }
  185. }
  186. }
  187. }
  188. w.write('<');
  189. String ns = n.getNamespaceURI();
  190. String tagName;
  191. if (ns == null) {
  192. tagName = n.getNodeName();
  193. w.write(tagName);
  194. if (!"".equals(m.getNamespace(""))) {
  195. w.write(" xmlns=\"\"");
  196. m = m.declare("", "");
  197. }
  198. } else {
  199. String prefix = n.getPrefix();
  200. if (prefix == null) {
  201. prefix = "";
  202. }
  203. if (ns.equals(m.getNamespace(prefix))) {
  204. tagName = n.getNodeName();
  205. w.write(tagName);
  206. } else {
  207. prefix = m.getPrefixForElement(ns);
  208. if (prefix == null) {
  209. prefix = m.getNewPrefix();
  210. tagName = prefix + ':' + n.getLocalName();
  211. w.write(tagName + " xmlns:" + prefix + "=\""
  212. + contentToString(ns, isXML11) + '"');
  213. m = m.declare(prefix, ns);
  214. } else {
  215. if (prefix.equals("")) {
  216. tagName = n.getLocalName();
  217. } else {
  218. tagName = prefix + ':' + n.getLocalName();
  219. }
  220. w.write(tagName);
  221. }
  222. }
  223. }
  224. if (n.hasAttributes()) {
  225. NamedNodeMap attr = n.getAttributes();
  226. int len = attr.getLength();
  227. for (int i = 0; i < len; i++) {
  228. Attr a = (Attr)attr.item(i);
  229. String name = a.getNodeName();
  230. String prefix = a.getPrefix();
  231. String ans = a.getNamespaceURI();
  232. if (ans != null &&
  233. !("xmlns".equals(prefix) || name.equals("xmlns"))) {
  234. if (prefix != null
  235. && !ans.equals(m.getNamespace(prefix))
  236. || prefix == null) {
  237. prefix = m.getPrefixForAttr(ans);
  238. if (prefix == null) {
  239. prefix = m.getNewPrefix();
  240. m = m.declare(prefix, ans);
  241. w.write(" xmlns:" + prefix + "=\""
  242. + contentToString(ans, isXML11) + '"');
  243. }
  244. name = prefix + ':' + a.getLocalName();
  245. }
  246. }
  247. w.write(' ' + name + "=\""
  248. + contentToString(a.getNodeValue(), isXML11)
  249. + '"');
  250. }
  251. }
  252. Node c = n.getFirstChild();
  253. if (c != null) {
  254. w.write('>');
  255. do {
  256. writeNode(c, w, m, isXML11);
  257. c = c.getNextSibling();
  258. } while (c != null);
  259. w.write("</" + tagName + '>');
  260. } else {
  261. w.write("/>");
  262. }
  263. break;
  264. }
  265. case Node.TEXT_NODE:
  266. w.write(contentToString(n.getNodeValue(), isXML11));
  267. break;
  268. case Node.CDATA_SECTION_NODE: {
  269. String data = n.getNodeValue();
  270. if (data.indexOf("]]>") != -1) {
  271. throw new IOException("Unserializable CDATA section node");
  272. }
  273. w.write("<![CDATA["
  274. + assertValidCharacters(data, isXML11)
  275. + "]]>");
  276. break;
  277. }
  278. case Node.ENTITY_REFERENCE_NODE:
  279. w.write('&' + n.getNodeName() + ';');
  280. break;
  281. case Node.PROCESSING_INSTRUCTION_NODE: {
  282. String target = n.getNodeName();
  283. String data = n.getNodeValue();
  284. if (target.equalsIgnoreCase("xml")
  285. || target.indexOf(':') != -1
  286. || data.indexOf("?>") != -1) {
  287. throw new
  288. IOException("Unserializable processing instruction node");
  289. }
  290. w.write("<?" + target + ' ' + data + "?>");
  291. break;
  292. }
  293. case Node.COMMENT_NODE: {
  294. w.write("<!--");
  295. String data = n.getNodeValue();
  296. int len = data.length();
  297. if (len != 0 && data.charAt(len - 1) == '-'
  298. || data.indexOf("--") != -1) {
  299. throw new IOException("Unserializable comment node");
  300. }
  301. w.write(data);
  302. w.write("-->");
  303. break;
  304. }
  305. case Node.DOCUMENT_TYPE_NODE: {
  306. DocumentType dt = (DocumentType)n;
  307. w.write("<!DOCTYPE "
  308. + n.getOwnerDocument().getDocumentElement().getNodeName());
  309. String pubID = dt.getPublicId();
  310. if (pubID != null) {
  311. char q = getUsableQuote(pubID);
  312. if (q == 0) {
  313. throw new IOException("Unserializable DOCTYPE node");
  314. }
  315. w.write(" PUBLIC " + q + pubID + q);
  316. }
  317. String sysID = dt.getSystemId();
  318. if (sysID != null) {
  319. char q = getUsableQuote(sysID);
  320. if (q == 0) {
  321. throw new IOException("Unserializable DOCTYPE node");
  322. }
  323. if (pubID == null) {
  324. w.write(" SYSTEM");
  325. }
  326. w.write(" " + q + sysID + q);
  327. }
  328. String subset = dt.getInternalSubset();
  329. if (subset != null) {
  330. w.write('[' + subset + ']');
  331. }
  332. w.write('>');
  333. break;
  334. }
  335. default:
  336. throw new IOException("Unknown DOM node type " + n.getNodeType());
  337. }
  338. }
  339. /**
  340. * Writes a node using the given writer.
  341. */
  342. public static void writeNode(Node n, Writer w) throws IOException {
  343. if (n.getNodeType() == Node.DOCUMENT_NODE) {
  344. writeDocument((Document) n, w);
  345. } else {
  346. AbstractDocument d = (AbstractDocument) n.getOwnerDocument();
  347. writeNode(n, w, NSMap.create(),
  348. d == null ? false : "1.1".equals(d.getXmlVersion()));
  349. }
  350. }
  351. /**
  352. * Returns the quote character to use when quoting the specified string.
  353. * If the string contains both single and double quotes, then 0 will be
  354. * returned.
  355. */
  356. private static char getUsableQuote(String s) {
  357. char ret = 0;
  358. int i = s.length() - 1;
  359. while (i >= 0) {
  360. char c = s.charAt(i);
  361. if (c == '"') {
  362. if (ret == 0) {
  363. ret = '\'';
  364. } else {
  365. return 0;
  366. }
  367. } else if (c == '\'') {
  368. if (ret == 0) {
  369. ret = '"';
  370. } else {
  371. return 0;
  372. }
  373. }
  374. i--;
  375. }
  376. return ret == 0 ? '"' : ret;
  377. }
  378. /**
  379. * Serializes the given DOM node using {@link #writeNode(Node,Writer)}
  380. * and returns the XML as a String.
  381. *
  382. * @param n The Node to serialize.
  383. * @return A String containing the XML serialization of the Node, or an
  384. * empty String if there was a problem during serialization.
  385. */
  386. public static String getXML(Node n) {
  387. Writer writer = new StringWriter();
  388. try {
  389. DOMUtilities.writeNode(n, writer);
  390. writer.close();
  391. } catch (IOException ex) {
  392. return "";
  393. }
  394. return writer.toString();
  395. }
  396. protected static String assertValidCharacters(String s, boolean isXML11)
  397. throws IOException {
  398. int len = s.length();
  399. for (int i = 0; i < len; i++) {
  400. char c = s.charAt(i);
  401. if (!isXML11 && !isXMLCharacter(c)
  402. || isXML11 && !isXML11Character(c)) {
  403. throw new IOException("Invalid character");
  404. }
  405. }
  406. return s;
  407. }
  408. /**
  409. * Returns the given content value transformed to replace invalid
  410. * characters with entities.
  411. */
  412. public static String contentToString(String s, boolean isXML11)
  413. throws IOException {
  414. StringBuffer result = new StringBuffer(s.length());
  415. int len = s.length();
  416. for (int i = 0; i < len; i++) {
  417. char c = s.charAt(i);
  418. if (!isXML11 && !isXMLCharacter(c)
  419. || isXML11 && !isXML11Character(c)) {
  420. throw new IOException("Invalid character");
  421. }
  422. switch (c) {
  423. case '<':
  424. result.append("&lt;");
  425. break;
  426. case '>':
  427. result.append("&gt;");
  428. break;
  429. case '&':
  430. result.append("&amp;");
  431. break;
  432. case '"':
  433. result.append("&quot;");
  434. break;
  435. case '\'':
  436. result.append("&apos;");
  437. break;
  438. default:
  439. result.append(c);
  440. }
  441. }
  442. return result.toString();
  443. }
  444. /**
  445. * Finds and returns the index of child node in the given parent's children
  446. * array
  447. *
  448. * @param child
  449. * The child node
  450. * @param parent
  451. * The parent node
  452. * @return the index
  453. */
  454. public static int getChildIndex(Node child, Node parent) {
  455. if (child == null || child.getParentNode() != parent
  456. || child.getParentNode() == null) {
  457. return -1;
  458. }
  459. return getChildIndex(child);
  460. }
  461. /**
  462. * Finds and returns the index of child node in its parent's children array
  463. *
  464. * @param child
  465. * The child node
  466. * @return the index in children array
  467. */
  468. public static int getChildIndex(Node child) {
  469. NodeList children = child.getParentNode().getChildNodes();
  470. for (int i = 0; i < children.getLength(); i++) {
  471. Node currentChild = children.item(i);
  472. if (currentChild == child) {
  473. return i;
  474. }
  475. }
  476. return -1;
  477. }
  478. /**
  479. * Checks if any of from the given list of nodes is an ancestor to another
  480. * node
  481. *
  482. * @param ancestorNodes
  483. * The potential ancestor nodes
  484. * @param node
  485. * The potential descendant node
  486. * @return True if at least one node is ancestor of the given node
  487. */
  488. public static boolean isAnyNodeAncestorOf(ArrayList ancestorNodes, Node node) {
  489. int n = ancestorNodes.size();
  490. for (int i = 0; i < n; i++) {
  491. Node ancestor = (Node) ancestorNodes.get(i);
  492. if (isAncestorOf(ancestor, node)) {
  493. return true;
  494. }
  495. }
  496. return false;
  497. }
  498. /**
  499. * Checks whether a node is ancestor of another node.
  500. *
  501. * @param node
  502. * The potential ancestor node
  503. * @param descendant
  504. * The potential descendant node
  505. * @return True if node is ancestor of the descendant node
  506. */
  507. public static boolean isAncestorOf(Node node, Node descendant) {
  508. if (node == null || descendant == null) {
  509. return false;
  510. }
  511. for (Node currentNode = descendant.getParentNode(); currentNode != null; currentNode = currentNode
  512. .getParentNode()) {
  513. if (currentNode == node) {
  514. return true;
  515. }
  516. }
  517. return false;
  518. }
  519. /**
  520. * Tests whether the given node is a child of the given parent node.
  521. *
  522. * @param node
  523. * The potential child node
  524. * @param parentNode
  525. * Parent node
  526. * @return True if a node is a child of the given parent node
  527. */
  528. public static boolean isParentOf(Node node, Node parentNode) {
  529. if (node == null || parentNode == null
  530. || node.getParentNode() != parentNode) {
  531. return false;
  532. }
  533. return true;
  534. }
  535. /**
  536. * Checks if the node can be appended on the given parent node
  537. *
  538. * @param node
  539. * The given node
  540. * @param parentNode
  541. * The given parent node
  542. * @return True if the given node can be appended on the parent node
  543. */
  544. public static boolean canAppend(Node node, Node parentNode) {
  545. if (node == null || parentNode == null || node == parentNode
  546. || isAncestorOf(node, parentNode)) {
  547. return false;
  548. }
  549. return true;
  550. }
  551. /**
  552. * Checks whether any of the nodes from the list can be appended to a given
  553. * parentNode.
  554. *
  555. * @param children
  556. * The given node list
  557. * @param parentNode
  558. * The potential parent node
  559. * @return true if at least one node from a list can be appended
  560. */
  561. public static boolean canAppendAny(ArrayList children, Node parentNode) {
  562. if (!canHaveChildren(parentNode)) {
  563. return false;
  564. }
  565. int n = children.size();
  566. for (int i = 0; i < n; i++) {
  567. Node child = (Node) children.get(i);
  568. if (canAppend(child, parentNode)) {
  569. return true;
  570. }
  571. }
  572. return false;
  573. }
  574. /**
  575. * Returns whether the given Node can have children.
  576. *
  577. * @param parentNode The Node to test
  578. * @return <code>true</code> if the node can have children,
  579. * <code>false</code> otherwise
  580. */
  581. public static boolean canHaveChildren(Node parentNode) {
  582. if (parentNode == null) {
  583. return false;
  584. }
  585. switch (parentNode.getNodeType()) {
  586. case Node.DOCUMENT_NODE:
  587. case Node.TEXT_NODE:
  588. case Node.COMMENT_NODE:
  589. case Node.CDATA_SECTION_NODE:
  590. case Node.PROCESSING_INSTRUCTION_NODE:
  591. return false;
  592. default:
  593. return true;
  594. }
  595. }
  596. /**
  597. * Parses the given XML string into a DocumentFragment of the given document
  598. * or a new document if 'doc' is null.
  599. *
  600. * @param text
  601. * The given XML string
  602. * @param doc
  603. * The given document
  604. * @param uri
  605. * The document URI
  606. * @param prefixes
  607. * The prefixes map with (prefix, namespaceURI) pairs
  608. * @param wrapperElementName
  609. * null: Ignore the wrapper element and prefixes map and try to
  610. * parse the text as a whole document otherwise: Wrap the given
  611. * text with the wrapper element with prefixes specified from the
  612. * prefixes map
  613. * @param documentFactory
  614. * What document factory to use when parsing the text
  615. * @return The document fragment or null on error.
  616. */
  617. public static Node parseXML(String text, Document doc, String uri,
  618. Map prefixes, String wrapperElementName,
  619. SAXDocumentFactory documentFactory) {
  620. // Create the wrapper element prefix and suffix, copying the (prefix,
  621. // namespaceURI) pairs from the prefixes map
  622. String wrapperElementPrefix = "";
  623. String wrapperElementSuffix = "";
  624. if (wrapperElementName != null) {
  625. wrapperElementPrefix = "<" + wrapperElementName;
  626. // Copy the prefixes from the prefixes map to the wrapper element
  627. if (prefixes != null) {
  628. wrapperElementPrefix += " ";
  629. Iterator iter = prefixes.entrySet().iterator();
  630. while (iter.hasNext()) {
  631. Map.Entry e = (Map.Entry) iter.next();
  632. String currentKey = (String) e.getKey();
  633. String currentValue = (String) e.getValue();
  634. wrapperElementPrefix += currentKey + "=\"" + currentValue
  635. + "\" ";
  636. }
  637. }
  638. wrapperElementPrefix += ">";
  639. wrapperElementSuffix += "</" + wrapperElementName + '>';
  640. }
  641. // Try and parse as a whole document, if no wrapper element is specified
  642. if (wrapperElementPrefix.trim().length() == 0
  643. && wrapperElementSuffix.trim().length() == 0) {
  644. try {
  645. Document d = documentFactory.createDocument(uri,
  646. new StringReader(text));
  647. if (doc == null) {
  648. return d;
  649. }
  650. Node result = doc.createDocumentFragment();
  651. result
  652. .appendChild(doc.importNode(d.getDocumentElement(),
  653. true));
  654. return result;
  655. } catch (Exception ex) {
  656. }
  657. }
  658. // Try and parse as a document fragment
  659. StringBuffer sb = new StringBuffer(wrapperElementPrefix.length()
  660. + text.length() + wrapperElementSuffix.length());
  661. sb.append(wrapperElementPrefix);
  662. sb.append(text);
  663. sb.append(wrapperElementSuffix);
  664. String newText = sb.toString();
  665. try {
  666. Document d = documentFactory.createDocument(uri, new StringReader(
  667. newText));
  668. if (doc == null) {
  669. return d;
  670. }
  671. for (Node node = d.getDocumentElement().getFirstChild(); node != null;
  672. node = node.getNextSibling()) {
  673. if (node.getNodeType() == Node.ELEMENT_NODE) {
  674. node = doc.importNode(node, true);
  675. Node result = doc.createDocumentFragment();
  676. result.appendChild(node);
  677. return result;
  678. }
  679. }
  680. } catch (Exception exc) {
  681. }
  682. return null;
  683. }
  684. /**
  685. * Deep clones a document using the given DOM implementation.
  686. */
  687. public static Document deepCloneDocument(Document doc, DOMImplementation impl) {
  688. Element root = doc.getDocumentElement();
  689. Document result = impl.createDocument(root.getNamespaceURI(),
  690. root.getNodeName(),
  691. null);
  692. Element rroot = result.getDocumentElement();
  693. boolean before = true;
  694. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) {
  695. if (n == root) {
  696. before = false;
  697. if (root.hasAttributes()) {
  698. NamedNodeMap attr = root.getAttributes();
  699. int len = attr.getLength();
  700. for (int i = 0; i < len; i++) {
  701. rroot.setAttributeNode((Attr)result.importNode(attr.item(i),
  702. true));
  703. }
  704. }
  705. for (Node c = root.getFirstChild();
  706. c != null;
  707. c = c.getNextSibling()) {
  708. rroot.appendChild(result.importNode(c, true));
  709. }
  710. } else {
  711. if (n.getNodeType() != Node.DOCUMENT_TYPE_NODE) {
  712. if (before) {
  713. result.insertBefore(result.importNode(n, true), rroot);
  714. } else {
  715. result.appendChild(result.importNode(n, true));
  716. }
  717. }
  718. }
  719. }
  720. return result;
  721. }
  722. /**
  723. * Tests whether the given string is a valid name.
  724. */
  725. public static boolean isValidName(String s) {
  726. int len = s.length();
  727. if (len == 0) {
  728. return false;
  729. }
  730. char c = s.charAt(0);
  731. int d = c / 32;
  732. int m = c % 32;
  733. if ((NAME_FIRST_CHARACTER[d] & (1 << m)) == 0) {
  734. return false;
  735. }
  736. for (int i = 1; i < len; i++) {
  737. c = s.charAt(i);
  738. d = c / 32;
  739. m = c % 32;
  740. if ((NAME_CHARACTER[d] & (1 << m)) == 0) {
  741. return false;
  742. }
  743. }
  744. return true;
  745. }
  746. /**
  747. * Tests whether the given string is a valid XML 1.1 name.
  748. */
  749. public static boolean isValidName11(String s) {
  750. int len = s.length();
  751. if (len == 0) {
  752. return false;
  753. }
  754. char c = s.charAt(0);
  755. int d = c / 32;
  756. int m = c % 32;
  757. if ((NAME11_FIRST_CHARACTER[d] & (1 << m)) == 0) {
  758. return false;
  759. }
  760. for (int i = 1; i < len; i++) {
  761. c = s.charAt(i);
  762. d = c / 32;
  763. m = c % 32;
  764. if ((NAME11_CHARACTER[d] & (1 << m)) == 0) {
  765. return false;
  766. }
  767. }
  768. return true;
  769. }
  770. /**
  771. * Tests whether the given string is a valid prefix.
  772. * This method assume that isValidName(s) is true.
  773. */
  774. public static boolean isValidPrefix(String s) {
  775. return s.indexOf(':') == -1;
  776. }
  777. /**
  778. * Gets the prefix from the given qualified name.
  779. * This method assume that isValidName(s) is true.
  780. */
  781. public static String getPrefix(String s) {
  782. int i = s.indexOf(':');
  783. return (i == -1 || i == s.length()-1)
  784. ? null
  785. : s.substring(0, i);
  786. }
  787. /**
  788. * Gets the local name from the given qualified name.
  789. * This method assume that isValidName(s) is true.
  790. */
  791. public static String getLocalName(String s) {
  792. int i = s.indexOf(':');
  793. return (i == -1 || i == s.length()-1)
  794. ? s
  795. : s.substring(i + 1);
  796. }
  797. /**
  798. * Parses a 'xml-stylesheet' processing instruction data section and
  799. * puts the pseudo attributes in the given table.
  800. */
  801. public static void parseStyleSheetPIData(String data, HashTable table) {
  802. // !!! Internationalization
  803. char c;
  804. int i = 0;
  805. // Skip leading whitespaces
  806. while (i < data.length()) {
  807. c = data.charAt(i);
  808. if (!XMLUtilities.isXMLSpace(c)) {
  809. break;
  810. }
  811. i++;
  812. }
  813. while (i < data.length()) {
  814. // Parse the pseudo attribute name
  815. c = data.charAt(i);
  816. int d = c / 32;
  817. int m = c % 32;
  818. if ((NAME_FIRST_CHARACTER[d] & (1 << m)) == 0) {
  819. throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
  820. "Wrong name initial: " + c);
  821. }
  822. StringBuffer ident = new StringBuffer();
  823. ident.append(c);
  824. while (++i < data.length()) {
  825. c = data.charAt(i);
  826. d = c / 32;
  827. m = c % 32;
  828. if ((NAME_CHARACTER[d] & (1 << m)) == 0) {
  829. break;
  830. }
  831. ident.append(c);
  832. }
  833. if (i >= data.length()) {
  834. throw new DOMException(DOMException.SYNTAX_ERR,
  835. "Wrong xml-stylesheet data: " + data);
  836. }
  837. // Skip whitespaces
  838. while (i < data.length()) {
  839. c = data.charAt(i);
  840. if (!XMLUtilities.isXMLSpace(c)) {
  841. break;
  842. }
  843. i++;
  844. }
  845. if (i >= data.length()) {
  846. throw new DOMException(DOMException.SYNTAX_ERR,
  847. "Wrong xml-stylesheet data: " + data);
  848. }
  849. // The next char must be '='
  850. if (data.charAt(i) != '=') {
  851. throw new DOMException(DOMException.SYNTAX_ERR,
  852. "Wrong xml-stylesheet data: " + data);
  853. }
  854. i++;
  855. // Skip whitespaces
  856. while (i < data.length()) {
  857. c = data.charAt(i);
  858. if (!XMLUtilities.isXMLSpace(c)) {
  859. break;
  860. }
  861. i++;
  862. }
  863. if (i >= data.length()) {
  864. throw new DOMException(DOMException.SYNTAX_ERR,
  865. "Wrong xml-stylesheet data: " + data);
  866. }
  867. // The next char must be '\'' or '"'
  868. c = data.charAt(i);
  869. i++;
  870. StringBuffer value = new StringBuffer();
  871. if (c == '\'') {
  872. while (i < data.length()) {
  873. c = data.charAt(i);
  874. if (c == '\'') {
  875. break;
  876. }
  877. value.append(c);
  878. i++;
  879. }
  880. if (i >= data.length()) {
  881. throw new DOMException(DOMException.SYNTAX_ERR,
  882. "Wrong xml-stylesheet data: " +
  883. data);
  884. }
  885. } else if (c == '"') {
  886. while (i < data.length()) {
  887. c = data.charAt(i);
  888. if (c == '"') {
  889. break;
  890. }
  891. value.append(c);
  892. i++;
  893. }
  894. if (i >= data.length()) {
  895. throw new DOMException(DOMException.SYNTAX_ERR,
  896. "Wrong xml-stylesheet data: " +
  897. data);
  898. }
  899. } else {
  900. throw new DOMException(DOMException.SYNTAX_ERR,
  901. "Wrong xml-stylesheet data: " + data);
  902. }
  903. table.put(ident.toString().intern(), value.toString());
  904. i++;
  905. // Skip whitespaces
  906. while (i < data.length()) {
  907. c = data.charAt(i);
  908. if (!XMLUtilities.isXMLSpace(c)) {
  909. break;
  910. }
  911. i++;
  912. }
  913. }
  914. }
  915. /**
  916. * String constants representing DOM modifier strings for various all
  917. * key lock combinations.
  918. */
  919. protected static final String[] LOCK_STRINGS = {
  920. "",
  921. "CapsLock",
  922. "NumLock",
  923. "NumLock CapsLock",
  924. "Scroll",
  925. "Scroll CapsLock",
  926. "Scroll NumLock",
  927. "Scroll NumLock CapsLock",
  928. "KanaMode",
  929. "KanaMode CapsLock",
  930. "KanaMode NumLock",
  931. "KanaMode NumLock CapsLock",
  932. "KanaMode Scroll",
  933. "KanaMode Scroll CapsLock",
  934. "KanaMode Scroll NumLock",
  935. "KanaMode Scroll NumLock CapsLock"
  936. };
  937. /**
  938. * String constants representing DOM modifier strings for various all
  939. * shift modifier combinations.
  940. */
  941. protected static final String[] MODIFIER_STRINGS = {
  942. "",
  943. "Shift",
  944. "Control",
  945. "Control Shift",
  946. "Meta",
  947. "Meta Shift",
  948. "Control Meta",
  949. "Control Meta Shift",
  950. "Alt",
  951. "Alt Shift",
  952. "Alt Control",
  953. "Alt Control Shift",
  954. "Alt Meta",
  955. "Alt Meta Shift",
  956. "Alt Control Meta",
  957. "Alt Control Meta Shift",
  958. "AltGraph",
  959. "AltGraph Shift",
  960. "AltGraph Control",
  961. "AltGraph Control Shift",
  962. "AltGraph Meta",
  963. "AltGraph Meta Shift",
  964. "AltGraph Control Meta",
  965. "AltGraph Control Meta Shift",
  966. "Alt AltGraph",
  967. "Alt AltGraph Shift",
  968. "Alt AltGraph Control",
  969. "Alt AltGraph Control Shift",
  970. "Alt AltGraph Meta",
  971. "Alt AltGraph Meta Shift",
  972. "Alt AltGraph Control Meta",
  973. "Alt AltGraph Control Meta Shift"
  974. };
  975. /**
  976. * Gets a DOM 3 modifiers string from the given lock and
  977. * shift bitmasks.
  978. */
  979. public static String getModifiersList(int lockState, int modifiersEx) {
  980. if ((modifiersEx & (1 << 13)) != 0) {
  981. modifiersEx = 0x10 | ((modifiersEx >> 6) & 0x0f);
  982. } else {
  983. modifiersEx = (modifiersEx >> 6) & 0x0f;
  984. }
  985. String s = LOCK_STRINGS[lockState & 0x0f];
  986. if (s.length() != 0) {
  987. return s + ' ' + MODIFIER_STRINGS[modifiersEx];
  988. }
  989. return MODIFIER_STRINGS[modifiersEx];
  990. }
  991. /**
  992. * Returns whether the given element has a particular attribute and that
  993. * it exists due to being specified explicitly, rather than being defaulted
  994. * from a DTD.
  995. */
  996. public static boolean isAttributeSpecifiedNS(Element e,
  997. String namespaceURI,
  998. String localName) {
  999. Attr a = e.getAttributeNodeNS(namespaceURI, localName);
  1000. return a != null && a.getSpecified();
  1001. }
  1002. }