Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 974   Methods: 32
NCLOC: 726   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
XOMHandler.java 99.5% 85.9% 100% 90%
coverage coverage
 1    /* Copyright 2002-2006 Elliotte Rusty Harold
 2   
 3    This library is free software; you can redistribute it and/or modify
 4    it under the terms of version 2.1 of the GNU Lesser General Public
 5    License as published by the Free Software Foundation.
 6   
 7    This library is distributed in the hope that it will be useful,
 8    but WITHOUT ANY WARRANTY; without even the implied warranty of
 9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 10    GNU Lesser General Public License for more details.
 11   
 12    You should have received a copy of the GNU Lesser General Public
 13    License along with this library; if not, write to the
 14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 15    Boston, MA 02111-1307 USA
 16   
 17    You can contact Elliotte Rusty Harold by sending e-mail to
 18    elharo@metalab.unc.edu. Please include the word "XOM" in the
 19    subject line. The XOM home page is located at http://www.xom.nu/
 20    */
 21   
 22   
 23    package nu.xom;
 24   
 25    import java.util.ArrayList;
 26   
 27    import org.xml.sax.ContentHandler;
 28    import org.xml.sax.DTDHandler;
 29    import org.xml.sax.Locator;
 30    import org.xml.sax.ext.DeclHandler;
 31    import org.xml.sax.ext.LexicalHandler;
 32   
 33    /**
 34    * @author Elliotte Rusty Harold
 35    * @version 1.2d1
 36    *
 37    */
 38    class XOMHandler
 39    implements ContentHandler, LexicalHandler, DeclHandler, DTDHandler {
 40   
 41    protected Document document;
 42    protected String documentBaseURI;
 43   
 44    // parent is never null. It is the node we're adding children
 45    // to. current corresponds to the most recent startElement()
 46    // method and may be null if we've skipped it (makeElement
 47    // returned null.) If we didn't skip it, then parent and
 48    // current should be the same node.
 49    protected ParentNode parent;
 50    protected ParentNode current;
 51    protected ArrayList parents;
 52    protected boolean inProlog;
 53    protected boolean inDTD;
 54    protected int position; // current number of items in prolog
 55    protected Locator locator;
 56    protected DocType doctype;
 57    protected StringBuffer internalDTDSubset;
 58    protected NodeFactory factory;
 59    boolean usingCrimson = false;
 60   
 61   
 62  1164 XOMHandler(NodeFactory factory) {
 63  1164 this.factory = factory;
 64    }
 65   
 66   
 67  22256 public void setDocumentLocator(Locator locator) {
 68  22256 this.locator = locator;
 69    }
 70   
 71   
 72  21833 Document getDocument() {
 73  21833 return document;
 74    }
 75   
 76   
 77    // See http://www.servlets.com/archive/servlet/ReadMsg?msgId=554071&listName=jdom-interest
 78    // This method is called to avoid leaking document sized leaking memory
 79    // when a Builder is not imediately reused
 80  22270 void freeMemory() {
 81  22270 document = null;
 82  22270 parent = null;
 83  22270 current = null;
 84  22270 parents = null;
 85  22270 locator = null;
 86  22270 doctype = null;
 87  22270 internalDTDSubset = null;
 88    }
 89   
 90   
 91  22260 public void startDocument() {
 92   
 93  22260 inDTD = false;
 94  22260 document = factory.startMakingDocument();
 95  22260 parent = document;
 96  22260 current = document;
 97  22260 parents = new ArrayList();
 98  22260 parents.add(document);
 99  22260 inProlog = true;
 100  22260 position = 0;
 101  22260 textString = null;
 102  22260 doctype = null;
 103  22260 if (locator != null) {
 104  22256 documentBaseURI = locator.getSystemId();
 105    // According to the XML spec,
 106    // "It is an error for a fragment identifier
 107    // (beginning with a # character) to be part of a system identifier"
 108    // but some parsers including Xerces seem to get this wrong, so we'll
 109  22256 document.setBaseURI(documentBaseURI);
 110    }
 111  22259 buffer = null;
 112   
 113    }
 114   
 115   
 116  21858 public void endDocument() {
 117  21858 factory.finishMakingDocument(document);
 118  21858 parents.remove(parents.size()-1);
 119    }
 120   
 121   
 122  335 public void startElement(String namespaceURI, String localName,
 123    String qualifiedName, org.xml.sax.Attributes attributes) {
 124   
 125  335 flushText();
 126  335 Element element;
 127  335 if (parent != document) {
 128  224 element = factory.startMakingElement(qualifiedName, namespaceURI);
 129    }
 130    else { // root
 131  111 element = factory.makeRootElement(qualifiedName, namespaceURI);
 132  110 if (element == null) { // null root; that's a no-no
 133  1 throw new NullPointerException(
 134    "Factory failed to create root element."
 135    );
 136    }
 137  109 document.setRootElement(element);
 138  109 inProlog = false;
 139    }
 140   
 141  333 current = element;
 142    // Need to push this, even if it's null
 143  333 parents.add(element);
 144   
 145  333 if (element != null) { // wasn't filtered out
 146  328 if (parent != document) {
 147    // a.k.a. parent not instanceof Document
 148  219 parent.appendChild(element);
 149    }
 150    // This is optimized for the very common case where
 151    // everything in the document has the same actual base URI.
 152    // It may add redundant base URIs in cases like XInclude
 153    // where different parts of the document have different
 154    // base URIs.
 155  326 if (locator != null) {
 156  319 String baseURI = locator.getSystemId();
 157  319 if (baseURI != null && !baseURI.equals(documentBaseURI)) {
 158  1 element.setActualBaseURI(baseURI);
 159    }
 160    }
 161   
 162    // Attach the attributes; this must be done before the
 163    // namespaces are attached.
 164    // XXX pull out length
 165   
 166    // XXX we've got a pretty good guess at how many attributes there
 167    // will be here; we should ensureCapacity up to that length
 168  326 for (int i = 0; i < attributes.getLength(); i++) {
 169  156 String qName = attributes.getQName(i);
 170  156 if (qName.startsWith("xmlns:") || qName.equals("xmlns")) {
 171  20 continue;
 172    }
 173    else {
 174  136 String namespace = attributes.getURI(i);
 175  136 String value = attributes.getValue(i);
 176  136 Nodes nodes = factory.makeAttribute(
 177    qName,
 178    namespace,
 179    value,
 180    convertStringToType(attributes.getType(i))
 181    );
 182  136 int numberChildren = 0;
 183  136 for (int j=0; j < nodes.size(); j++) {
 184  135 Node node = nodes.get(j);
 185  135 if (node.isAttribute()) {
 186  133 factory.addAttribute(element, (Attribute) node);
 187    }
 188    else {
 189  2 factory.insertChild(element, node, numberChildren++);
 190    }
 191    }
 192    }
 193    }
 194   
 195    // Attach the namespaces
 196  325 for (int i = 0; i < attributes.getLength(); i++) {
 197  154 String qName = attributes.getQName(i);
 198  154 if (qName.startsWith("xmlns:")) {
 199  14 String namespaceName = attributes.getValue(i);
 200  14 String namespacePrefix = qName.substring(6);
 201  14 String currentValue
 202    = element.getNamespaceURI(namespacePrefix);
 203  14 if (!namespaceName.equals(currentValue) && ! namespacePrefix.equals(element.getNamespacePrefix())) {
 204  8 element.addNamespaceDeclaration(
 205    namespacePrefix, namespaceName);
 206    }
 207    }
 208  140 else if (qName.equals("xmlns")) {
 209  6 String namespaceName = attributes.getValue(i);
 210  6 String namespacePrefix = "";
 211  6 String currentValue
 212    = element.getNamespaceURI(namespacePrefix);
 213  6 if (!namespaceName.equals(currentValue) && ! "".equals(element.getNamespacePrefix())) {
 214  1 element.addNamespaceDeclaration(namespacePrefix,
 215    namespaceName);
 216    }
 217    }
 218    }
 219   
 220    // this is the new parent
 221  325 parent = element;
 222    }
 223   
 224    }
 225   
 226   
 227  323 public void endElement(
 228    String namespaceURI, String localName, String qualifiedName) {
 229   
 230    // If we're immediately inside a skipped element
 231    // we need to reset current to null, not to the parent
 232  323 current = (ParentNode) parents.remove(parents.size()-1);
 233  323 flushText();
 234   
 235  323 if (current != null) {
 236  318 parent = current.getParent();
 237  318 Nodes result = factory.finishMakingElement((Element) current);
 238   
 239    // Optimization for default case where result only contains current
 240  317 if (result.size() != 1 || result.get(0) != current) {
 241  19 if (!parent.isDocument()) {
 242    // allow factories to detach the element itself in
 243    // finishMakingElement
 244  13 int childCount = parent.getChildCount();
 245  13 try {
 246  13 parent.removeChild(childCount - 1);
 247    }
 248    catch (IndexOutOfBoundsException ex) {
 249  1 throw new XMLException(
 250    "Factory detached element in finishMakingElement()",
 251    ex);
 252    }
 253  12 for (int i=0; i < result.size(); i++) {
 254  15 Node node = result.get(i);
 255  15 if (node.isAttribute()) {
 256  4 ((Element) parent).addAttribute((Attribute) node);
 257    }
 258    else {
 259  11 parent.appendChild(node);
 260    }
 261    }
 262    }
 263    else { // root element
 264  6 Document doc = (Document) parent;
 265  6 Element currentRoot = doc.getRootElement();
 266  6 boolean beforeRoot = true;
 267  6 for (int i=0; i < result.size(); i++) {
 268  11 Node node = result.get(i);
 269  11 if (node.isElement()) {
 270  4 if (node != currentRoot) {
 271  3 if (!beforeRoot) {
 272    // already set root, oops
 273  1 throw new IllegalAddException("Factory returned multiple roots");
 274    }
 275  2 doc.setRootElement((Element) node);
 276    }
 277  3 beforeRoot = false;
 278    }
 279  7 else if (beforeRoot) {
 280  5 doc.insertChild(node, doc.indexOf(doc.getRootElement()));
 281    }
 282    else {
 283  2 doc.appendChild(node);
 284    }
 285    }
 286  4 if (beforeRoot) {
 287    // somebody tried to replace the root element with
 288    // no element at all. That's a no-no
 289  2 throw new WellformednessException(
 290    "Factory attempted to remove the root element");
 291    }
 292    }
 293    }
 294    }
 295   
 296    }
 297   
 298   
 299  2150428 static Attribute.Type convertStringToType(String saxType) {
 300   
 301  2104193 if (saxType.equals("CDATA")) return Attribute.Type.CDATA;
 302  12487 if (saxType.equals("ID")) return Attribute.Type.ID;
 303  14054 if (saxType.equals("IDREF")) return Attribute.Type.IDREF;
 304  85 if (saxType.equals("IDREFS")) return Attribute.Type.IDREFS;
 305  19287 if (saxType.equals("NMTOKEN")) return Attribute.Type.NMTOKEN;
 306  113 if (saxType.equals("NMTOKENS")) return Attribute.Type.NMTOKENS;
 307  86 if (saxType.equals("ENTITY")) return Attribute.Type.ENTITY;
 308  65 if (saxType.equals("ENTITIES")) return Attribute.Type.ENTITIES;
 309  37 if (saxType.equals("NOTATION")) return Attribute.Type.NOTATION;
 310   
 311    // non-standard but some parsers use this
 312  21 if (saxType.equals("ENUMERATION")) {
 313  3 return Attribute.Type.ENUMERATION;
 314    }
 315  9 if (saxType.startsWith("(")) return Attribute.Type.ENUMERATION;
 316   
 317  9 return Attribute.Type.UNDECLARED;
 318   
 319    }
 320   
 321   
 322    protected String textString = null;
 323    protected StringBuffer buffer = null;
 324   
 325  2508774 public void characters(char[] text, int start, int length) {
 326   
 327  2 if (length <= 0) return;
 328  2389999 if (textString == null) textString = new String(text, start, length);
 329    else {
 330  52327 if (buffer == null) buffer = new StringBuffer(textString);
 331  118773 buffer.append(text, start, length);
 332    }
 333  107 if (finishedCDATA) inCDATA = false;
 334   
 335    }
 336   
 337   
 338    // accumulate all text that's in the buffer into a text node
 339  683 private void flushText() {
 340   
 341  683 if (buffer != null) {
 342  35 textString = buffer.toString();
 343  35 buffer = null;
 344    }
 345   
 346  683 if (textString != null) {
 347  442 Nodes result;
 348  442 if (!inCDATA) {
 349  441 result = factory.makeText(textString);
 350    }
 351    else {
 352  1 result = factory.makeCDATASection(textString);
 353    }
 354  442 for (int i=0; i < result.size(); i++) {
 355  224 Node node = result.get(i);
 356  224 if (node.isAttribute()) {
 357  2 ((Element) parent).addAttribute((Attribute) node);
 358    }
 359    else {
 360  222 parent.appendChild(node);
 361    }
 362    }
 363  442 textString = null;
 364    }
 365  683 inCDATA = false;
 366  683 finishedCDATA = false;
 367   
 368    }
 369   
 370   
 371  58725 public void ignorableWhitespace(
 372    char[] text, int start, int length) {
 373  58725 characters(text, start, length);
 374    }
 375   
 376   
 377  17 public void processingInstruction(String target, String data) {
 378   
 379  12 if (!inDTD) flushText();
 380  2 if (inDTD && !inInternalSubset()) return;
 381  15 Nodes result = factory.makeProcessingInstruction(target, data);
 382   
 383  12 for (int i = 0; i < result.size(); i++) {
 384  9 Node node = result.get(i);
 385  9 if (!inDTD) {
 386  6 if (inProlog) {
 387  4 parent.insertChild(node, position);
 388  4 position++;
 389    }
 390    else {
 391  2 if (node.isAttribute()) {
 392  1 ((Element) parent).addAttribute((Attribute) node);
 393    }
 394  1 else parent.appendChild(node);
 395    }
 396    }
 397    else {
 398  3 if (node.isProcessingInstruction() || node.isComment()) {
 399  2 internalDTDSubset.append(" ");
 400  2 internalDTDSubset.append(node.toXML());
 401  2 internalDTDSubset.append("\n");
 402    }
 403    else {
 404  1 throw new XMLException("Factory tried to put a "
 405    + node.getClass().getName()
 406    + " in the internal DTD subset");
 407    }
 408    }
 409    }
 410   
 411    }
 412   
 413   
 414    // XOM handles this with attribute values; not prefix mappings
 415  2520 public void startPrefixMapping(String prefix, String uri) {}
 416  2031 public void endPrefixMapping(String prefix) {}
 417   
 418  22 public void skippedEntity(String name) {
 419   
 420    // Xerces 2.7 now calls this method in the DTD
 421    // for parameter entities it doesn't resolve. We can ignore these.
 422  21 if (name.startsWith("%")) return;
 423  1 flushText();
 424  1 throw new XMLException("Could not resolve entity " + name);
 425   
 426    }
 427   
 428   
 429    // LexicalHandler events
 430  13 public void startDTD(String rootName, String publicID,
 431    String systemID) {
 432   
 433  13 inDTD = true;
 434  13 Nodes result = factory.makeDocType(rootName, publicID, systemID);
 435  13 for (int i = 0; i < result.size(); i++) {
 436  13 Node node = result.get(i);
 437  13 document.insertChild(node, position);
 438  12 position++;
 439  12 if (node.isDocType()) {
 440  11 DocType doctype = (DocType) node;
 441  11 internalDTDSubset = new StringBuffer();
 442  11 this.doctype = doctype;
 443    }
 444    }
 445   
 446    }
 447   
 448   
 449  2511 public void endDTD() {
 450   
 451  2511 inDTD = false;
 452  2511 if (doctype != null) {
 453  2509 doctype.fastSetInternalDTDSubset(internalDTDSubset.toString());
 454    }
 455   
 456    }
 457   
 458   
 459    protected boolean inExternalSubset = false;
 460   
 461    // We have a problem here. Xerces gets this right,
 462    // but Crimson and possibly other parsers don't properly
 463    // report these entities, or perhaps just not tag them
 464    // with [dtd] like they're supposed to.
 465  22533 public void startEntity(String name) {
 466  747 if (name.equals("[dtd]")) inExternalSubset = true;
 467    }
 468   
 469   
 470  22521 public void endEntity(String name) {
 471  747 if (name.equals("[dtd]")) inExternalSubset = false;
 472    }
 473   
 474   
 475    protected boolean inCDATA = false;
 476    protected boolean finishedCDATA = false;
 477   
 478  620 public void startCDATA() {
 479  520 if (textString == null) inCDATA = true;
 480  620 finishedCDATA = false;
 481    }
 482   
 483   
 484  620 public void endCDATA() {
 485  620 finishedCDATA = true;
 486    }
 487   
 488   
 489  17 public void comment(char[] text, int start, int length) {
 490   
 491  12 if (!inDTD) flushText();
 492  2 if (inDTD && !inInternalSubset()) return;
 493   
 494  15 Nodes result = factory.makeComment(new String(text, start, length));
 495   
 496  15 for (int i = 0; i < result.size(); i++) {
 497  12 Node node = result.get(i);
 498  12 if (!inDTD) {
 499  9 if (inProlog) {
 500  4 parent.insertChild(node, position);
 501  3 position++;
 502    }
 503    else {
 504  5 if (node instanceof Attribute) {
 505  1 ((Element) parent).addAttribute((Attribute) node);
 506    }
 507  4 else parent.appendChild(node);
 508    }
 509    }
 510    else {
 511  3 if (node.isComment() || node.isProcessingInstruction()) {
 512  2 internalDTDSubset.append(" ");
 513  2 internalDTDSubset.append(node.toXML());
 514  2 internalDTDSubset.append("\n");
 515    }
 516    else {
 517  1 throw new XMLException("Factory tried to put a "
 518    + node.getClass().getName()
 519    + " in the internal DTD subset");
 520    }
 521    }
 522    }
 523   
 524    }
 525   
 526   
 527  9685 public void elementDecl(String name, String model) {
 528   
 529  9685 if (inInternalSubset() && doctype != null) {
 530  3042 internalDTDSubset.append(" <!ELEMENT ");
 531  3042 internalDTDSubset.append(name);
 532  3042 internalDTDSubset.append(' ');
 533  3042 internalDTDSubset.append(model);
 534    // workaround for Crimson bug
 535  3042 if (model.indexOf("#PCDATA") > 0 && model.indexOf('|') > 0) {
 536  152 if (model.endsWith(")")) {
 537  1 internalDTDSubset.append('*');
 538    }
 539    }
 540  3042 internalDTDSubset.append(">\n");
 541    }
 542   
 543    }
 544   
 545   
 546    // This method only behaves properly when called from the DeclHandler
 547    // and DTDHandler callbacks; i.e. from inside the DTD;
 548    // It is not intended for use anywhere in the document.
 549  40693 protected boolean inInternalSubset() {
 550   
 551  9614 if (!usingCrimson && !inExternalSubset) return true;
 552  31079 String currentURI = locator.getSystemId();
 553  37 if (currentURI == this.documentBaseURI) return true;
 554  0 if (currentURI.equals(this.documentBaseURI)) return true;
 555  31042 return false;
 556   
 557    }
 558   
 559   
 560  15677 public void attributeDecl(String elementName,
 561    String attributeName, String type, String mode,
 562    String defaultValue) {
 563   
 564    // workaround for Crimson bug
 565  15677 if (type.startsWith("NOTATION ")) {
 566  85 if (type.indexOf('(') == -1 && ! type.endsWith(")")) {
 567  2 type = "NOTATION (" + type.substring("NOTATION ".length()) + ")";
 568    }
 569    }
 570   
 571  15677 if (inInternalSubset() && doctype != null) {
 572  1791 internalDTDSubset.append(" <!ATTLIST ");
 573  1791 internalDTDSubset.append(elementName);
 574  1791 internalDTDSubset.append(' ');
 575  1791 internalDTDSubset.append(attributeName);
 576  1791 internalDTDSubset.append(' ');
 577  1791 internalDTDSubset.append(type);
 578  1791 if (mode != null) {
 579  1385 internalDTDSubset.append(' ');
 580  1385 internalDTDSubset.append(mode);
 581    }
 582  1791 if (defaultValue != null) {
 583  519 internalDTDSubset.append(' ');
 584  519 internalDTDSubset.append('"');
 585  519 internalDTDSubset.append(escapeReservedCharactersInDefaultAttributeValues(defaultValue));
 586  519 internalDTDSubset.append("\"");
 587    }
 588  1791 internalDTDSubset.append(">\n");
 589    }
 590   
 591    }
 592   
 593   
 594  7079 public void internalEntityDecl(String name,
 595    String value) {
 596   
 597  7079 if (inInternalSubset() && doctype != null) {
 598  2945 internalDTDSubset.append(" <!ENTITY ");
 599  2945 if (name.startsWith("%")) {
 600  125 internalDTDSubset.append("% ");
 601  125 internalDTDSubset.append(name.substring(1));
 602    }
 603    else {
 604  2820 internalDTDSubset.append(name);
 605    }
 606  2945 internalDTDSubset.append(" \"");
 607  2945 internalDTDSubset.append(escapeReservedCharactersInDeclarations(value));
 608  2945 internalDTDSubset.append("\">\n");
 609    }
 610   
 611    }
 612   
 613   
 614  337 public void externalEntityDecl(String name,
 615    String publicID, String systemID) {
 616   
 617  337 if (inInternalSubset() && doctype != null) {
 618  292 internalDTDSubset.append(" <!ENTITY ");
 619  292 if (name.startsWith("%")) {
 620  62 internalDTDSubset.append("% ");
 621  62 internalDTDSubset.append(name.substring(1));
 622    }
 623    else {
 624  230 internalDTDSubset.append(name);
 625    }
 626   
 627  292 if (locator != null && URIUtil.isAbsolute(systemID)) {
 628  290 String documentURL = locator.getSystemId();
 629    // work around Crimson style file:/root URLs
 630  290 if (documentURL != null) {
 631  259 if (documentURL.startsWith("file:/") && !documentURL.startsWith("file:///")) {
 632  1 documentURL = "file://" + documentURL.substring(5);
 633    }
 634  259 if (systemID.startsWith("file:/") && !systemID.startsWith("file:///")) {
 635  1 systemID = "file://" + systemID.substring(5);
 636    }
 637  259 systemID = URIUtil.relativize(documentURL, systemID);
 638    }
 639    }
 640   
 641  292 if (publicID != null) {
 642  46 internalDTDSubset.append(" PUBLIC \"");
 643  46 internalDTDSubset.append(publicID);
 644  46 internalDTDSubset.append("\" \"");
 645  46 internalDTDSubset.append(systemID);
 646    }
 647    else {
 648    // need to escape system ID????
 649  246 internalDTDSubset.append(" SYSTEM \"");
 650  246 internalDTDSubset.append(systemID);
 651    }
 652  292 internalDTDSubset.append("\">\n");
 653   
 654    }
 655   
 656    }
 657   
 658   
 659  604 public void notationDecl(String name, String publicID,
 660    String systemID) {
 661   
 662  604 if (systemID != null) {
 663  502 systemID = escapeReservedCharactersInDeclarations(systemID);
 664    }
 665   
 666  604 if (inInternalSubset() && doctype != null) {
 667  294 internalDTDSubset.append(" <!NOTATION ");
 668  294 internalDTDSubset.append(name);
 669  294 if (publicID != null) {
 670  118 internalDTDSubset.append(" PUBLIC \"");
 671  118 internalDTDSubset.append(publicID);
 672  118 internalDTDSubset.append('"');
 673  118 if (systemID != null) {
 674  26 internalDTDSubset.append(" \"");
 675  26 internalDTDSubset.append(systemID);
 676  26 internalDTDSubset.append('"');
 677    }
 678    }
 679    else {
 680  176 internalDTDSubset.append(" SYSTEM \"");
 681  176 internalDTDSubset.append(systemID);
 682  176 internalDTDSubset.append('"');
 683    }
 684  294 internalDTDSubset.append(">\n");
 685    }
 686   
 687    }
 688   
 689   
 690  352 public void unparsedEntityDecl(String name, String publicID,
 691    String systemID, String notationName) {
 692   
 693    // escapable characters????
 694  352 if (inInternalSubset() && doctype != null) {
 695  148 internalDTDSubset.append(" <!ENTITY ");
 696  148 if (publicID != null) {
 697  26 internalDTDSubset.append(name);
 698  26 internalDTDSubset.append(" PUBLIC \"");
 699  26 internalDTDSubset.append(publicID);
 700  26 internalDTDSubset.append("\" \"");
 701  26 internalDTDSubset.append(systemID);
 702  26 internalDTDSubset.append("\" NDATA ");
 703  26 internalDTDSubset.append(notationName);
 704    }
 705    else {
 706  122 internalDTDSubset.append(name);
 707  122 internalDTDSubset.append(" SYSTEM \"");
 708  122 internalDTDSubset.append(systemID);
 709  122 internalDTDSubset.append("\" NDATA ");
 710  122 internalDTDSubset.append(notationName);
 711    }
 712  148 internalDTDSubset.append(">\n");
 713    }
 714   
 715    }
 716   
 717   
 718  3447 private static String escapeReservedCharactersInDeclarations(String s) {
 719   
 720  3447 int length = s.length();
 721  3447 StringBuffer result = new StringBuffer(length);
 722  3447 for (int i = 0; i < length; i++) {
 723  36758 char c = s.charAt(i);
 724  36758 switch (c) {
 725  9 case '\r':
 726  9 result.append("&#x0D;");
 727  9 break;
 728  0 case 14:
 729    // placeholder for table lookup
 730  0 break;
 731  0 case 15:
 732    // placeholder for table lookup
 733  0 break;
 734  0 case 16 :
 735    // placeholder for table lookup
 736  0 break;
 737  0 case 17:
 738    // placeholder for table lookup
 739  0 break;
 740  0 case 18:
 741    // placeholder for table lookup
 742  0 break;
 743  0 case 19:
 744    // placeholder for table lookup
 745  0 break;
 746  0 case 20:
 747    // placeholder for table lookup
 748  0 break;
 749  0 case 21:
 750    // placeholder for table lookup
 751  0 break;
 752  0 case 22:
 753    // placeholder for table lookup
 754  0 break;
 755  0 case 23:
 756    // placeholder for table lookup
 757  0 break;
 758  0 case 24:
 759    // placeholder for table lookup
 760  0 break;
 761  0 case 25:
 762    // placeholder for table lookup
 763  0 break;
 764  0 case 26:
 765    // placeholder for table lookup
 766  0 break;
 767  0 case 27:
 768    // placeholder for table lookup
 769  0 break;
 770  0 case 28:
 771    // placeholder for table lookup
 772  0 break;
 773  0 case 29:
 774    // placeholder for table lookup
 775  0 break;
 776  0 case 30:
 777    // placeholder for table lookup
 778  0 break;
 779  0 case 31:
 780    // placeholder for table lookup
 781  0 break;
 782  639 case ' ':
 783  639 result.append(' ');
 784  639 break;
 785  99 case '!':
 786  99 result.append('!');
 787  99 break;
 788  56 case '\"':
 789  56 result.append("&#x22;");
 790  56 break;
 791  95 case '#':
 792  95 result.append('#');
 793  95 break;
 794  2 case '$':
 795  2 result.append('$');
 796  2 break;
 797  9 case '%':
 798  9 result.append("&#x25;");
 799  9 break;
 800  180 case '&':
 801  180 result.append("&#x26;");
 802  180 break;
 803  35669 default:
 804  35669 result.append(c);
 805    }
 806    }
 807   
 808  3447 return result.toString();
 809   
 810    }
 811   
 812   
 813  519 private static String escapeReservedCharactersInDefaultAttributeValues(String s) {
 814   
 815  519 int length = s.length();
 816  519 StringBuffer result = new StringBuffer(length);
 817  519 for (int i = 0; i < length; i++) {
 818  4120 char c = s.charAt(i);
 819  4120 switch (c) {
 820  1 case '\r':
 821  1 result.append("&#x0D;");
 822  1 break;
 823  0 case 14:
 824    // placeholder for table lookup
 825  0 break;
 826  0 case 15:
 827    // placeholder for table lookup
 828  0 break;
 829  0 case 16 :
 830    // placeholder for table lookup
 831  0 break;
 832  0 case 17:
 833    // placeholder for table lookup
 834  0 break;
 835  0 case 18:
 836    // placeholder for table lookup
 837  0 break;
 838  0 case 19:
 839    // placeholder for table lookup
 840  0 break;
 841  0 case 20:
 842    // placeholder for table lookup
 843  0 break;
 844  0 case 21:
 845    // placeholder for table lookup
 846  0 break;
 847  0 case 22:
 848    // placeholder for table lookup
 849  0 break;
 850  0 case 23:
 851    // placeholder for table lookup
 852  0 break;
 853  0 case 24:
 854    // placeholder for table lookup
 855  0 break;
 856  0 case 25:
 857    // placeholder for table lookup
 858  0 break;
 859  0 case 26:
 860    // placeholder for table lookup
 861  0 break;
 862  0 case 27:
 863    // placeholder for table lookup
 864  0 break;
 865  0 case 28:
 866    // placeholder for table lookup
 867  0 break;
 868  0 case 29:
 869    // placeholder for table lookup
 870  0 break;
 871  0 case 30:
 872    // placeholder for table lookup
 873  0 break;
 874  0 case 31:
 875    // placeholder for table lookup
 876  0 break;
 877  82 case ' ':
 878  82 result.append(' ');
 879  82 break;
 880  1 case '!':
 881  1 result.append('!');
 882  1 break;
 883  1 case '\"':
 884  1 result.append("&quot;");
 885  1 break;
 886  4 case '#':
 887  4 result.append('#');
 888  4 break;
 889  8 case '$':
 890  8 result.append('$');
 891  8 break;
 892  4 case '%':
 893  4 result.append("&#x25;");
 894  4 break;
 895  1 case '&':
 896  1 result.append("&amp;");
 897  1 break;
 898  1 case '\'':
 899  1 result.append('\'');
 900  1 break;
 901  1 case '(':
 902  1 result.append('(');
 903  1 break;
 904  1 case ')':
 905  1 result.append(')');
 906  1 break;
 907  1 case '*':
 908  1 result.append('*');
 909  1 break;
 910  1 case '+':
 911  1 result.append('+');
 912  1 break;
 913  1 case ',':
 914  1 result.append(',');
 915  1 break;
 916  21 case '-':
 917  21 result.append('-');
 918  21 break;
 919  111 case '.':
 920  111 result.append('.');
 921  111 break;
 922  244 case '/':
 923  244 result.append('/');
 924  244 break;
 925  19 case '0':
 926  19 result.append('0');
 927  19 break;
 928  161 case '1':
 929  161 result.append('1');
 930  161 break;
 931  74 case '2':
 932  74 result.append('2');
 933  74 break;
 934  64 case '3':
 935  64 result.append('3');
 936  64 break;
 937  10 case '4':
 938  10 result.append('4');
 939  10 break;
 940  1 case '5':
 941  1 result.append('5');
 942  1 break;
 943  1 case '6':
 944  1 result.append('6');
 945  1 break;
 946  1 case '7':
 947  1 result.append('7');
 948  1 break;
 949  1 case '8':
 950  1 result.append('8');
 951  1 break;
 952  136 case '9':
 953  136 result.append('9');
 954  136 break;
 955  49 case ':':
 956  49 result.append(':');
 957  49 break;
 958  4 case ';':
 959  4 result.append(';');
 960  4 break;
 961  1 case '<':
 962  1 result.append("&lt;");
 963  1 break;
 964  3114 default:
 965  3114 result.append(c);
 966    }
 967    }
 968   
 969  519 return result.toString();
 970   
 971    }
 972   
 973   
 974    }