001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: DOM3TreeWalker.java 1225446 2011-12-29 06:12:59Z mrglavas $
020 */
021
022 package org.apache.xml.serializer.dom3;
023
024 import java.io.File;
025 import java.io.IOException;
026 import java.io.Writer;
027 import java.util.Enumeration;
028 import java.util.Hashtable;
029 import java.util.Properties;
030
031 import org.apache.xml.serializer.dom3.NamespaceSupport;
032 import org.apache.xml.serializer.OutputPropertiesFactory;
033 import org.apache.xml.serializer.SerializationHandler;
034 import org.apache.xml.serializer.utils.MsgKey;
035 import org.apache.xml.serializer.utils.Utils;
036 import org.apache.xml.serializer.utils.XML11Char;
037 import org.apache.xml.serializer.utils.XMLChar;
038 import org.w3c.dom.Attr;
039 import org.w3c.dom.CDATASection;
040 import org.w3c.dom.Comment;
041 import org.w3c.dom.DOMError;
042 import org.w3c.dom.DOMErrorHandler;
043 import org.w3c.dom.Document;
044 import org.w3c.dom.DocumentType;
045 import org.w3c.dom.Element;
046 import org.w3c.dom.Entity;
047 import org.w3c.dom.EntityReference;
048 import org.w3c.dom.NamedNodeMap;
049 import org.w3c.dom.Node;
050 import org.w3c.dom.NodeList;
051 import org.w3c.dom.ProcessingInstruction;
052 import org.w3c.dom.Text;
053 import org.w3c.dom.ls.LSSerializerFilter;
054 import org.w3c.dom.traversal.NodeFilter;
055 import org.xml.sax.Locator;
056 import org.xml.sax.SAXException;
057 import org.xml.sax.ext.LexicalHandler;
058 import org.xml.sax.helpers.LocatorImpl;
059
060 /**
061 * Built on org.apache.xml.serializer.TreeWalker and adds functionality to
062 * traverse and serialize a DOM Node (Level 2 or Level 3) as specified in
063 * the DOM Level 3 LS Recommedation by evaluating and applying DOMConfiguration
064 * parameters and filters if any during serialization.
065 *
066 * @xsl.usage internal
067 */
068 final class DOM3TreeWalker {
069
070 /**
071 * The SerializationHandler, it extends ContentHandler and when
072 * this class is instantiated via the constructor provided, a
073 * SerializationHandler object is passed to it.
074 */
075 private SerializationHandler fSerializer = null;
076
077 /** We do not need DOM2Helper since DOM Level 3 LS applies to DOM Level 2 or newer */
078
079 /** Locator object for this TreeWalker */
080 private LocatorImpl fLocator = new LocatorImpl();
081
082 /** ErrorHandler */
083 private DOMErrorHandler fErrorHandler = null;
084
085 /** LSSerializerFilter */
086 private LSSerializerFilter fFilter = null;
087
088 /** If the serializer is an instance of a LexicalHandler */
089 private LexicalHandler fLexicalHandler = null;
090
091 private int fWhatToShowFilter;
092
093 /** New Line character to use in serialization */
094 private String fNewLine = null;
095
096 /** DOMConfiguration Properties */
097 private Properties fDOMConfigProperties = null;
098
099 /** Keeps track if we are in an entity reference when entities=true */
100 private boolean fInEntityRef = false;
101
102 /** Stores the version of the XML document to be serialize */
103 private String fXMLVersion = null;
104
105 /** XML Version, default 1.0 */
106 private boolean fIsXMLVersion11 = false;
107
108 /** Is the Node a Level 3 DOM node */
109 private boolean fIsLevel3DOM = false;
110
111 /** DOM Configuration Parameters */
112 private int fFeatures = 0;
113
114 /** Flag indicating whether following text to be processed is raw text */
115 boolean fNextIsRaw = false;
116
117 //
118 private static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
119
120 //
121 private static final String XMLNS_PREFIX = "xmlns";
122
123 //
124 private static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
125
126 //
127 private static final String XML_PREFIX = "xml";
128
129 /** stores namespaces in scope */
130 protected NamespaceSupport fNSBinder;
131
132 /** stores all namespace bindings on the current element */
133 protected NamespaceSupport fLocalNSBinder;
134
135 /** stores the current element depth */
136 private int fElementDepth = 0;
137
138 // ***********************************************************************
139 // DOMConfiguration paramter settings
140 // ***********************************************************************
141 // Parameter canonical-form, true [optional] - NOT SUPPORTED
142 private final static int CANONICAL = 0x1 << 0;
143
144 // Parameter cdata-sections, true [required] (default)
145 private final static int CDATA = 0x1 << 1;
146
147 // Parameter check-character-normalization, true [optional] - NOT SUPPORTED
148 private final static int CHARNORMALIZE = 0x1 << 2;
149
150 // Parameter comments, true [required] (default)
151 private final static int COMMENTS = 0x1 << 3;
152
153 // Parameter datatype-normalization, true [optional] - NOT SUPPORTED
154 private final static int DTNORMALIZE = 0x1 << 4;
155
156 // Parameter element-content-whitespace, true [required] (default) - value - false [optional] NOT SUPPORTED
157 private final static int ELEM_CONTENT_WHITESPACE = 0x1 << 5;
158
159 // Parameter entities, true [required] (default)
160 private final static int ENTITIES = 0x1 << 6;
161
162 // Parameter infoset, true [required] (default), false has no effect --> True has no effect for the serializer
163 private final static int INFOSET = 0x1 << 7;
164
165 // Parameter namespaces, true [required] (default)
166 private final static int NAMESPACES = 0x1 << 8;
167
168 // Parameter namespace-declarations, true [required] (default)
169 private final static int NAMESPACEDECLS = 0x1 << 9;
170
171 // Parameter normalize-characters, true [optional] - NOT SUPPORTED
172 private final static int NORMALIZECHARS = 0x1 << 10;
173
174 // Parameter split-cdata-sections, true [required] (default)
175 private final static int SPLITCDATA = 0x1 << 11;
176
177 // Parameter validate, true [optional] - NOT SUPPORTED
178 private final static int VALIDATE = 0x1 << 12;
179
180 // Parameter validate-if-schema, true [optional] - NOT SUPPORTED
181 private final static int SCHEMAVALIDATE = 0x1 << 13;
182
183 // Parameter split-cdata-sections, true [required] (default)
184 private final static int WELLFORMED = 0x1 << 14;
185
186 // Parameter discard-default-content, true [required] (default)
187 // Not sure how this will be used in level 2 Documents
188 private final static int DISCARDDEFAULT = 0x1 << 15;
189
190 // Parameter format-pretty-print, true [optional]
191 private final static int PRETTY_PRINT = 0x1 << 16;
192
193 // Parameter ignore-unknown-character-denormalizations, true [required] (default)
194 // We currently do not support XML 1.1 character normalization
195 private final static int IGNORE_CHAR_DENORMALIZE = 0x1 << 17;
196
197 // Parameter discard-default-content, true [required] (default)
198 private final static int XMLDECL = 0x1 << 18;
199
200 /**
201 * Constructor.
202 * @param contentHandler serialHandler The implemention of the SerializationHandler interface
203 */
204 DOM3TreeWalker(
205 SerializationHandler serialHandler,
206 DOMErrorHandler errHandler,
207 LSSerializerFilter filter,
208 String newLine) {
209 fSerializer = serialHandler;
210 //fErrorHandler = errHandler == null ? new DOMErrorHandlerImpl() : errHandler; // Should we be using the default?
211 fErrorHandler = errHandler;
212 fFilter = filter;
213 fLexicalHandler = null;
214 fNewLine = newLine;
215
216 fNSBinder = new NamespaceSupport();
217 fLocalNSBinder = new NamespaceSupport();
218
219 fDOMConfigProperties = fSerializer.getOutputFormat();
220 fSerializer.setDocumentLocator(fLocator);
221 initProperties(fDOMConfigProperties);
222
223 try {
224 // Bug see Bugzilla 26741
225 fLocator.setSystemId(
226 System.getProperty("user.dir") + File.separator + "dummy.xsl");
227 } catch (SecurityException se) { // user.dir not accessible from applet
228
229 }
230 }
231
232 /**
233 * Perform a pre-order traversal non-recursive style.
234 *
235 * Note that TreeWalker assumes that the subtree is intended to represent
236 * a complete (though not necessarily well-formed) document and, during a
237 * traversal, startDocument and endDocument will always be issued to the
238 * SAX listener.
239 *
240 * @param pos Node in the tree where to start traversal
241 *
242 * @throws TransformerException
243 */
244 public void traverse(Node pos) throws org.xml.sax.SAXException {
245 this.fSerializer.startDocument();
246
247 // Determine if the Node is a DOM Level 3 Core Node.
248 if (pos.getNodeType() != Node.DOCUMENT_NODE) {
249 Document ownerDoc = pos.getOwnerDocument();
250 if (ownerDoc != null
251 && ownerDoc.getImplementation().hasFeature("Core", "3.0")) {
252 fIsLevel3DOM = true;
253 }
254 } else {
255 if (((Document) pos)
256 .getImplementation()
257 .hasFeature("Core", "3.0")) {
258 fIsLevel3DOM = true;
259 }
260 }
261
262 if (fSerializer instanceof LexicalHandler) {
263 fLexicalHandler = ((LexicalHandler) this.fSerializer);
264 }
265
266 if (fFilter != null)
267 fWhatToShowFilter = fFilter.getWhatToShow();
268
269 Node top = pos;
270
271 while (null != pos) {
272 startNode(pos);
273
274 Node nextNode = null;
275
276 nextNode = pos.getFirstChild();
277
278 while (null == nextNode) {
279 endNode(pos);
280
281 if (top.equals(pos))
282 break;
283
284 nextNode = pos.getNextSibling();
285
286 if (null == nextNode) {
287 pos = pos.getParentNode();
288
289 if ((null == pos) || (top.equals(pos))) {
290 if (null != pos)
291 endNode(pos);
292
293 nextNode = null;
294
295 break;
296 }
297 }
298 }
299
300 pos = nextNode;
301 }
302 this.fSerializer.endDocument();
303 }
304
305 /**
306 * Perform a pre-order traversal non-recursive style.
307
308 * Note that TreeWalker assumes that the subtree is intended to represent
309 * a complete (though not necessarily well-formed) document and, during a
310 * traversal, startDocument and endDocument will always be issued to the
311 * SAX listener.
312 *
313 * @param pos Node in the tree where to start traversal
314 * @param top Node in the tree where to end traversal
315 *
316 * @throws TransformerException
317 */
318 public void traverse(Node pos, Node top) throws org.xml.sax.SAXException {
319
320 this.fSerializer.startDocument();
321
322 // Determine if the Node is a DOM Level 3 Core Node.
323 if (pos.getNodeType() != Node.DOCUMENT_NODE) {
324 Document ownerDoc = pos.getOwnerDocument();
325 if (ownerDoc != null
326 && ownerDoc.getImplementation().hasFeature("Core", "3.0")) {
327 fIsLevel3DOM = true;
328 }
329 } else {
330 if (((Document) pos)
331 .getImplementation()
332 .hasFeature("Core", "3.0")) {
333 fIsLevel3DOM = true;
334 }
335 }
336
337 if (fSerializer instanceof LexicalHandler) {
338 fLexicalHandler = ((LexicalHandler) this.fSerializer);
339 }
340
341 if (fFilter != null)
342 fWhatToShowFilter = fFilter.getWhatToShow();
343
344 while (null != pos) {
345 startNode(pos);
346
347 Node nextNode = null;
348
349 nextNode = pos.getFirstChild();
350
351 while (null == nextNode) {
352 endNode(pos);
353
354 if ((null != top) && top.equals(pos))
355 break;
356
357 nextNode = pos.getNextSibling();
358
359 if (null == nextNode) {
360 pos = pos.getParentNode();
361
362 if ((null == pos) || ((null != top) && top.equals(pos))) {
363 nextNode = null;
364
365 break;
366 }
367 }
368 }
369
370 pos = nextNode;
371 }
372 this.fSerializer.endDocument();
373 }
374
375 /**
376 * Optimized dispatch of characters.
377 */
378 private final void dispatachChars(Node node)
379 throws org.xml.sax.SAXException {
380 if (fSerializer != null) {
381 this.fSerializer.characters(node);
382 } else {
383 String data = ((Text) node).getData();
384 this.fSerializer.characters(data.toCharArray(), 0, data.length());
385 }
386 }
387
388 /**
389 * Start processing given node
390 *
391 * @param node Node to process
392 *
393 * @throws org.xml.sax.SAXException
394 */
395 protected void startNode(Node node) throws org.xml.sax.SAXException {
396 if (node instanceof Locator) {
397 Locator loc = (Locator) node;
398 fLocator.setColumnNumber(loc.getColumnNumber());
399 fLocator.setLineNumber(loc.getLineNumber());
400 fLocator.setPublicId(loc.getPublicId());
401 fLocator.setSystemId(loc.getSystemId());
402 } else {
403 fLocator.setColumnNumber(0);
404 fLocator.setLineNumber(0);
405 }
406
407 switch (node.getNodeType()) {
408 case Node.DOCUMENT_TYPE_NODE :
409 serializeDocType((DocumentType) node, true);
410 break;
411 case Node.COMMENT_NODE :
412 serializeComment((Comment) node);
413 break;
414 case Node.DOCUMENT_FRAGMENT_NODE :
415 // Children are traversed
416 break;
417 case Node.DOCUMENT_NODE :
418 break;
419 case Node.ELEMENT_NODE :
420 serializeElement((Element) node, true);
421 break;
422 case Node.PROCESSING_INSTRUCTION_NODE :
423 serializePI((ProcessingInstruction) node);
424 break;
425 case Node.CDATA_SECTION_NODE :
426 serializeCDATASection((CDATASection) node);
427 break;
428 case Node.TEXT_NODE :
429 serializeText((Text) node);
430 break;
431 case Node.ENTITY_REFERENCE_NODE :
432 serializeEntityReference((EntityReference) node, true);
433 break;
434 default :
435 }
436 }
437
438 /**
439 * End processing of given node
440 *
441 *
442 * @param node Node we just finished processing
443 *
444 * @throws org.xml.sax.SAXException
445 */
446 protected void endNode(Node node) throws org.xml.sax.SAXException {
447
448 switch (node.getNodeType()) {
449 case Node.DOCUMENT_NODE :
450 break;
451 case Node.DOCUMENT_TYPE_NODE :
452 serializeDocType((DocumentType) node, false);
453 break;
454 case Node.ELEMENT_NODE :
455 serializeElement((Element) node, false);
456 break;
457 case Node.CDATA_SECTION_NODE :
458 break;
459 case Node.ENTITY_REFERENCE_NODE :
460 serializeEntityReference((EntityReference) node, false);
461 break;
462 default :
463 }
464 }
465
466 // ***********************************************************************
467 // Node serialization methods
468 // ***********************************************************************
469 /**
470 * Applies a filter on the node to serialize
471 *
472 * @param node The Node to serialize
473 * @return True if the node is to be serialized else false if the node
474 * is to be rejected or skipped.
475 */
476 protected boolean applyFilter(Node node, int nodeType) {
477 if (fFilter != null && (fWhatToShowFilter & nodeType) != 0) {
478
479 short code = fFilter.acceptNode(node);
480 switch (code) {
481 case NodeFilter.FILTER_REJECT :
482 case NodeFilter.FILTER_SKIP :
483 return false; // skip the node
484 default : // fall through..
485 }
486 }
487 return true;
488 }
489
490 /**
491 * Serializes a Document Type Node.
492 *
493 * @param node The Docuemnt Type Node to serialize
494 * @param bStart Invoked at the start or end of node. Default true.
495 */
496 protected void serializeDocType(DocumentType node, boolean bStart)
497 throws SAXException {
498 // The DocType and internalSubset can not be modified in DOM and is
499 // considered to be well-formed as the outcome of successful parsing.
500 String docTypeName = node.getNodeName();
501 String publicId = node.getPublicId();
502 String systemId = node.getSystemId();
503 String internalSubset = node.getInternalSubset();
504
505 //DocumentType nodes are never passed to the filter
506
507 if (internalSubset != null && !"".equals(internalSubset)) {
508
509 if (bStart) {
510 try {
511 // The Serializer does not provide a way to write out the
512 // DOCTYPE internal subset via an event call, so we write it
513 // out here.
514 Writer writer = fSerializer.getWriter();
515 StringBuffer dtd = new StringBuffer();
516
517 dtd.append("<!DOCTYPE ");
518 dtd.append(docTypeName);
519 if (null != publicId) {
520 dtd.append(" PUBLIC \"");
521 dtd.append(publicId);
522 dtd.append('\"');
523 }
524
525 if (null != systemId) {
526 if (null == publicId) {
527 dtd.append(" SYSTEM \"");
528 } else {
529 dtd.append(" \"");
530 }
531 dtd.append(systemId);
532 dtd.append('\"');
533 }
534
535 dtd.append(" [ ");
536
537 dtd.append(fNewLine);
538 dtd.append(internalSubset);
539 dtd.append("]>");
540 dtd.append(fNewLine);
541
542 writer.write(dtd.toString());
543 writer.flush();
544
545 } catch (IOException e) {
546 throw new SAXException(Utils.messages.createMessage(
547 MsgKey.ER_WRITING_INTERNAL_SUBSET, null), e);
548 }
549 } // else if !bStart do nothing
550
551 } else {
552
553 if (bStart) {
554 if (fLexicalHandler != null) {
555 fLexicalHandler.startDTD(docTypeName, publicId, systemId);
556 }
557 } else {
558 if (fLexicalHandler != null) {
559 fLexicalHandler.endDTD();
560 }
561 }
562 }
563 }
564
565 /**
566 * Serializes a Comment Node.
567 *
568 * @param node The Comment Node to serialize
569 */
570 protected void serializeComment(Comment node) throws SAXException {
571 // comments=true
572 if ((fFeatures & COMMENTS) != 0) {
573 String data = node.getData();
574
575 // well-formed=true
576 if ((fFeatures & WELLFORMED) != 0) {
577 isCommentWellFormed(data);
578 }
579
580 if (fLexicalHandler != null) {
581 // apply the LSSerializer filter after the operations requested by the
582 // DOMConfiguration parameters have been applied
583 if (!applyFilter(node, NodeFilter.SHOW_COMMENT)) {
584 return;
585 }
586
587 fLexicalHandler.comment(data.toCharArray(), 0, data.length());
588 }
589 }
590 }
591
592 /**
593 * Serializes an Element Node.
594 *
595 * @param node The Element Node to serialize
596 * @param bStart Invoked at the start or end of node.
597 */
598 protected void serializeElement(Element node, boolean bStart)
599 throws SAXException {
600 if (bStart) {
601 fElementDepth++;
602
603 // We use the Xalan specific startElement and starPrefixMapping calls
604 // (and addAttribute and namespaceAfterStartElement) as opposed to
605 // SAX specific, for performance reasons as they reduce the overhead
606 // of creating an AttList object upfront.
607
608 // well-formed=true
609 if ((fFeatures & WELLFORMED) != 0) {
610 isElementWellFormed(node);
611 }
612
613 // REVISIT: We apply the LSSerializer filter for elements before
614 // namesapce fixup
615 if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) {
616 return;
617 }
618
619 // namespaces=true, record and fixup namspaced element
620 if ((fFeatures & NAMESPACES) != 0) {
621 fNSBinder.pushContext();
622 fLocalNSBinder.reset();
623
624 recordLocalNSDecl(node);
625 fixupElementNS(node);
626 }
627
628 // Namespace normalization
629 fSerializer.startElement(
630 node.getNamespaceURI(),
631 node.getLocalName(),
632 node.getNodeName());
633
634 serializeAttList(node);
635
636 } else {
637 fElementDepth--;
638
639 // apply the LSSerializer filter
640 if (!applyFilter(node, NodeFilter.SHOW_ELEMENT)) {
641 return;
642 }
643
644 this.fSerializer.endElement(
645 node.getNamespaceURI(),
646 node.getLocalName(),
647 node.getNodeName());
648 // since endPrefixMapping was not used by SerializationHandler it was removed
649 // for performance reasons.
650
651 if ((fFeatures & NAMESPACES) != 0 ) {
652 fNSBinder.popContext();
653 }
654
655 }
656 }
657
658 /**
659 * Serializes the Attr Nodes of an Element.
660 *
661 * @param node The OwnerElement whose Attr Nodes are to be serialized.
662 */
663 protected void serializeAttList(Element node) throws SAXException {
664 NamedNodeMap atts = node.getAttributes();
665 int nAttrs = atts.getLength();
666
667 for (int i = 0; i < nAttrs; i++) {
668 Node attr = atts.item(i);
669
670 String localName = attr.getLocalName();
671 String attrName = attr.getNodeName();
672 String attrPrefix = attr.getPrefix() == null ? "" : attr.getPrefix();
673 String attrValue = attr.getNodeValue();
674
675 // Determine the Attr's type.
676 String type = null;
677 if (fIsLevel3DOM) {
678 type = ((Attr) attr).getSchemaTypeInfo().getTypeName();
679 }
680 type = type == null ? "CDATA" : type;
681
682 String attrNS = attr.getNamespaceURI();
683 if (attrNS !=null && attrNS.length() == 0) {
684 attrNS=null;
685 // we must remove prefix for this attribute
686 attrName=attr.getLocalName();
687 }
688
689 boolean isSpecified = ((Attr) attr).getSpecified();
690 boolean addAttr = true;
691 boolean applyFilter = false;
692 boolean xmlnsAttr =
693 attrName.equals("xmlns") || attrName.startsWith("xmlns:");
694
695 // well-formed=true
696 if ((fFeatures & WELLFORMED) != 0) {
697 isAttributeWellFormed(attr);
698 }
699
700 //-----------------------------------------------------------------
701 // start Attribute namespace fixup
702 //-----------------------------------------------------------------
703 // namespaces=true, normalize all non-namespace attributes
704 // Step 3. Attribute
705 if ((fFeatures & NAMESPACES) != 0 && !xmlnsAttr) {
706
707 // If the Attr has a namespace URI
708 if (attrNS != null) {
709 attrPrefix = attrPrefix == null ? "" : attrPrefix;
710
711 String declAttrPrefix = fNSBinder.getPrefix(attrNS);
712 String declAttrNS = fNSBinder.getURI(attrPrefix);
713
714 // attribute has no prefix (default namespace decl does not apply to
715 // attributes)
716 // OR
717 // attribute prefix is not declared
718 // OR
719 // conflict: attribute has a prefix that conflicts with a binding
720 if ("".equals(attrPrefix) || "".equals(declAttrPrefix)
721 || !attrPrefix.equals(declAttrPrefix)) {
722
723 // namespaceURI matches an in scope declaration of one or
724 // more prefixes
725 if (declAttrPrefix != null && !"".equals(declAttrPrefix)) {
726 // pick the prefix that was found and change attribute's
727 // prefix and nodeName.
728 attrPrefix = declAttrPrefix;
729
730 if (declAttrPrefix.length() > 0 ) {
731 attrName = declAttrPrefix + ":" + localName;
732 } else {
733 attrName = localName;
734 }
735 } else {
736 // The current prefix is not null and it has no in scope
737 // declaration
738 if (attrPrefix != null && !"".equals(attrPrefix)
739 && declAttrNS == null) {
740 // declare this prefix
741 if ((fFeatures & NAMESPACEDECLS) != 0) {
742 fSerializer.addAttribute(XMLNS_URI, attrPrefix,
743 XMLNS_PREFIX + ":" + attrPrefix, "CDATA",
744 attrNS);
745 fNSBinder.declarePrefix(attrPrefix, attrNS);
746 fLocalNSBinder.declarePrefix(attrPrefix, attrNS);
747 }
748 } else {
749 // find a prefix following the pattern "NS" +index
750 // (starting at 1)
751 // make sure this prefix is not declared in the current
752 // scope.
753 int counter = 1;
754 attrPrefix = "NS" + counter++;
755
756 while (fLocalNSBinder.getURI(attrPrefix) != null) {
757 attrPrefix = "NS" + counter++;
758 }
759 // change attribute's prefix and Name
760 attrName = attrPrefix + ":" + localName;
761
762 // create a local namespace declaration attribute
763 // Add the xmlns declaration attribute
764 if ((fFeatures & NAMESPACEDECLS) != 0) {
765
766 fSerializer.addAttribute(XMLNS_URI, attrPrefix,
767 XMLNS_PREFIX + ":" + attrPrefix, "CDATA",
768 attrNS);
769 fNSBinder.declarePrefix(attrPrefix, attrNS);
770 fLocalNSBinder.declarePrefix(attrPrefix, attrNS);
771 }
772 }
773 }
774 }
775
776 } else { // if the Attr has no namespace URI
777 // Attr has no localName
778 if (localName == null) {
779 // DOM Level 1 node!
780 String msg = Utils.messages.createMessage(
781 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
782 new Object[] { attrName });
783
784 if (fErrorHandler != null) {
785 fErrorHandler
786 .handleError(new DOMErrorImpl(
787 DOMError.SEVERITY_ERROR, msg,
788 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME, null,
789 null, null));
790 }
791
792 } else { // uri=null and no colon
793 // attr has no namespace URI and no prefix
794 // no action is required, since attrs don't use default
795 }
796 }
797
798 }
799
800
801 // discard-default-content=true
802 // Default attr's are not passed to the filter and this contraint
803 // is applied only when discard-default-content=true
804 // What about default xmlns attributes???? check for xmlnsAttr
805 if ((((fFeatures & DISCARDDEFAULT) != 0) && isSpecified)
806 || ((fFeatures & DISCARDDEFAULT) == 0)) {
807 applyFilter = true;
808 } else {
809 addAttr = false;
810 }
811
812 if (applyFilter) {
813 // apply the filter for Attributes that are not default attributes
814 // or namespace decl attributes
815 if (fFilter != null
816 && (fFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)
817 != 0) {
818
819 if (!xmlnsAttr) {
820 short code = fFilter.acceptNode(attr);
821 switch (code) {
822 case NodeFilter.FILTER_REJECT :
823 case NodeFilter.FILTER_SKIP :
824 addAttr = false;
825 break;
826 default : //fall through..
827 }
828 }
829 }
830 }
831
832 // if the node is a namespace node
833 if (addAttr && xmlnsAttr) {
834 // If namespace-declarations=true, add the node , else don't add it
835 if ((fFeatures & NAMESPACEDECLS) != 0) {
836 // The namespace may have been fixed up, in that case don't add it.
837 if (localName != null && !"".equals(localName)) {
838 fSerializer.addAttribute(attrNS, localName, attrName, type, attrValue);
839 }
840 }
841 } else if (
842 addAttr && !xmlnsAttr) { // if the node is not a namespace node
843 // If namespace-declarations=true, add the node with the Attr nodes namespaceURI
844 // else add the node setting it's namespace to null or else the serializer will later
845 // attempt to add a xmlns attr for the prefixed attribute
846 if (((fFeatures & NAMESPACEDECLS) != 0) && (attrNS != null)) {
847 fSerializer.addAttribute(
848 attrNS,
849 localName,
850 attrName,
851 type,
852 attrValue);
853 } else {
854 fSerializer.addAttribute(
855 "",
856 localName,
857 attrName,
858 type,
859 attrValue);
860 }
861 }
862
863 //
864 if (xmlnsAttr && ((fFeatures & NAMESPACEDECLS) != 0)) {
865 int index;
866 // Use "" instead of null, as Xerces likes "" for the
867 // name of the default namespace. Fix attributed
868 // to "Steven Murray" <smurray@ebt.com>.
869 String prefix =
870 (index = attrName.indexOf(":")) < 0
871 ? ""
872 : attrName.substring(index + 1);
873
874 if (!"".equals(prefix)) {
875 fSerializer.namespaceAfterStartElement(prefix, attrValue);
876 }
877 }
878 }
879
880 }
881
882 /**
883 * Serializes an ProcessingInstruction Node.
884 *
885 * @param node The ProcessingInstruction Node to serialize
886 */
887 protected void serializePI(ProcessingInstruction node)
888 throws SAXException {
889 ProcessingInstruction pi = node;
890 String name = pi.getNodeName();
891
892 // well-formed=true
893 if ((fFeatures & WELLFORMED) != 0) {
894 isPIWellFormed(node);
895 }
896
897 // apply the LSSerializer filter
898 if (!applyFilter(node, NodeFilter.SHOW_PROCESSING_INSTRUCTION)) {
899 return;
900 }
901
902 // String data = pi.getData();
903 if (name.equals("xslt-next-is-raw")) {
904 fNextIsRaw = true;
905 } else {
906 this.fSerializer.processingInstruction(name, pi.getData());
907 }
908 }
909
910 /**
911 * Serializes an CDATASection Node.
912 *
913 * @param node The CDATASection Node to serialize
914 */
915 protected void serializeCDATASection(CDATASection node)
916 throws SAXException {
917 // well-formed=true
918 if ((fFeatures & WELLFORMED) != 0) {
919 isCDATASectionWellFormed(node);
920 }
921
922 // cdata-sections = true
923 if ((fFeatures & CDATA) != 0) {
924
925 // split-cdata-sections = true
926 // Assumption: This parameter has an effect only when
927 // cdata-sections=true
928 // ToStream, by default splits cdata-sections. Hence the check
929 // below.
930 String nodeValue = node.getNodeValue();
931 int endIndex = nodeValue.indexOf("]]>");
932 if ((fFeatures & SPLITCDATA) != 0) {
933 if (endIndex >= 0) {
934 // The first node split will contain the ]] markers
935 String relatedData = nodeValue.substring(0, endIndex + 2);
936
937 String msg =
938 Utils.messages.createMessage(
939 MsgKey.ER_CDATA_SECTIONS_SPLIT,
940 null);
941
942 if (fErrorHandler != null) {
943 fErrorHandler.handleError(
944 new DOMErrorImpl(
945 DOMError.SEVERITY_WARNING,
946 msg,
947 MsgKey.ER_CDATA_SECTIONS_SPLIT,
948 null,
949 relatedData,
950 null));
951 }
952 }
953 } else {
954 if (endIndex >= 0) {
955 // The first node split will contain the ]] markers
956 String relatedData = nodeValue.substring(0, endIndex + 2);
957
958 String msg =
959 Utils.messages.createMessage(
960 MsgKey.ER_CDATA_SECTIONS_SPLIT,
961 null);
962
963 if (fErrorHandler != null) {
964 fErrorHandler.handleError(
965 new DOMErrorImpl(
966 DOMError.SEVERITY_ERROR,
967 msg,
968 MsgKey.ER_CDATA_SECTIONS_SPLIT));
969 }
970 // Report an error and return. What error???
971 return;
972 }
973 }
974
975 // apply the LSSerializer filter
976 if (!applyFilter(node, NodeFilter.SHOW_CDATA_SECTION)) {
977 return;
978 }
979
980 // splits the cdata-section
981 if (fLexicalHandler != null) {
982 fLexicalHandler.startCDATA();
983 }
984 dispatachChars(node);
985 if (fLexicalHandler != null) {
986 fLexicalHandler.endCDATA();
987 }
988 } else {
989 dispatachChars(node);
990 }
991 }
992
993 /**
994 * Serializes an Text Node.
995 *
996 * @param node The Text Node to serialize
997 */
998 protected void serializeText(Text node) throws SAXException {
999 if (fNextIsRaw) {
1000 fNextIsRaw = false;
1001 fSerializer.processingInstruction(
1002 javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING,
1003 "");
1004 dispatachChars(node);
1005 fSerializer.processingInstruction(
1006 javax.xml.transform.Result.PI_ENABLE_OUTPUT_ESCAPING,
1007 "");
1008 } else {
1009 // keep track of dispatch or not to avoid duplicaiton of filter code
1010 boolean bDispatch = false;
1011
1012 // well-formed=true
1013 if ((fFeatures & WELLFORMED) != 0) {
1014 isTextWellFormed(node);
1015 }
1016
1017 // if the node is whitespace
1018 // Determine the Attr's type.
1019 boolean isElementContentWhitespace = false;
1020 if (fIsLevel3DOM) {
1021 isElementContentWhitespace =
1022 node.isElementContentWhitespace();
1023 }
1024
1025 if (isElementContentWhitespace) {
1026 // element-content-whitespace=true
1027 if ((fFeatures & ELEM_CONTENT_WHITESPACE) != 0) {
1028 bDispatch = true;
1029 }
1030 } else {
1031 bDispatch = true;
1032 }
1033
1034 // apply the LSSerializer filter
1035 if (!applyFilter(node, NodeFilter.SHOW_TEXT)) {
1036 return;
1037 }
1038
1039 if (bDispatch) {
1040 dispatachChars(node);
1041 }
1042 }
1043 }
1044
1045 /**
1046 * Serializes an EntityReference Node.
1047 *
1048 * @param node The EntityReference Node to serialize
1049 * @param bStart Inicates if called from start or endNode
1050 */
1051 protected void serializeEntityReference(
1052 EntityReference node,
1053 boolean bStart)
1054 throws SAXException {
1055 if (bStart) {
1056 EntityReference eref = node;
1057 // entities=true
1058 if ((fFeatures & ENTITIES) != 0) {
1059
1060 // perform well-formedness and other checking only if
1061 // entities = true
1062
1063 // well-formed=true
1064 if ((fFeatures & WELLFORMED) != 0) {
1065 isEntityReferneceWellFormed(node);
1066 }
1067
1068 // check "unbound-prefix-in-entity-reference" [fatal]
1069 // Raised if the configuration parameter "namespaces" is set to true
1070 if ((fFeatures & NAMESPACES) != 0) {
1071 checkUnboundPrefixInEntRef(node);
1072 }
1073
1074 // The filter should not apply in this case, since the
1075 // EntityReference is not being expanded.
1076 // should we pass entity reference nodes to the filter???
1077 }
1078
1079 if (fLexicalHandler != null) {
1080
1081 // startEntity outputs only Text but not Element, Attr, Comment
1082 // and PI child nodes. It does so by setting the m_inEntityRef
1083 // in ToStream and using this to decide if a node is to be
1084 // serialized or not.
1085 fLexicalHandler.startEntity(eref.getNodeName());
1086 }
1087
1088 } else {
1089 EntityReference eref = node;
1090 // entities=true or false,
1091 if (fLexicalHandler != null) {
1092 fLexicalHandler.endEntity(eref.getNodeName());
1093 }
1094 }
1095 }
1096
1097
1098 // ***********************************************************************
1099 // Methods to check well-formedness
1100 // ***********************************************************************
1101 /**
1102 * Taken from org.apache.xerces.dom.CoreDocumentImpl
1103 *
1104 * Check the string against XML's definition of acceptable names for
1105 * elements and attributes and so on using the XMLCharacterProperties
1106 * utility class
1107 */
1108 protected boolean isXMLName(String s, boolean xml11Version) {
1109
1110 if (s == null) {
1111 return false;
1112 }
1113 if (!xml11Version)
1114 return XMLChar.isValidName(s);
1115 else
1116 return XML11Char.isXML11ValidName(s);
1117 }
1118
1119 /**
1120 * Taken from org.apache.xerces.dom.CoreDocumentImpl
1121 *
1122 * Checks if the given qualified name is legal with respect
1123 * to the version of XML to which this document must conform.
1124 *
1125 * @param prefix prefix of qualified name
1126 * @param local local part of qualified name
1127 */
1128 protected boolean isValidQName(
1129 String prefix,
1130 String local,
1131 boolean xml11Version) {
1132
1133 // check that both prefix and local part match NCName
1134 if (local == null)
1135 return false;
1136 boolean validNCName = false;
1137
1138 if (!xml11Version) {
1139 validNCName =
1140 (prefix == null || XMLChar.isValidNCName(prefix))
1141 && XMLChar.isValidNCName(local);
1142 } else {
1143 validNCName =
1144 (prefix == null || XML11Char.isXML11ValidNCName(prefix))
1145 && XML11Char.isXML11ValidNCName(local);
1146 }
1147
1148 return validNCName;
1149 }
1150
1151 /**
1152 * Checks if a XML character is well-formed
1153 *
1154 * @param characters A String of characters to be checked for Well-Formedness
1155 * @param refInvalidChar A reference to the character to be returned that was determined invalid.
1156 */
1157 protected boolean isWFXMLChar(String chardata, Character refInvalidChar) {
1158 if (chardata == null || (chardata.length() == 0)) {
1159 return true;
1160 }
1161
1162 char[] dataarray = chardata.toCharArray();
1163 int datalength = dataarray.length;
1164
1165 // version of the document is XML 1.1
1166 if (fIsXMLVersion11) {
1167 //we need to check all characters as per production rules of XML11
1168 int i = 0;
1169 while (i < datalength) {
1170 if (XML11Char.isXML11Invalid(dataarray[i++])) {
1171 // check if this is a supplemental character
1172 char ch = dataarray[i - 1];
1173 if (XMLChar.isHighSurrogate(ch) && i < datalength) {
1174 char ch2 = dataarray[i++];
1175 if (XMLChar.isLowSurrogate(ch2)
1176 && XMLChar.isSupplemental(
1177 XMLChar.supplemental(ch, ch2))) {
1178 continue;
1179 }
1180 }
1181 // Reference to invalid character which is returned
1182 refInvalidChar = new Character(ch);
1183 return false;
1184 }
1185 }
1186 } // version of the document is XML 1.0
1187 else {
1188 // we need to check all characters as per production rules of XML 1.0
1189 int i = 0;
1190 while (i < datalength) {
1191 if (XMLChar.isInvalid(dataarray[i++])) {
1192 // check if this is a supplemental character
1193 char ch = dataarray[i - 1];
1194 if (XMLChar.isHighSurrogate(ch) && i < datalength) {
1195 char ch2 = dataarray[i++];
1196 if (XMLChar.isLowSurrogate(ch2)
1197 && XMLChar.isSupplemental(
1198 XMLChar.supplemental(ch, ch2))) {
1199 continue;
1200 }
1201 }
1202 // Reference to invalid character which is returned
1203 refInvalidChar = new Character(ch);
1204 return false;
1205 }
1206 }
1207 } // end-else fDocument.isXMLVersion()
1208
1209 return true;
1210 } // isXMLCharWF
1211
1212 /**
1213 * Checks if a XML character is well-formed. If there is a problem with
1214 * the character a non-null Character is returned else null is returned.
1215 *
1216 * @param characters A String of characters to be checked for Well-Formedness
1217 * @return Character A reference to the character to be returned that was determined invalid.
1218 */
1219 protected Character isWFXMLChar(String chardata) {
1220 Character refInvalidChar;
1221 if (chardata == null || (chardata.length() == 0)) {
1222 return null;
1223 }
1224
1225 char[] dataarray = chardata.toCharArray();
1226 int datalength = dataarray.length;
1227
1228 // version of the document is XML 1.1
1229 if (fIsXMLVersion11) {
1230 //we need to check all characters as per production rules of XML11
1231 int i = 0;
1232 while (i < datalength) {
1233 if (XML11Char.isXML11Invalid(dataarray[i++])) {
1234 // check if this is a supplemental character
1235 char ch = dataarray[i - 1];
1236 if (XMLChar.isHighSurrogate(ch) && i < datalength) {
1237 char ch2 = dataarray[i++];
1238 if (XMLChar.isLowSurrogate(ch2)
1239 && XMLChar.isSupplemental(
1240 XMLChar.supplemental(ch, ch2))) {
1241 continue;
1242 }
1243 }
1244 // Reference to invalid character which is returned
1245 refInvalidChar = new Character(ch);
1246 return refInvalidChar;
1247 }
1248 }
1249 } // version of the document is XML 1.0
1250 else {
1251 // we need to check all characters as per production rules of XML 1.0
1252 int i = 0;
1253 while (i < datalength) {
1254 if (XMLChar.isInvalid(dataarray[i++])) {
1255 // check if this is a supplemental character
1256 char ch = dataarray[i - 1];
1257 if (XMLChar.isHighSurrogate(ch) && i < datalength) {
1258 char ch2 = dataarray[i++];
1259 if (XMLChar.isLowSurrogate(ch2)
1260 && XMLChar.isSupplemental(
1261 XMLChar.supplemental(ch, ch2))) {
1262 continue;
1263 }
1264 }
1265 // Reference to invalid character which is returned
1266 refInvalidChar = new Character(ch);
1267 return refInvalidChar;
1268 }
1269 }
1270 } // end-else fDocument.isXMLVersion()
1271
1272 return null;
1273 } // isXMLCharWF
1274
1275 /**
1276 * Checks if a comment node is well-formed
1277 *
1278 * @param data The contents of the comment node
1279 * @return a boolean indiacating if the comment is well-formed or not.
1280 */
1281 protected void isCommentWellFormed(String data) {
1282 if (data == null || (data.length() == 0)) {
1283 return;
1284 }
1285
1286 char[] dataarray = data.toCharArray();
1287 int datalength = dataarray.length;
1288
1289 // version of the document is XML 1.1
1290 if (fIsXMLVersion11) {
1291 // we need to check all chracters as per production rules of XML11
1292 int i = 0;
1293 while (i < datalength) {
1294 char c = dataarray[i++];
1295 if (XML11Char.isXML11Invalid(c)) {
1296 // check if this is a supplemental character
1297 if (XMLChar.isHighSurrogate(c) && i < datalength) {
1298 char c2 = dataarray[i++];
1299 if (XMLChar.isLowSurrogate(c2)
1300 && XMLChar.isSupplemental(
1301 XMLChar.supplemental(c, c2))) {
1302 continue;
1303 }
1304 }
1305 String msg =
1306 Utils.messages.createMessage(
1307 MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
1308 new Object[] { new Character(c)});
1309
1310 if (fErrorHandler != null) {
1311 fErrorHandler.handleError(
1312 new DOMErrorImpl(
1313 DOMError.SEVERITY_FATAL_ERROR,
1314 msg,
1315 MsgKey.ER_WF_INVALID_CHARACTER,
1316 null,
1317 null,
1318 null));
1319 }
1320 } else if (c == '-' && i < datalength && dataarray[i] == '-') {
1321 String msg =
1322 Utils.messages.createMessage(
1323 MsgKey.ER_WF_DASH_IN_COMMENT,
1324 null);
1325
1326 if (fErrorHandler != null) {
1327 fErrorHandler.handleError(
1328 new DOMErrorImpl(
1329 DOMError.SEVERITY_FATAL_ERROR,
1330 msg,
1331 MsgKey.ER_WF_INVALID_CHARACTER,
1332 null,
1333 null,
1334 null));
1335 }
1336 }
1337 }
1338 } // version of the document is XML 1.0
1339 else {
1340 // we need to check all chracters as per production rules of XML 1.0
1341 int i = 0;
1342 while (i < datalength) {
1343 char c = dataarray[i++];
1344 if (XMLChar.isInvalid(c)) {
1345 // check if this is a supplemental character
1346 if (XMLChar.isHighSurrogate(c) && i < datalength) {
1347 char c2 = dataarray[i++];
1348 if (XMLChar.isLowSurrogate(c2)
1349 && XMLChar.isSupplemental(
1350 XMLChar.supplemental(c, c2))) {
1351 continue;
1352 }
1353 }
1354 String msg =
1355 Utils.messages.createMessage(
1356 MsgKey.ER_WF_INVALID_CHARACTER_IN_COMMENT,
1357 new Object[] { new Character(c)});
1358
1359 if (fErrorHandler != null) {
1360 fErrorHandler.handleError(
1361 new DOMErrorImpl(
1362 DOMError.SEVERITY_FATAL_ERROR,
1363 msg,
1364 MsgKey.ER_WF_INVALID_CHARACTER,
1365 null,
1366 null,
1367 null));
1368 }
1369 } else if (c == '-' && i < datalength && dataarray[i] == '-') {
1370 String msg =
1371 Utils.messages.createMessage(
1372 MsgKey.ER_WF_DASH_IN_COMMENT,
1373 null);
1374
1375 if (fErrorHandler != null) {
1376 fErrorHandler.handleError(
1377 new DOMErrorImpl(
1378 DOMError.SEVERITY_FATAL_ERROR,
1379 msg,
1380 MsgKey.ER_WF_INVALID_CHARACTER,
1381 null,
1382 null,
1383 null));
1384 }
1385 }
1386 }
1387 }
1388 return;
1389 }
1390
1391 /**
1392 * Checks if an element node is well-formed, by checking its Name for well-formedness.
1393 *
1394 * @param data The contents of the comment node
1395 * @return a boolean indiacating if the comment is well-formed or not.
1396 */
1397 protected void isElementWellFormed(Node node) {
1398 boolean isNameWF = false;
1399 if ((fFeatures & NAMESPACES) != 0) {
1400 isNameWF =
1401 isValidQName(
1402 node.getPrefix(),
1403 node.getLocalName(),
1404 fIsXMLVersion11);
1405 } else {
1406 isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11);
1407 }
1408
1409 if (!isNameWF) {
1410 String msg =
1411 Utils.messages.createMessage(
1412 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1413 new Object[] { "Element", node.getNodeName()});
1414
1415 if (fErrorHandler != null) {
1416 fErrorHandler.handleError(
1417 new DOMErrorImpl(
1418 DOMError.SEVERITY_FATAL_ERROR,
1419 msg,
1420 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1421 null,
1422 null,
1423 null));
1424 }
1425 }
1426 }
1427
1428 /**
1429 * Checks if an attr node is well-formed, by checking it's Name and value
1430 * for well-formedness.
1431 *
1432 * @param data The contents of the comment node
1433 * @return a boolean indiacating if the comment is well-formed or not.
1434 */
1435 protected void isAttributeWellFormed(Node node) {
1436 boolean isNameWF = false;
1437 if ((fFeatures & NAMESPACES) != 0) {
1438 isNameWF =
1439 isValidQName(
1440 node.getPrefix(),
1441 node.getLocalName(),
1442 fIsXMLVersion11);
1443 } else {
1444 isNameWF = isXMLName(node.getNodeName(), fIsXMLVersion11);
1445 }
1446
1447 if (!isNameWF) {
1448 String msg =
1449 Utils.messages.createMessage(
1450 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1451 new Object[] { "Attr", node.getNodeName()});
1452
1453 if (fErrorHandler != null) {
1454 fErrorHandler.handleError(
1455 new DOMErrorImpl(
1456 DOMError.SEVERITY_FATAL_ERROR,
1457 msg,
1458 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1459 null,
1460 null,
1461 null));
1462 }
1463 }
1464
1465 // Check the Attr's node value
1466 // WFC: No < in Attribute Values
1467 String value = node.getNodeValue();
1468 if (value.indexOf('<') >= 0) {
1469 String msg =
1470 Utils.messages.createMessage(
1471 MsgKey.ER_WF_LT_IN_ATTVAL,
1472 new Object[] {
1473 ((Attr) node).getOwnerElement().getNodeName(),
1474 node.getNodeName()});
1475
1476 if (fErrorHandler != null) {
1477 fErrorHandler.handleError(
1478 new DOMErrorImpl(
1479 DOMError.SEVERITY_FATAL_ERROR,
1480 msg,
1481 MsgKey.ER_WF_LT_IN_ATTVAL,
1482 null,
1483 null,
1484 null));
1485 }
1486 }
1487
1488 // we need to loop through the children of attr nodes and check their values for
1489 // well-formedness
1490 NodeList children = node.getChildNodes();
1491 for (int i = 0; i < children.getLength(); i++) {
1492 Node child = children.item(i);
1493 // An attribute node with no text or entity ref child for example
1494 // doc.createAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ns");
1495 // followes by
1496 // element.setAttributeNodeNS(attribute);
1497 // can potentially lead to this situation. If the attribute
1498 // was a prefix Namespace attribute declaration then then DOM Core
1499 // should have some exception defined for this.
1500 if (child == null) {
1501 // we should probably report an error
1502 continue;
1503 }
1504 switch (child.getNodeType()) {
1505 case Node.TEXT_NODE :
1506 isTextWellFormed((Text) child);
1507 break;
1508 case Node.ENTITY_REFERENCE_NODE :
1509 isEntityReferneceWellFormed((EntityReference) child);
1510 break;
1511 default :
1512 }
1513 }
1514
1515 // TODO:
1516 // WFC: Check if the attribute prefix is bound to
1517 // http://www.w3.org/2000/xmlns/
1518
1519 // WFC: Unique Att Spec
1520 // Perhaps pass a seen boolean value to this method. serializeAttList will determine
1521 // if the attr was seen before.
1522 }
1523
1524 /**
1525 * Checks if a PI node is well-formed, by checking it's Name and data
1526 * for well-formedness.
1527 *
1528 * @param data The contents of the comment node
1529 */
1530 protected void isPIWellFormed(ProcessingInstruction node) {
1531 // Is the PI Target a valid XML name
1532 if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) {
1533 String msg =
1534 Utils.messages.createMessage(
1535 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1536 new Object[] { "ProcessingInstruction", node.getTarget()});
1537
1538 if (fErrorHandler != null) {
1539 fErrorHandler.handleError(
1540 new DOMErrorImpl(
1541 DOMError.SEVERITY_FATAL_ERROR,
1542 msg,
1543 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1544 null,
1545 null,
1546 null));
1547 }
1548 }
1549
1550 // Does the PI Data carry valid XML characters
1551
1552 // REVISIT: Should we check if the PI DATA contains a ?> ???
1553 Character invalidChar = isWFXMLChar(node.getData());
1554 if (invalidChar != null) {
1555 String msg =
1556 Utils.messages.createMessage(
1557 MsgKey.ER_WF_INVALID_CHARACTER_IN_PI,
1558 new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });
1559
1560 if (fErrorHandler != null) {
1561 fErrorHandler.handleError(
1562 new DOMErrorImpl(
1563 DOMError.SEVERITY_FATAL_ERROR,
1564 msg,
1565 MsgKey.ER_WF_INVALID_CHARACTER,
1566 null,
1567 null,
1568 null));
1569 }
1570 }
1571 }
1572
1573 /**
1574 * Checks if an CDATASection node is well-formed, by checking it's data
1575 * for well-formedness. Note that the presence of a CDATA termination mark
1576 * in the contents of a CDATASection is handled by the parameter
1577 * spli-cdata-sections
1578 *
1579 * @param data The contents of the comment node
1580 */
1581 protected void isCDATASectionWellFormed(CDATASection node) {
1582 // Does the data valid XML character data
1583 Character invalidChar = isWFXMLChar(node.getData());
1584 //if (!isWFXMLChar(node.getData(), invalidChar)) {
1585 if (invalidChar != null) {
1586 String msg =
1587 Utils.messages.createMessage(
1588 MsgKey.ER_WF_INVALID_CHARACTER_IN_CDATA,
1589 new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });
1590
1591 if (fErrorHandler != null) {
1592 fErrorHandler.handleError(
1593 new DOMErrorImpl(
1594 DOMError.SEVERITY_FATAL_ERROR,
1595 msg,
1596 MsgKey.ER_WF_INVALID_CHARACTER,
1597 null,
1598 null,
1599 null));
1600 }
1601 }
1602 }
1603
1604 /**
1605 * Checks if an Text node is well-formed, by checking if it contains invalid
1606 * XML characters.
1607 *
1608 * @param data The contents of the comment node
1609 */
1610 protected void isTextWellFormed(Text node) {
1611 // Does the data valid XML character data
1612 Character invalidChar = isWFXMLChar(node.getData());
1613 if (invalidChar != null) {
1614 String msg =
1615 Utils.messages.createMessage(
1616 MsgKey.ER_WF_INVALID_CHARACTER_IN_TEXT,
1617 new Object[] { Integer.toHexString(Character.getNumericValue(invalidChar.charValue())) });
1618
1619 if (fErrorHandler != null) {
1620 fErrorHandler.handleError(
1621 new DOMErrorImpl(
1622 DOMError.SEVERITY_FATAL_ERROR,
1623 msg,
1624 MsgKey.ER_WF_INVALID_CHARACTER,
1625 null,
1626 null,
1627 null));
1628 }
1629 }
1630 }
1631
1632 /**
1633 * Checks if an EntityRefernece node is well-formed, by checking it's node name. Then depending
1634 * on whether it is referenced in Element content or in an Attr Node, checks if the EntityReference
1635 * references an unparsed entity or a external entity and if so throws raises the
1636 * appropriate well-formedness error.
1637 *
1638 * @param data The contents of the comment node
1639 * @parent The parent of the EntityReference Node
1640 */
1641 protected void isEntityReferneceWellFormed(EntityReference node) {
1642 // Is the EntityReference name a valid XML name
1643 if (!isXMLName(node.getNodeName(), fIsXMLVersion11)) {
1644 String msg =
1645 Utils.messages.createMessage(
1646 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1647 new Object[] { "EntityReference", node.getNodeName()});
1648
1649 if (fErrorHandler != null) {
1650 fErrorHandler.handleError(
1651 new DOMErrorImpl(
1652 DOMError.SEVERITY_FATAL_ERROR,
1653 msg,
1654 MsgKey.ER_WF_INVALID_CHARACTER_IN_NODE_NAME,
1655 null,
1656 null,
1657 null));
1658 }
1659 }
1660
1661 // determine the parent node
1662 Node parent = node.getParentNode();
1663
1664 // Traverse the declared entities and check if the nodeName and namespaceURI
1665 // of the EntityReference matches an Entity. If so, check the if the notationName
1666 // is not null, if so, report an error.
1667 DocumentType docType = node.getOwnerDocument().getDoctype();
1668 if (docType != null) {
1669 NamedNodeMap entities = docType.getEntities();
1670 for (int i = 0; i < entities.getLength(); i++) {
1671 Entity ent = (Entity) entities.item(i);
1672
1673 String nodeName =
1674 node.getNodeName() == null ? "" : node.getNodeName();
1675 String nodeNamespaceURI =
1676 node.getNamespaceURI() == null
1677 ? ""
1678 : node.getNamespaceURI();
1679 String entName =
1680 ent.getNodeName() == null ? "" : ent.getNodeName();
1681 String entNamespaceURI =
1682 ent.getNamespaceURI() == null ? "" : ent.getNamespaceURI();
1683 // If referenced in Element content
1684 // WFC: Parsed Entity
1685 if (parent.getNodeType() == Node.ELEMENT_NODE) {
1686 if (entNamespaceURI.equals(nodeNamespaceURI)
1687 && entName.equals(nodeName)) {
1688
1689 if (ent.getNotationName() != null) {
1690 String msg =
1691 Utils.messages.createMessage(
1692 MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
1693 new Object[] { node.getNodeName()});
1694
1695 if (fErrorHandler != null) {
1696 fErrorHandler.handleError(
1697 new DOMErrorImpl(
1698 DOMError.SEVERITY_FATAL_ERROR,
1699 msg,
1700 MsgKey.ER_WF_REF_TO_UNPARSED_ENT,
1701 null,
1702 null,
1703 null));
1704 }
1705 }
1706 }
1707 } // end if WFC: Parsed Entity
1708
1709 // If referenced in an Attr value
1710 // WFC: No External Entity References
1711 if (parent.getNodeType() == Node.ATTRIBUTE_NODE) {
1712 if (entNamespaceURI.equals(nodeNamespaceURI)
1713 && entName.equals(nodeName)) {
1714
1715 if (ent.getPublicId() != null
1716 || ent.getSystemId() != null
1717 || ent.getNotationName() != null) {
1718 String msg =
1719 Utils.messages.createMessage(
1720 MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
1721 new Object[] { node.getNodeName()});
1722
1723 if (fErrorHandler != null) {
1724 fErrorHandler.handleError(
1725 new DOMErrorImpl(
1726 DOMError.SEVERITY_FATAL_ERROR,
1727 msg,
1728 MsgKey.ER_WF_REF_TO_EXTERNAL_ENT,
1729 null,
1730 null,
1731 null));
1732 }
1733 }
1734 }
1735 } //end if WFC: No External Entity References
1736 }
1737 }
1738 } // isEntityReferneceWellFormed
1739
1740 /**
1741 * If the configuration parameter "namespaces" is set to true, this methods
1742 * checks if an entity whose replacement text contains unbound namespace
1743 * prefixes is referenced in a location where there are no bindings for
1744 * the namespace prefixes and if so raises a LSException with the error-type
1745 * "unbound-prefix-in-entity-reference"
1746 *
1747 * @param Node, The EntityReference nodes whose children are to be checked
1748 */
1749 protected void checkUnboundPrefixInEntRef(Node node) {
1750 Node child, next;
1751 for (child = node.getFirstChild(); child != null; child = next) {
1752 next = child.getNextSibling();
1753
1754 if (child.getNodeType() == Node.ELEMENT_NODE) {
1755
1756 //If a NamespaceURI is not declared for the current
1757 //node's prefix, raise a fatal error.
1758 String prefix = child.getPrefix();
1759 if (prefix != null
1760 && fNSBinder.getURI(prefix) == null) {
1761 String msg =
1762 Utils.messages.createMessage(
1763 MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
1764 new Object[] {
1765 node.getNodeName(),
1766 child.getNodeName(),
1767 prefix });
1768
1769 if (fErrorHandler != null) {
1770 fErrorHandler.handleError(
1771 new DOMErrorImpl(
1772 DOMError.SEVERITY_FATAL_ERROR,
1773 msg,
1774 MsgKey.ER_ELEM_UNBOUND_PREFIX_IN_ENTREF,
1775 null,
1776 null,
1777 null));
1778 }
1779 }
1780
1781 NamedNodeMap attrs = child.getAttributes();
1782
1783 for (int i = 0; i < attrs.getLength(); i++) {
1784 String attrPrefix = attrs.item(i).getPrefix();
1785 if (attrPrefix != null
1786 && fNSBinder.getURI(attrPrefix) == null) {
1787 String msg =
1788 Utils.messages.createMessage(
1789 MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
1790 new Object[] {
1791 node.getNodeName(),
1792 child.getNodeName(),
1793 attrs.item(i)});
1794
1795 if (fErrorHandler != null) {
1796 fErrorHandler.handleError(
1797 new DOMErrorImpl(
1798 DOMError.SEVERITY_FATAL_ERROR,
1799 msg,
1800 MsgKey.ER_ATTR_UNBOUND_PREFIX_IN_ENTREF,
1801 null,
1802 null,
1803 null));
1804 }
1805 }
1806 }
1807 }
1808
1809 if (child.hasChildNodes()) {
1810 checkUnboundPrefixInEntRef(child);
1811 }
1812 }
1813 }
1814
1815 // ***********************************************************************
1816 // Namespace normalization
1817 // ***********************************************************************
1818 /**
1819 * Records local namespace declarations, to be used for normalization later
1820 *
1821 * @param Node, The element node, whose namespace declarations are to be recorded
1822 */
1823 protected void recordLocalNSDecl(Node node) {
1824 NamedNodeMap atts = ((Element) node).getAttributes();
1825 int length = atts.getLength();
1826
1827 for (int i = 0; i < length; i++) {
1828 Node attr = atts.item(i);
1829
1830 String localName = attr.getLocalName();
1831 String attrPrefix = attr.getPrefix();
1832 String attrValue = attr.getNodeValue();
1833 String attrNS = attr.getNamespaceURI();
1834
1835 localName =
1836 localName == null
1837 || XMLNS_PREFIX.equals(localName) ? "" : localName;
1838 attrPrefix = attrPrefix == null ? "" : attrPrefix;
1839 attrValue = attrValue == null ? "" : attrValue;
1840 attrNS = attrNS == null ? "" : attrNS;
1841
1842 // check if attribute is a namespace decl
1843 if (XMLNS_URI.equals(attrNS)) {
1844
1845 // No prefix may be bound to http://www.w3.org/2000/xmlns/.
1846 if (XMLNS_URI.equals(attrValue)) {
1847 String msg =
1848 Utils.messages.createMessage(
1849 MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
1850 new Object[] { attrPrefix, XMLNS_URI });
1851
1852 if (fErrorHandler != null) {
1853 fErrorHandler.handleError(
1854 new DOMErrorImpl(
1855 DOMError.SEVERITY_ERROR,
1856 msg,
1857 MsgKey.ER_NS_PREFIX_CANNOT_BE_BOUND,
1858 null,
1859 null,
1860 null));
1861 }
1862 } else {
1863 // store the namespace-declaration
1864 if (XMLNS_PREFIX.equals(attrPrefix) ) {
1865 // record valid decl
1866 if (attrValue.length() != 0) {
1867 fNSBinder.declarePrefix(localName, attrValue);
1868 } else {
1869 // Error; xmlns:prefix=""
1870 }
1871 } else { // xmlns
1872 // empty prefix is always bound ("" or some string)
1873 fNSBinder.declarePrefix("", attrValue);
1874 }
1875 }
1876
1877 }
1878 }
1879 }
1880
1881 /**
1882 * Fixes an element's namespace
1883 *
1884 * @param Node, The element node, whose namespace is to be fixed
1885 */
1886 protected void fixupElementNS(Node node) throws SAXException {
1887 String namespaceURI = ((Element) node).getNamespaceURI();
1888 String prefix = ((Element) node).getPrefix();
1889 String localName = ((Element) node).getLocalName();
1890
1891 if (namespaceURI != null) {
1892 //if ( Element's prefix/namespace pair (or default namespace,
1893 // if no prefix) are within the scope of a binding )
1894 prefix = prefix == null ? "" : prefix;
1895 String inScopeNamespaceURI = fNSBinder.getURI(prefix);
1896
1897 if ((inScopeNamespaceURI != null
1898 && inScopeNamespaceURI.equals(namespaceURI))) {
1899 // do nothing, declaration in scope is inherited
1900
1901 } else {
1902 // Create a local namespace declaration attr for this namespace,
1903 // with Element's current prefix (or a default namespace, if
1904 // no prefix). If there's a conflicting local declaration
1905 // already present, change its value to use this namespace.
1906
1907 // Add the xmlns declaration attribute
1908 //fNSBinder.pushNamespace(prefix, namespaceURI, fElementDepth);
1909 if ((fFeatures & NAMESPACEDECLS) != 0) {
1910 if ("".equals(prefix) || "".equals(namespaceURI)) {
1911 ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, namespaceURI);
1912 } else {
1913 ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX + ":" + prefix, namespaceURI);
1914 }
1915 }
1916 fLocalNSBinder.declarePrefix(prefix, namespaceURI);
1917 fNSBinder.declarePrefix(prefix, namespaceURI);
1918
1919 }
1920 } else {
1921 // Element has no namespace
1922 // DOM Level 1
1923 if (localName == null || "".equals(localName)) {
1924 // DOM Level 1 node!
1925 String msg =
1926 Utils.messages.createMessage(
1927 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
1928 new Object[] { node.getNodeName()});
1929
1930 if (fErrorHandler != null) {
1931 fErrorHandler.handleError(
1932 new DOMErrorImpl(
1933 DOMError.SEVERITY_ERROR,
1934 msg,
1935 MsgKey.ER_NULL_LOCAL_ELEMENT_NAME,
1936 null,
1937 null,
1938 null));
1939 }
1940 } else {
1941 namespaceURI = fNSBinder.getURI("");
1942 if (namespaceURI !=null && namespaceURI.length() > 0) {
1943 ((Element)node).setAttributeNS(XMLNS_URI, XMLNS_PREFIX, "");
1944 fLocalNSBinder.declarePrefix("", "");
1945 fNSBinder.declarePrefix("", "");
1946 }
1947 }
1948 }
1949 }
1950 /**
1951 * This table is a quick lookup of a property key (String) to the integer that
1952 * is the bit to flip in the fFeatures field, so the integers should have
1953 * values 1,2,4,8,16...
1954 *
1955 */
1956 private static final Hashtable s_propKeys = new Hashtable();
1957 static {
1958
1959 // Initialize the mappings of property keys to bit values (Integer objects)
1960 // or mappings to a String object "", which indicates we are interested
1961 // in the property, but it does not have a simple bit value to flip
1962
1963 // cdata-sections
1964 int i = CDATA;
1965 Integer val = new Integer(i);
1966 s_propKeys.put(
1967 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_CDATA_SECTIONS,
1968 val);
1969
1970 // comments
1971 int i1 = COMMENTS;
1972 val = new Integer(i1);
1973 s_propKeys.put(
1974 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_COMMENTS,
1975 val);
1976
1977 // element-content-whitespace
1978 int i2 = ELEM_CONTENT_WHITESPACE;
1979 val = new Integer(i2);
1980 s_propKeys.put(
1981 DOMConstants.S_DOM3_PROPERTIES_NS
1982 + DOMConstants.DOM_ELEMENT_CONTENT_WHITESPACE,
1983 val);
1984 int i3 = ENTITIES;
1985
1986 // entities
1987 val = new Integer(i3);
1988 s_propKeys.put(
1989 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_ENTITIES,
1990 val);
1991
1992 // namespaces
1993 int i4 = NAMESPACES;
1994 val = new Integer(i4);
1995 s_propKeys.put(
1996 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_NAMESPACES,
1997 val);
1998
1999 // namespace-declarations
2000 int i5 = NAMESPACEDECLS;
2001 val = new Integer(i5);
2002 s_propKeys.put(
2003 DOMConstants.S_DOM3_PROPERTIES_NS
2004 + DOMConstants.DOM_NAMESPACE_DECLARATIONS,
2005 val);
2006
2007 // split-cdata-sections
2008 int i6 = SPLITCDATA;
2009 val = new Integer(i6);
2010 s_propKeys.put(
2011 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_SPLIT_CDATA,
2012 val);
2013
2014 // discard-default-content
2015 int i7 = WELLFORMED;
2016 val = new Integer(i7);
2017 s_propKeys.put(
2018 DOMConstants.S_DOM3_PROPERTIES_NS + DOMConstants.DOM_WELLFORMED,
2019 val);
2020
2021 // discard-default-content
2022 int i8 = DISCARDDEFAULT;
2023 val = new Integer(i8);
2024 s_propKeys.put(
2025 DOMConstants.S_DOM3_PROPERTIES_NS
2026 + DOMConstants.DOM_DISCARD_DEFAULT_CONTENT,
2027 val);
2028
2029 // We are interested in these properties, but they don't have a simple
2030 // bit value to deal with.
2031 s_propKeys.put(
2032 DOMConstants.S_DOM3_PROPERTIES_NS
2033 + DOMConstants.DOM_FORMAT_PRETTY_PRINT,
2034 "");
2035 s_propKeys.put(DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL, "");
2036 s_propKeys.put(
2037 DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.S_XML_VERSION,
2038 "");
2039 s_propKeys.put(DOMConstants.S_XSL_OUTPUT_ENCODING, "");
2040 s_propKeys.put(DOMConstants.S_XERCES_PROPERTIES_NS + DOMConstants.DOM_ENTITIES, "");
2041 }
2042
2043 /**
2044 * Initializes fFeatures based on the DOMConfiguration Parameters set.
2045 *
2046 * @param properties DOMConfiguraiton properties that were set and which are
2047 * to be used while serializing the DOM.
2048 */
2049 protected void initProperties(Properties properties) {
2050
2051 for (Enumeration keys = properties.keys(); keys.hasMoreElements();) {
2052
2053 final String key = (String) keys.nextElement();
2054
2055 // caonical-form
2056 // Other features will be enabled or disabled when this is set to true or false.
2057
2058 // error-handler; set via the constructor
2059
2060 // infoset
2061 // Other features will be enabled or disabled when this is set to true
2062
2063 // A quick lookup for the given set of properties (cdata-sections ...)
2064 final Object iobj = s_propKeys.get(key);
2065 if (iobj != null) {
2066 if (iobj instanceof Integer) {
2067 // Dealing with a property that has a simple bit value that
2068 // we need to set
2069
2070 // cdata-sections
2071 // comments
2072 // element-content-whitespace
2073 // entities
2074 // namespaces
2075 // namespace-declarations
2076 // split-cdata-sections
2077 // well-formed
2078 // discard-default-content
2079 final int BITFLAG = ((Integer) iobj).intValue();
2080 if ((properties.getProperty(key).endsWith("yes"))) {
2081 fFeatures = fFeatures | BITFLAG;
2082 } else {
2083 fFeatures = fFeatures & ~BITFLAG;
2084 }
2085 } else {
2086 // We are interested in the property, but it is not
2087 // a simple bit that we need to set.
2088
2089 if ((DOMConstants.S_DOM3_PROPERTIES_NS
2090 + DOMConstants.DOM_FORMAT_PRETTY_PRINT)
2091 .equals(key)) {
2092 // format-pretty-print; set internally on the serializers via xsl:output properties in LSSerializer
2093 if ((properties.getProperty(key).endsWith("yes"))) {
2094 fSerializer.setIndent(true);
2095 fSerializer.setIndentAmount(3);
2096 } else {
2097 fSerializer.setIndent(false);
2098 }
2099 } else if (
2100 (DOMConstants.S_XSL_OUTPUT_OMIT_XML_DECL).equals(
2101 key)) {
2102 // omit-xml-declaration; set internally on the serializers via xsl:output properties in LSSerializer
2103 if ((properties.getProperty(key).endsWith("yes"))) {
2104 fSerializer.setOmitXMLDeclaration(true);
2105 } else {
2106 fSerializer.setOmitXMLDeclaration(false);
2107 }
2108 } else if (
2109 (
2110 DOMConstants.S_XERCES_PROPERTIES_NS
2111 + DOMConstants.S_XML_VERSION).equals(
2112 key)) {
2113 // Retreive the value of the XML Version attribute via the xml-version
2114 String version = properties.getProperty(key);
2115 if ("1.1".equals(version)) {
2116 fIsXMLVersion11 = true;
2117 fSerializer.setVersion(version);
2118 } else {
2119 fSerializer.setVersion("1.0");
2120 }
2121 } else if (
2122 (DOMConstants.S_XSL_OUTPUT_ENCODING).equals(key)) {
2123 // Retreive the value of the XML Encoding attribute
2124 String encoding = properties.getProperty(key);
2125 if (encoding != null) {
2126 fSerializer.setEncoding(encoding);
2127 }
2128 } else if ((DOMConstants.S_XERCES_PROPERTIES_NS
2129 + DOMConstants.DOM_ENTITIES).equals(key)) {
2130 // Preserve entity references in the document
2131 if ((properties.getProperty(key).endsWith("yes"))) {
2132 fSerializer.setDTDEntityExpansion(false);
2133 }
2134 else {
2135 fSerializer.setDTDEntityExpansion(true);
2136 }
2137 } else {
2138 // We shouldn't get here, ever, now what?
2139 }
2140 }
2141 }
2142 }
2143 // Set the newLine character to use
2144 if (fNewLine != null) {
2145 fSerializer.setOutputProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR, fNewLine);
2146 }
2147 }
2148
2149 } //TreeWalker