~ubuntu-branches/ubuntu/precise/xom/precise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
/* Copyright 2002-2005 Elliotte Rusty Harold
   
   This library is free software; you can redistribute it and/or modify
   it under the terms of version 2.1 of the GNU Lesser General Public 
   License as published by the Free Software Foundation.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
   GNU Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the 
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
   Boston, MA 02111-1307  USA
   
   You can contact Elliotte Rusty Harold by sending e-mail to
   elharo@metalab.unc.edu. Please include the word "XOM" in the
   subject line. The XOM home page is located at http://www.xom.nu/
*/


package nu.xom;

import java.util.ArrayList;

import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.Locator;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;

/**
 * @author Elliotte Rusty Harold
 * @version 1.1b7
 *
 */
class XOMHandler 
  implements ContentHandler, LexicalHandler, DeclHandler, DTDHandler {

    protected Document     document;
    protected String       documentBaseURI;
    
    // parent is never null. It is the node we're adding children 
    // to. current corresponds to the most recent startElement()
    // method and may be null if we've skipped it (makeElement
    // returned null.) If we didn't skip it, then parent and
    // current should be the same node.
    protected ParentNode   parent;
    protected ParentNode   current;
    protected ArrayList    parents;
    protected boolean      inProlog;
    protected boolean      inDTD;
    protected int          position; // current number of items in prolog
    protected Locator      locator; 
    protected DocType      doctype;
    protected StringBuffer internalDTDSubset;
    protected NodeFactory  factory;
              boolean      usingCrimson = false;
    
    
    XOMHandler(NodeFactory factory) {
        this.factory = factory; 
    }   
    
    
    public void setDocumentLocator(Locator locator) {
        this.locator = locator;
    }

    
    Document getDocument() {
        return document;
    }


    // See http://www.servlets.com/archive/servlet/ReadMsg?msgId=554071&listName=jdom-interest
    // This method is called to avoid leaking document sized leaking memory
    // when a Builder is not imediately reused
    void freeMemory() {
        document = null;
        parent = null;
        current = null;
        parents = null;
        locator = null;
        doctype = null;
        internalDTDSubset = null;
    }

    
    public void startDocument() {
        
        inDTD = false;
        document = factory.startMakingDocument();
        parent = document;
        current = document;
        parents = new ArrayList();
        parents.add(document);
        inProlog = true;
        position = 0;
        textString = null;
        doctype = null;
        if (locator != null) {
            documentBaseURI = locator.getSystemId();
            // According to the XML spec, 
            // "It is an error for a fragment identifier 
            // (beginning with a # character) to be part of a system identifier"
            // but some parsers including Xerces seem to get this wrong, so we'll 
            document.setBaseURI(documentBaseURI);
        }
        buffer = null;
        
    }
  
    
    public void endDocument() {
        factory.finishMakingDocument(document);
        parents.remove(parents.size()-1);
    }
  
    
    public void startElement(String namespaceURI, String localName, 
      String qualifiedName, org.xml.sax.Attributes attributes) {
        
        flushText();
        Element element;
        if (parent != document) {
            element = factory.startMakingElement(qualifiedName, namespaceURI);
        }
        else {  // root
            element = factory.makeRootElement(qualifiedName, namespaceURI);
            if (element == null) { // null root; that's a no-no
                throw new NullPointerException(
                    "Factory failed to create root element."
                );   
            }
            document.setRootElement(element);
            inProlog = false;
        }
        
        current = element;
        // Need to push this, even if it's null 
        parents.add(element);
        
        if (element != null) { // wasn't filtered out
            if (parent != document) { 
                // a.k.a. parent not instanceof Document
                parent.appendChild(element);
            }
            // This is optimized for the very common case where 
            // everything in the document has the same actual base URI. 
            // It may add redundant base URIs in cases like XInclude 
            // where different parts of the document have different 
            // base URIs.
            if (locator != null) {
                 String baseURI = locator.getSystemId();
                 if (baseURI != null && !baseURI.equals(documentBaseURI)) {
                     element.setActualBaseURI(baseURI);
                 }
            }         
            
            // Attach the attributes; this must be done before the
            // namespaces are attached.      
            // XXX pull out length
            
            // XXX we've got a pretty good guess at how many attributes there
            // will be here; we should ensureCapacity up to that length
            for (int i = 0; i < attributes.getLength(); i++) {
                String qName = attributes.getQName(i);
                if (qName.startsWith("xmlns:") || qName.equals("xmlns")) {               
                    continue;               
                }             
                else {
                    String namespace = attributes.getURI(i);
                    String value = attributes.getValue(i);
                    Nodes nodes = factory.makeAttribute(
                      qName, 
                      namespace, 
                      value, 
                      convertStringToType(attributes.getType(i))
                    );
                    int numberChildren = 0;
                    for (int j=0; j < nodes.size(); j++) {
                        Node node = nodes.get(j);
                        if (node.isAttribute()) {
                            factory.addAttribute(element, (Attribute) node);
                        }
                        else {
                            factory.insertChild(element, node, numberChildren++);   
                        }
                    }
                }
            }

            // Attach the namespaces
            for (int i = 0; i < attributes.getLength(); i++) {
                String qName = attributes.getQName(i);
                if (qName.startsWith("xmlns:")) {               
                    String namespaceName = attributes.getValue(i);
                    String namespacePrefix = qName.substring(6);
                    String currentValue
                       = element.getNamespaceURI(namespacePrefix); 
                    if (!namespaceName.equals(currentValue) && ! namespacePrefix.equals(element.getNamespacePrefix())) {
                        element.addNamespaceDeclaration(
                          namespacePrefix, namespaceName);
                    }              
                }   
                else if (qName.equals("xmlns")) {               
                    String namespaceName = attributes.getValue(i);
                    String namespacePrefix = "";
                    String currentValue 
                      = element.getNamespaceURI(namespacePrefix); 
                    if (!namespaceName.equals(currentValue) && ! "".equals(element.getNamespacePrefix())) {
                        element.addNamespaceDeclaration(namespacePrefix, 
                         namespaceName);
                    }                
                }             
            }
            
            // this is the new parent
            parent = element;
        }
        
    }

    
    public void endElement(
      String namespaceURI, String localName, String qualifiedName) {
        
        // If we're immediately inside a skipped element
        // we need to reset current to null, not to the parent
        current = (ParentNode) parents.remove(parents.size()-1);
        flushText();
        
        if (current != null) {
            parent = current.getParent();
            Nodes result = factory.finishMakingElement((Element) current);
            
            // Optimization for default case where result only contains current
            if (result.size() != 1 || result.get(0) != current) {            
                if (!parent.isDocument()) {
                    // allow factories to detach the element itself in
                    // finishMakingElement
                    int childCount = parent.getChildCount();
                    try {
                        parent.removeChild(childCount - 1);
                    }
                    catch (IndexOutOfBoundsException ex) {
                        throw new XMLException(
                          "Factory detached element in finishMakingElement()", 
                          ex);
                    }
                    for (int i=0; i < result.size(); i++) {
                        Node node = result.get(i);
                         if (node.isAttribute()) {
                             ((Element) parent).addAttribute((Attribute) node);
                         }
                         else {
                             parent.appendChild(node);   
                         }
                    }
                }
                else { // root element
                    Document doc = (Document) parent;
                    Element currentRoot = doc.getRootElement();
                    boolean beforeRoot = true;
                    for (int i=0; i < result.size(); i++) {
                        Node node = result.get(i);
                        if (node.isElement()) {
                            if (node != currentRoot) {   
                                if (!beforeRoot) {
                                    // already set root, oops
                                    throw new IllegalAddException("Factory returned multiple roots");   
                                }
                                doc.setRootElement((Element) node);
                            }
                            beforeRoot = false;
                        }
                        else if (beforeRoot) {
                            doc.insertChild(node, doc.indexOf(doc.getRootElement()));   
                        }
                        else {
                            doc.appendChild(node);   
                        }
                    }
                    if (beforeRoot) {
                        // somebody tried to replace the root element with
                        // no element at all. That's a no-no
                        throw new WellformednessException(
                          "Factory attempted to remove the root element");
                    }
                }
            }
        }
        
    }
    
    
    static Attribute.Type convertStringToType(String saxType) {
    
        if (saxType.equals("CDATA"))    return Attribute.Type.CDATA;
        if (saxType.equals("ID"))       return Attribute.Type.ID;
        if (saxType.equals("IDREF"))    return Attribute.Type.IDREF;
        if (saxType.equals("IDREFS"))   return Attribute.Type.IDREFS;
        if (saxType.equals("NMTOKEN"))  return Attribute.Type.NMTOKEN;
        if (saxType.equals("NMTOKENS")) return Attribute.Type.NMTOKENS;
        if (saxType.equals("ENTITY"))   return Attribute.Type.ENTITY;
        if (saxType.equals("ENTITIES")) return Attribute.Type.ENTITIES;
        if (saxType.equals("NOTATION")) return Attribute.Type.NOTATION;
        
        // non-standard but some parsers use this
        if (saxType.equals("ENUMERATION")) {
            return Attribute.Type.ENUMERATION;
        } 
        if (saxType.startsWith("(")) return Attribute.Type.ENUMERATION;
    
        return Attribute.Type.UNDECLARED;
        
    }
  
    
    protected String textString = null;
    protected StringBuffer buffer = null;
  
    public void characters(char[] text, int start, int length) {
        
        if (length <= 0) return;
        if (textString == null) textString = new String(text, start, length);
        else {
            if (buffer == null) buffer = new StringBuffer(textString);
            buffer.append(text, start, length);
        }
        if (finishedCDATA) inCDATA = false;
        
    }
 
    
    // accumulate all text that's in the buffer into a text node
    protected void flushText() {
        
        if (buffer != null) {
            textString = buffer.toString();
            buffer = null;
        }
        
        if (textString != null) {
            Nodes result;
            if (!inCDATA) {
                result = factory.makeText(textString);
            }
            else {
                result = factory.makeCDATASection(textString);
            }
            for (int i=0; i < result.size(); i++) {
                Node node = result.get(i);
                if (node.isAttribute()) {
                    ((Element) parent).addAttribute((Attribute) node);
                }
                else {
                    parent.appendChild(node);   
                }
            }
            textString = null;
        }
        inCDATA = false;
        finishedCDATA = false;
        
    }
  
    
    public void ignorableWhitespace(
      char[] text, int start, int length) {
        characters(text, start, length);
    }
  
    
    public void processingInstruction(String target, String data) {
        
        if (!inDTD) flushText();
        if (inDTD && !inInternalSubset()) return;
        Nodes result = factory.makeProcessingInstruction(target, data);
        
        for (int i = 0; i < result.size(); i++) {
            Node node = result.get(i);
            if (!inDTD) {
                if (inProlog) {
                    parent.insertChild(node, position);
                    position++;
                }
                else {
                    if (node.isAttribute()) {
                        ((Element) parent).addAttribute((Attribute) node);
                    }
                    else parent.appendChild(node);
                }
            }
            else {
                if (node.isProcessingInstruction() || node.isComment()) {
                    internalDTDSubset.append("  ");            
                    internalDTDSubset.append(node.toXML());            
                    internalDTDSubset.append("\n");            
                }
                else {
                    throw new XMLException("Factory tried to put a " 
                      + node.getClass().getName() 
                      + " in the internal DTD subset");   
                }
            }            
        }

    }


    // XOM handles this with attribute values; not prefix mappings
    public void startPrefixMapping(String prefix, String uri) {}
    public void endPrefixMapping(String prefix) {}

    public void skippedEntity(String name) {
        
        // Xerces 2.7 now calls this method in the DTD 
        // for parameter entities it doesn't resolve. We can ignore these.
        if (name.startsWith("%")) return;
        flushText();
        throw new XMLException("Could not resolve entity " + name);
        
    }
    
    
    // LexicalHandler events
    public void startDTD(String rootName, String publicID, 
      String systemID) {
        
        inDTD = true;
        Nodes result = factory.makeDocType(rootName, publicID, systemID);
        for (int i = 0; i < result.size(); i++) {
            Node node = result.get(i);
            document.insertChild(node, position);
            position++;
            if (node.isDocType()) {
                DocType doctype = (DocType) node;
                internalDTDSubset = new StringBuffer(); 
                this.doctype = doctype;
            }
        }
        
    }
     
    
    public void endDTD() {
        
        inDTD = false;
        if (doctype != null) {
            doctype.fastSetInternalDTDSubset(internalDTDSubset.toString());
        }
        
    }

    
    protected boolean inExternalSubset = false;

    // We have a problem here. Xerces gets this right,
    // but Crimson and possibly other parsers don't properly
    // report these entities, or perhaps just not tag them
    // with [dtd] like they're supposed to.
    public void startEntity(String name) {
      if (name.equals("[dtd]")) inExternalSubset = true;
    }
    
    
    public void endEntity(String name) {
      if (name.equals("[dtd]")) inExternalSubset = false;    
    }
    
    
    protected boolean inCDATA = false;
    protected boolean finishedCDATA = false;
    
    public void startCDATA() {
        if (textString == null) inCDATA = true;
        finishedCDATA = false;
    }
    
    
    public void endCDATA() {
        finishedCDATA = true;
    }

    
    public void comment(char[] text, int start, int length) {
        
        if (!inDTD) flushText();
        if (inDTD && !inInternalSubset()) return;

        Nodes result = factory.makeComment(new String(text, start, length));
        
        for (int i = 0; i < result.size(); i++) {
            Node node = result.get(i);
            if (!inDTD) {
                if (inProlog) {
                    parent.insertChild(node, position);
                    position++;
                }
                else {
                    if (node instanceof Attribute) {
                        ((Element) parent).addAttribute((Attribute) node);
                    }
                    else parent.appendChild(node);
                }
            }
            else {
                if (node.isComment() || node.isProcessingInstruction()) {
                    internalDTDSubset.append("  ");            
                    internalDTDSubset.append(node.toXML());            
                    internalDTDSubset.append("\n");            
                }
                else {
                    throw new XMLException("Factory tried to put a " 
                      + node.getClass().getName() 
                      + " in the internal DTD subset");   
                }
            }            
        }

    }    
    
    
    public void elementDecl(String name, String model) {
        
        if (inInternalSubset() && doctype != null) {
            internalDTDSubset.append("  <!ELEMENT ");
            internalDTDSubset.append(name); 
            internalDTDSubset.append(' '); 
            internalDTDSubset.append(model); 
            // workaround for Crimson bug
            if (model.indexOf("#PCDATA") > 0 && model.indexOf('|') > 0) {
                if (model.endsWith(")")) {
                    internalDTDSubset.append('*');  
                }
            }
            internalDTDSubset.append(">\n"); 
        }
        
    }
  
    
    // This method only behaves properly when called from the DeclHandler 
    // and DTDHandler callbacks; i.e. from inside the DTD;
    // It is not intended for use anywhere in the document.
    protected boolean inInternalSubset() {

        if (!usingCrimson && !inExternalSubset) return true;
        String currentURI = locator.getSystemId();
        if (currentURI == this.documentBaseURI) return true;
        if (currentURI.equals(this.documentBaseURI)) return true;
        return false;
        
    }


    public void attributeDecl(String elementName, 
      String attributeName, String type, String mode, 
      String defaultValue)  {
    
        // workaround for Crimson bug
        if (type.startsWith("NOTATION ")) {
            if (type.indexOf('(') == -1 && ! type.endsWith(")")) {
                type = "NOTATION (" + type.substring("NOTATION ".length()) + ")";
            }
        }
        
        if (inInternalSubset() && doctype != null) {
            internalDTDSubset.append("  <!ATTLIST ");
            internalDTDSubset.append(elementName);
            internalDTDSubset.append(' ');
            internalDTDSubset.append(attributeName);
            internalDTDSubset.append(' ');
            internalDTDSubset.append(type);
            if (mode != null) {
            internalDTDSubset.append(' ');
                internalDTDSubset.append(mode);
            }
            if (defaultValue != null) {
                internalDTDSubset.append(' ');
                internalDTDSubset.append('"');
                internalDTDSubset.append(escapeReservedCharactersInDefaultAttributeValues(defaultValue));
                internalDTDSubset.append("\"");         
            }
            internalDTDSubset.append(">\n");   
        }
        
    }
  
    
    public void internalEntityDecl(String name, 
       String value) {   
        
        if (inInternalSubset() && doctype != null) {
            internalDTDSubset.append("  <!ENTITY ");
            if (name.startsWith("%")) {
                internalDTDSubset.append("% "); 
                internalDTDSubset.append(name.substring(1));
            }
            else {
                internalDTDSubset.append(name); 
            }
            internalDTDSubset.append(" \""); 
            internalDTDSubset.append(escapeReservedCharactersInDeclarations(value)); 
            internalDTDSubset.append("\">\n"); 
        }
        
    }
  
    
    public void externalEntityDecl(String name, 
       String publicID, String systemID) {
        
        if (inInternalSubset() && doctype != null) {
            internalDTDSubset.append("  <!ENTITY ");
            if (name.startsWith("%")) { 
                internalDTDSubset.append("% ");
                internalDTDSubset.append(name.substring(1));
            }
            else {
                internalDTDSubset.append(name);
            }
               
            if (locator != null && URIUtil.isAbsolute(systemID)) {
                String documentURL = locator.getSystemId();
                // work around Crimson style file:/root URLs
                if (documentURL != null) {
                    if (documentURL.startsWith("file:/") && !documentURL.startsWith("file:///")) {
                        documentURL = "file://" + documentURL.substring(5); 
                    }
                    if (systemID.startsWith("file:/") && !systemID.startsWith("file:///")) {
                        systemID = "file://" + systemID.substring(5); 
                    }
                    systemID = URIUtil.relativize(documentURL, systemID);
                }
            }

            if (publicID != null) { 
                internalDTDSubset.append(" PUBLIC \""); 
                internalDTDSubset.append(publicID); 
                internalDTDSubset.append("\" \""); 
                internalDTDSubset.append(systemID);       
            }
            else {
                // need to escape system ID????
                internalDTDSubset.append(" SYSTEM \""); 
                internalDTDSubset.append(systemID); 
            }
            internalDTDSubset.append("\">\n");
            
        }
        
    }
    
    
    public void notationDecl(String name, String publicID, 
      String systemID) {
        
        if (systemID != null) {
            systemID = escapeReservedCharactersInDeclarations(systemID);
        }
        
        if (inInternalSubset() && doctype != null) {
            internalDTDSubset.append("  <!NOTATION ");
            internalDTDSubset.append(name); 
            if (publicID != null) {
                internalDTDSubset.append(" PUBLIC \""); 
                internalDTDSubset.append(publicID);
                internalDTDSubset.append('"'); 
                if (systemID != null) {
                    internalDTDSubset.append(" \"");                                     
                    internalDTDSubset.append(systemID);
                    internalDTDSubset.append('"');                                     
                }
            }
            else {
                internalDTDSubset.append(" SYSTEM \""); 
                internalDTDSubset.append(systemID);
                internalDTDSubset.append('"');                 
            }
            internalDTDSubset.append(">\n"); 
        }        
        
    }
   
    
    public void unparsedEntityDecl(String name, String publicID, 
     String systemID, String notationName) {
        
        // escapable characters????
        if (inInternalSubset() && doctype != null) {
            internalDTDSubset.append("  <!ENTITY ");
            if (publicID != null) { 
                internalDTDSubset.append(name); 
                internalDTDSubset.append(" PUBLIC \""); 
                internalDTDSubset.append(publicID); 
                internalDTDSubset.append("\" \""); 
                internalDTDSubset.append(systemID); 
                internalDTDSubset.append("\" NDATA "); 
                internalDTDSubset.append(notationName);       
            }
            else {
                internalDTDSubset.append(name); 
                internalDTDSubset.append(" SYSTEM \""); 
                internalDTDSubset.append(systemID); 
                internalDTDSubset.append("\" NDATA "); 
                internalDTDSubset.append(notationName);     
            }
            internalDTDSubset.append(">\n"); 
        }
        
    }
    
    
    private static String escapeReservedCharactersInDeclarations(String s) {
        
        int length = s.length();
        StringBuffer result = new StringBuffer(length);
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            switch (c) {
                case '\r': 
                    result.append("&#x0D;");
                    break;
                case 14:
                    // placeholder for table lookup
                    break;
                case 15:
                    // placeholder for table lookup
                    break;
                case 16 :
                    // placeholder for table lookup
                    break;
                case 17:
                    // placeholder for table lookup
                    break;
                case 18:
                    // placeholder for table lookup
                    break;
                case 19:
                    // placeholder for table lookup
                    break;
                case 20:
                    // placeholder for table lookup
                    break;
                case 21:
                    // placeholder for table lookup
                    break;
                case 22:
                    // placeholder for table lookup
                    break;
                case 23:
                    // placeholder for table lookup
                    break;
                case 24:
                    // placeholder for table lookup
                    break;
                case 25:
                    // placeholder for table lookup
                    break;
                case 26:
                    // placeholder for table lookup
                    break;
                case 27:
                    // placeholder for table lookup
                    break;
                case 28:
                    // placeholder for table lookup
                    break;
                case 29:
                    // placeholder for table lookup
                    break;
                case 30:
                    // placeholder for table lookup
                    break;
                case 31:
                    // placeholder for table lookup
                    break;
                case ' ':
                    result.append(' ');
                    break;
                case '!':
                    result.append('!');
                    break;
                case '\"':
                    result.append("&#x22;");
                    break;
                case '#':
                    result.append('#');
                    break;
                case '$':
                    result.append('$');
                    break;
                case '%':
                    result.append("&#x25;");
                    break;
                case '&':
                    result.append("&#x26;");
                    break;
                default:
                    result.append(c);
            }
        }
        
        return result.toString();
        
    }

    
    private static String escapeReservedCharactersInDefaultAttributeValues(String s) {
        
        int length = s.length();
        StringBuffer result = new StringBuffer(length);
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            switch (c) {
                case '\r': 
                    result.append("&#x0D;");
                    break;
                case 14:
                    // placeholder for table lookup
                    break;
                case 15:
                    // placeholder for table lookup
                    break;
                case 16 :
                    // placeholder for table lookup
                    break;
                case 17:
                    // placeholder for table lookup
                    break;
                case 18:
                    // placeholder for table lookup
                    break;
                case 19:
                    // placeholder for table lookup
                    break;
                case 20:
                    // placeholder for table lookup
                    break;
                case 21:
                    // placeholder for table lookup
                    break;
                case 22:
                    // placeholder for table lookup
                    break;
                case 23:
                    // placeholder for table lookup
                    break;
                case 24:
                    // placeholder for table lookup
                    break;
                case 25:
                    // placeholder for table lookup
                    break;
                case 26:
                    // placeholder for table lookup
                    break;
                case 27:
                    // placeholder for table lookup
                    break;
                case 28:
                    // placeholder for table lookup
                    break;
                case 29:
                    // placeholder for table lookup
                    break;
                case 30:
                    // placeholder for table lookup
                    break;
                case 31:
                    // placeholder for table lookup
                    break;
                case ' ':
                    result.append(' ');
                    break;
                case '!':
                    result.append('!');
                    break;
                case '\"':
                    result.append("&quot;");
                    break;
                case '#':
                    result.append('#');
                    break;
                case '$':
                    result.append('$');
                    break;
                case '%':
                    result.append("&#x25;");
                    break;
                case '&':
                    result.append("&amp;");
                    break;
                case '\'':
                    result.append('\'');
                    break;
                case '(':
                    result.append('(');
                    break;
                case ')':
                    result.append(')');
                    break;
                case '*':
                    result.append('*');
                    break;
                case '+':
                    result.append('+');
                    break;
                case ',':
                    result.append(',');
                    break;
                case '-':
                    result.append('-');
                    break;
                case '.':
                    result.append('.');
                    break;
                case '/':
                    result.append('/');
                    break;
                case '0':
                    result.append('0');
                    break;
                case '1':
                    result.append('1');
                    break;
                case '2':
                    result.append('2');
                    break;
                case '3':
                    result.append('3');
                    break;
                case '4':
                    result.append('4');
                    break;
                case '5':
                    result.append('5');
                    break;
                case '6':
                    result.append('6');
                    break;
                case '7':
                    result.append('7');
                    break;
                case '8':
                    result.append('8');
                    break;
                case '9':
                    result.append('9');
                    break;
                case ':':
                    result.append(':');
                    break;
                case ';':
                    result.append(';');
                    break;
                case '<': 
                    result.append("&lt;");
                    break;
                default:
                    result.append(c);
            }
        }
        
        return result.toString();
        
    }

    
}