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