dom4j - 1.6.1

Clover coverage report - dom4j - 1.6.1
Coverage timestamp: ma mei 16 2005 14:23:01 GMT+01:00
FRAMES NO FRAMES

file stats: | LOC: | 1.006 |  | Methods: | 55
| NCLOC: | 449 |  | Classes: | 1

Source file | Conditionals | Statements | Methods | TOTAL

SAXContentHandler.java

64,4%

79,2%

72,7%

73,8%

1 |  | /*
2 |  | * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3 |  | *
4 |  | * This software is open source.
5 |  | * See the bottom of this file for the licence.
6 |  | */
7 |  |
8 |  | package org.dom4j.io;
9 |  |
10 |  | import java.lang.reflect.Method;
11 |  | import java.util.ArrayList;
12 |  | import java.util.HashMap;
13 |  | import java.util.List;
14 |  | import java.util.Map;
15 |  |
16 |  | import org.dom4j.Branch;
17 |  | import org.dom4j.Document;
18 |  | import org.dom4j.DocumentFactory;
19 |  | import org.dom4j.DocumentType;
20 |  | import org.dom4j.Element;
21 |  | import org.dom4j.ElementHandler;
22 |  | import org.dom4j.Namespace;
23 |  | import org.dom4j.QName;
24 |  | import org.dom4j.dtd.AttributeDecl;
25 |  | import org.dom4j.dtd.ElementDecl;
26 |  | import org.dom4j.dtd.ExternalEntityDecl;
27 |  | import org.dom4j.dtd.InternalEntityDecl;
28 |  | import org.dom4j.tree.AbstractElement;
29 |  | import org.dom4j.tree.NamespaceStack;
30 |  |
31 |  | import org.xml.sax.Attributes;
32 |  | import org.xml.sax.DTDHandler;
33 |  | import org.xml.sax.EntityResolver;
34 |  | import org.xml.sax.InputSource;
35 |  | import org.xml.sax.Locator;
36 |  | import org.xml.sax.SAXException;
37 |  | import org.xml.sax.SAXParseException;
38 |  | import org.xml.sax.ext.DeclHandler;
39 |  | import org.xml.sax.ext.LexicalHandler;
40 |  | import org.xml.sax.helpers.DefaultHandler;
41 |  |
42 |  | /**
43 |  | * <p>
44 |  | * <code>SAXContentHandler</code> builds a dom4j tree via SAX events.
45 |  | * </p>
46 |  | *
47 |  | * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
48 |  | * @version $Revision: 1.61 $
49 |  | */
50 |  | public class SAXContentHandler extends DefaultHandler implements
51 |  | LexicalHandler, DeclHandler, DTDHandler {
52 |  | /** The factory used to create new <code>Document</code> instances */
53 |  | private DocumentFactory documentFactory;
54 |  |
55 |  | /** The document that is being built */
56 |  | private Document document;
57 |  |
58 |  | /** stack of <code>Element</code> objects */
59 |  | private ElementStack elementStack;
60 |  |
61 |  | /** stack of <code>Namespace</code> and <code>QName</code> objects */
62 |  | private NamespaceStack namespaceStack;
63 |  |
64 |  | /** the <code>ElementHandler</code> called as the elements are complete */
65 |  | private ElementHandler elementHandler;
66 |  |
67 |  | /** the Locator */
68 |  | private Locator locator;
69 |  |
70 |  | /** The name of the current entity */
71 |  | private String entity;
72 |  |
73 |  | /** Flag used to indicate that we are inside a DTD section */
74 |  | private boolean insideDTDSection;
75 |  |
76 |  | /** Flag used to indicate that we are inside a CDATA section */
77 |  | private boolean insideCDATASection;
78 |  |
79 |  | /**
80 |  | * buffer to hold contents of cdata section across multiple characters
81 |  | * events
82 |  | */
83 |  | private StringBuffer cdataText;
84 |  |
85 |  | /** namespaces that are available for use */
86 |  | private Map availableNamespaceMap = new HashMap();
87 |  |
88 |  | /** declared namespaces that are not yet available for use */
89 |  | private List declaredNamespaceList = new ArrayList();
90 |  |
91 |  | /** internal DTD declarations */
92 |  | private List internalDTDDeclarations;
93 |  |
94 |  | /** external DTD declarations */
95 |  | private List externalDTDDeclarations;
96 |  |
97 |  | /** The number of namespaces that are declared in the current scope */
98 |  | private int declaredNamespaceIndex;
99 |  |
100 |  | /** The entity resolver */
101 |  | private EntityResolver entityResolver;
102 |  |
103 |  | private InputSource inputSource;
104 |  |
105 |  | /** The current element we are on */
106 |  | private Element currentElement;
107 |  |
108 |  | /** Should internal DTD declarations be expanded into a List in the DTD */
109 |  | private boolean includeInternalDTDDeclarations = false;
110 |  |
111 |  | /** Should external DTD declarations be expanded into a List in the DTD */
112 |  | private boolean includeExternalDTDDeclarations = false;
113 |  |
114 |  | /** The number of levels deep we are inside a startEntity/endEntity call */
115 |  | private int entityLevel;
116 |  |
117 |  | /** Are we in an internal DTD subset? */
118 |  | private boolean internalDTDsubset = false;
119 |  |
120 |  | /** Whether adjacent text nodes should be merged */
121 |  | private boolean mergeAdjacentText = false;
122 |  |
123 |  | /** Have we added text to the buffer */
124 |  | private boolean textInTextBuffer = false;
125 |  |
126 |  | /** Should we ignore comments */
127 |  | private boolean ignoreComments = false;
128 |  |
129 |  | /** Buffer used to concatenate text together */
130 |  | private StringBuffer textBuffer;
131 |  |
132 |  | /** Holds value of property stripWhitespaceText. */
133 |  | private boolean stripWhitespaceText = false;
134 |  |
135 | 19 | public SAXContentHandler() {
136 | 19 | this(DocumentFactory.getInstance());
137 |  | }
138 |  |
139 | 19 | public SAXContentHandler(DocumentFactory documentFactory) {
140 | 19 | this(documentFactory, null);
141 |  | }
142 |  |
143 | 5811 | public SAXContentHandler(DocumentFactory documentFactory,
144 |  | ElementHandler elementHandler) {
145 | 5811 | this(documentFactory, elementHandler, null);
146 | 5811 | this.elementStack = createElementStack();
147 |  | }
148 |  |
149 | 5811 | public SAXContentHandler(DocumentFactory documentFactory,
150 |  | ElementHandler elementHandler, ElementStack elementStack) {
151 | 5811 | this.documentFactory = documentFactory;
152 | 5811 | this.elementHandler = elementHandler;
153 | 5811 | this.elementStack = elementStack;
154 | 5811 | this.namespaceStack = new NamespaceStack(documentFactory);
155 |  | }
156 |  |
157 |  | /**
158 |  | * DOCUMENT ME!
159 |  | *
160 |  | * @return the document that has been or is being built
161 |  | */
162 | 11692 | public Document getDocument() {
163 | 11692 | if (document == null) {
164 | 5814 | document = createDocument();
165 |  | }
166 |  |
167 | 11692 | return document;
168 |  | }
169 |  |
170 |  | // ContentHandler interface
171 |  | // -------------------------------------------------------------------------
172 | 5813 | public void setDocumentLocator(Locator documentLocator) {
173 | 5813 | this.locator = documentLocator;
174 |  | }
175 |  |
176 | 23 | public void processingInstruction(String target, String data)
177 |  | throws SAXException {
178 | 23 | if (mergeAdjacentText && textInTextBuffer) {
179 | 0 | completeCurrentTextNode();
180 |  | }
181 |  |
182 | 23 | if (currentElement != null) {
183 | 1 | currentElement.addProcessingInstruction(target, data);
184 |  | } else {
185 | 22 | getDocument().addProcessingInstruction(target, data);
186 |  | }
187 |  | }
188 |  |
189 | 5884 | public void startPrefixMapping(String prefix, String uri)
190 |  | throws SAXException {
191 | 5884 | namespaceStack.push(prefix, uri);
192 |  | }
193 |  |
194 | 5884 | public void endPrefixMapping(String prefix) throws SAXException {
195 | 5884 | namespaceStack.pop(prefix);
196 | 5884 | declaredNamespaceIndex = namespaceStack.size();
197 |  | }
198 |  |
199 | 5814 | public void startDocument() throws SAXException {
200 |  | // document = createDocument();
201 | 5814 | document = null;
202 | 5814 | currentElement = null;
203 |  |
204 | 5814 | elementStack.clear();
205 |  |
206 | 5814 | if ((elementHandler != null)
207 |  | && (elementHandler instanceof DispatchHandler)) {
208 | 18 | elementStack.setDispatchHandler((DispatchHandler) elementHandler);
209 |  | }
210 |  |
211 | 5814 | namespaceStack.clear();
212 | 5814 | declaredNamespaceIndex = 0;
213 |  |
214 | 5814 | if (mergeAdjacentText && (textBuffer == null)) {
215 | 4 | textBuffer = new StringBuffer();
216 |  | }
217 |  |
218 | 5814 | textInTextBuffer = false;
219 |  | }
220 |  |
221 | 5814 | public void endDocument() throws SAXException {
222 | 5814 | namespaceStack.clear();
223 | 5814 | elementStack.clear();
224 | 5814 | currentElement = null;
225 | 5814 | textBuffer = null;
226 |  | }
227 |  |
228 | 86721 | public void startElement(String namespaceURI, String localName,
229 |  | String qualifiedName, Attributes attributes) throws SAXException {
230 | 86721 | if (mergeAdjacentText && textInTextBuffer) {
231 | 1 | completeCurrentTextNode();
232 |  | }
233 |  |
234 | 86721 | QName qName = namespaceStack.getQName(namespaceURI, localName,
235 |  | qualifiedName);
236 |  |
237 | 86721 | Branch branch = currentElement;
238 |  |
239 | 86721 | if (branch == null) {
240 | 5814 | branch = getDocument();
241 |  | }
242 |  |
243 | 86721 | Element element = branch.addElement(qName);
244 |  |
245 |  | // add all declared namespaces
246 | 86721 | addDeclaredNamespaces(element);
247 |  |
248 |  | // now lets add all attribute values
249 | 86721 | addAttributes(element, attributes);
250 |  |
251 | 86721 | elementStack.pushElement(element);
252 | 86721 | currentElement = element;
253 |  |
254 | 86721 | entity = null; // fixes bug527062
255 |  |
256 | 86721 | if (elementHandler != null) {
257 | 78 | elementHandler.onStart(elementStack);
258 |  | }
259 |  | }
260 |  |
261 | 86721 | public void endElement(String namespaceURI, String localName, String qName)
262 |  | throws SAXException {
263 | 86721 | if (mergeAdjacentText && textInTextBuffer) {
264 | 5 | completeCurrentTextNode();
265 |  | }
266 |  |
267 | 86721 | if ((elementHandler != null) && (currentElement != null)) {
268 | 78 | elementHandler.onEnd(elementStack);
269 |  | }
270 |  |
271 | 86721 | elementStack.popElement();
272 | 86721 | currentElement = elementStack.peekElement();
273 |  | }
274 |  |
275 | 109454 | public void characters(char[] ch, int start, int end) throws SAXException {
276 | 109454 | if (end == 0) {
277 | 0 | return;
278 |  | }
279 |  |
280 | 109454 | if (currentElement != null) {
281 | 109454 | if (entity != null) {
282 | 4 | if (mergeAdjacentText && textInTextBuffer) {
283 | 0 | completeCurrentTextNode();
284 |  | }
285 |  |
286 | 4 | currentElement.addEntity(entity, new String(ch, start, end));
287 | 4 | entity = null;
288 | 109450 | } else if (insideCDATASection) {
289 | 200 | if (mergeAdjacentText && textInTextBuffer) {
290 | 1 | completeCurrentTextNode();
291 |  | }
292 |  |
293 | 200 | cdataText.append(new String(ch, start, end));
294 |  | } else {
295 | 109250 | if (mergeAdjacentText) {
296 | 32 | textBuffer.append(ch, start, end);
297 | 32 | textInTextBuffer = true;
298 |  | } else {
299 | 109218 | currentElement.addText(new String(ch, start, end));
300 |  | }
301 |  | }
302 |  | }
303 |  | }
304 |  |
305 |  | // ErrorHandler interface
306 |  | // -------------------------------------------------------------------------
307 |  |
308 |  | /**
309 |  | * This method is called when a warning occurs during the parsing of the
310 |  | * document. This method does nothing.
311 |  | *
312 |  | * @param exception
313 |  | * DOCUMENT ME!
314 |  | *
315 |  | * @throws SAXException
316 |  | * DOCUMENT ME!
317 |  | */
318 | 0 | public void warning(SAXParseException exception) throws SAXException {
319 |  | // ignore warnings by default
320 |  | }
321 |  |
322 |  | /**
323 |  | * This method is called when an error is detected during parsing such as a
324 |  | * validation error. This method rethrows the exception
325 |  | *
326 |  | * @param exception
327 |  | * DOCUMENT ME!
328 |  | *
329 |  | * @throws SAXException
330 |  | * DOCUMENT ME!
331 |  | */
332 | 0 | public void error(SAXParseException exception) throws SAXException {
333 | 0 | throw exception;
334 |  | }
335 |  |
336 |  | /**
337 |  | * This method is called when a fatal error occurs during parsing. This
338 |  | * method rethrows the exception
339 |  | *
340 |  | * @param exception
341 |  | * DOCUMENT ME!
342 |  | *
343 |  | * @throws SAXException
344 |  | * DOCUMENT ME!
345 |  | */
346 | 0 | public void fatalError(SAXParseException exception) throws SAXException {
347 | 0 | throw exception;
348 |  | }
349 |  |
350 |  | // LexicalHandler interface
351 |  | // -------------------------------------------------------------------------
352 | 11 | public void startDTD(String name, String publicId, String systemId)
353 |  | throws SAXException {
354 | 11 | getDocument().addDocType(name, publicId, systemId);
355 | 11 | insideDTDSection = true;
356 | 11 | internalDTDsubset = true;
357 |  | }
358 |  |
359 | 11 | public void endDTD() throws SAXException {
360 | 11 | insideDTDSection = false;
361 |  |
362 | 11 | DocumentType docType = getDocument().getDocType();
363 |  |
364 | 11 | if (docType != null) {
365 | 11 | if (internalDTDDeclarations != null) {
366 | 4 | docType.setInternalDeclarations(internalDTDDeclarations);
367 |  | }
368 |  |
369 | 11 | if (externalDTDDeclarations != null) {
370 | 3 | docType.setExternalDeclarations(externalDTDDeclarations);
371 |  | }
372 |  | }
373 |  |
374 | 11 | internalDTDDeclarations = null;
375 | 11 | externalDTDDeclarations = null;
376 |  | }
377 |  |
378 | 433 | public void startEntity(String name) throws SAXException {
379 | 433 | ++entityLevel;
380 |  |
381 |  | // Ignore DTD references
382 | 433 | entity = null;
383 |  |
384 | 433 | if (!insideDTDSection) {
385 | 429 | if (!isIgnorableEntity(name)) {
386 | 5 | entity = name;
387 |  | }
388 |  | }
389 |  |
390 |  | // internal DTD subsets can only appear outside of a
391 |  | // startEntity/endEntity block
392 |  | // see the startDTD method in
393 |  | // http://dom4j.org/javadoc/org/xml/sax/ext/LexicalHandler.html
394 | 433 | internalDTDsubset = false;
395 |  | }
396 |  |
397 | 433 | public void endEntity(String name) throws SAXException {
398 | 433 | --entityLevel;
399 | 433 | entity = null;
400 |  |
401 | 433 | if (entityLevel == 0) {
402 | 433 | internalDTDsubset = true;
403 |  | }
404 |  | }
405 |  |
406 | 70 | public void startCDATA() throws SAXException {
407 | 70 | insideCDATASection = true;
408 | 70 | cdataText = new StringBuffer();
409 |  | }
410 |  |
411 | 70 | public void endCDATA() throws SAXException {
412 | 70 | insideCDATASection = false;
413 | 70 | currentElement.addCDATA(cdataText.toString());
414 |  | }
415 |  |
416 | 445 | public void comment(char[] ch, int start, int end) throws SAXException {
417 | 445 | if (!ignoreComments) {
418 | 445 | if (mergeAdjacentText && textInTextBuffer) {
419 | 1 | completeCurrentTextNode();
420 |  | }
421 |  |
422 | 445 | String text = new String(ch, start, end);
423 |  |
424 | 445 | if (!insideDTDSection && (text.length() > 0)) {
425 | 442 | if (currentElement != null) {
426 | 422 | currentElement.addComment(text);
427 |  | } else {
428 | 20 | getDocument().addComment(text);
429 |  | }
430 |  | }
431 |  | }
432 |  | }
433 |  |
434 |  | // DeclHandler interface
435 |  | // -------------------------------------------------------------------------
436 |  |
437 |  | /**
438 |  | * Report an element type declaration.
439 |  | *
440 |  | * <p>
441 |  | * The content model will consist of the string "EMPTY", the string "ANY",
442 |  | * or a parenthesised group, optionally followed by an occurrence indicator.
443 |  | * The model will be normalized so that all parameter entities are fully
444 |  | * resolved and all whitespace is removed,and will include the enclosing
445 |  | * parentheses. Other normalization (such as removing redundant parentheses
446 |  | * or simplifying occurrence indicators) is at the discretion of the parser.
447 |  | * </p>
448 |  | *
449 |  | * @param name
450 |  | * The element type name.
451 |  | * @param model
452 |  | * The content model as a normalized string.
453 |  | *
454 |  | * @exception SAXException
455 |  | * The application may raise an exception.
456 |  | */
457 | 6 | public void elementDecl(String name, String model) throws SAXException {
458 | 6 | if (internalDTDsubset) {
459 | 3 | if (includeInternalDTDDeclarations) {
460 | 3 | addDTDDeclaration(new ElementDecl(name, model));
461 |  | }
462 |  | } else {
463 | 3 | if (includeExternalDTDDeclarations) {
464 | 3 | addExternalDTDDeclaration(new ElementDecl(name, model));
465 |  | }
466 |  | }
467 |  | }
468 |  |
469 |  | /**
470 |  | * Report an attribute type declaration.
471 |  | *
472 |  | * <p>
473 |  | * Only the effective (first) declaration for an attribute will be reported.
474 |  | * The type will be one of the strings "CDATA", "ID", "IDREF", "IDREFS",
475 |  | * "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", a parenthesized token group
476 |  | * with the separator "|" and all whitespace removed, or the word "NOTATION"
477 |  | * followed by a space followed by a parenthesized token group with all
478 |  | * whitespace removed.
479 |  | * </p>
480 |  | *
481 |  | * <p>
482 |  | * Any parameter entities in the attribute value will be expanded, but
483 |  | * general entities will not.
484 |  | * </p>
485 |  | *
486 |  | * @param eName
487 |  | * The name of the associated element.
488 |  | * @param aName
489 |  | * The name of the attribute.
490 |  | * @param type
491 |  | * A string representing the attribute type.
492 |  | * @param valueDefault
493 |  | * A string representing the attribute default ("#IMPLIED",
494 |  | * "#REQUIRED", or "#FIXED") or null if none of these applies.
495 |  | * @param val
496 |  | * A string representing the attribute's default value, or null
497 |  | * if there is none.
498 |  | *
499 |  | * @exception SAXException
500 |  | * The application may raise an exception.
501 |  | */
502 | 3 | public void attributeDecl(String eName, String aName, String type,
503 |  | String valueDefault, String val) throws SAXException {
504 | 3 | if (internalDTDsubset) {
505 | 3 | if (includeInternalDTDDeclarations) {
506 | 3 | addDTDDeclaration(new AttributeDecl(eName, aName, type,
507 |  | valueDefault, val));
508 |  | }
509 |  | } else {
510 | 0 | if (includeExternalDTDDeclarations) {
511 | 0 | addExternalDTDDeclaration(new AttributeDecl(eName, aName, type,
512 |  | valueDefault, val));
513 |  | }
514 |  | }
515 |  | }
516 |  |
517 |  | /**
518 |  | * Report an internal entity declaration.
519 |  | *
520 |  | * <p>
521 |  | * Only the effective (first) declaration for each entity will be reported.
522 |  | * All parameter entities in the value will be expanded, but general
523 |  | * entities will not.
524 |  | * </p>
525 |  | *
526 |  | * @param name
527 |  | * The name of the entity. If it is a parameter entity, the name
528 |  | * will begin with '%'.
529 |  | * @param value
530 |  | * The replacement text of the entity.
531 |  | *
532 |  | * @exception SAXException
533 |  | * The application may raise an exception.
534 |  | *
535 |  | * @see #externalEntityDecl
536 |  | * @see org.xml.sax.DTDHandler#unparsedEntityDecl
537 |  | */
538 | 13 | public void internalEntityDecl(String name, String value)
539 |  | throws SAXException {
540 | 13 | if (internalDTDsubset) {
541 | 13 | if (includeInternalDTDDeclarations) {
542 | 13 | addDTDDeclaration(new InternalEntityDecl(name, value));
543 |  | }
544 |  | } else {
545 | 0 | if (includeExternalDTDDeclarations) {
546 | 0 | addExternalDTDDeclaration(new InternalEntityDecl(name, value));
547 |  | }
548 |  | }
549 |  | }
550 |  |
551 |  | /**
552 |  | * Report a parsed external entity declaration.
553 |  | *
554 |  | * <p>
555 |  | * Only the effective (first) declaration for each entity will be reported.
556 |  | * </p>
557 |  | *
558 |  | * @param name
559 |  | * The name of the entity. If it is a parameter entity, the name
560 |  | * will begin with '%'.
561 |  | * @param publicId
562 |  | * The declared public identifier of the entity, or null if none
563 |  | * was declared.
564 |  | * @param sysId
565 |  | * The declared system identifier of the entity.
566 |  | *
567 |  | * @exception SAXException
568 |  | * The application may raise an exception.
569 |  | *
570 |  | * @see #internalEntityDecl
571 |  | * @see org.xml.sax.DTDHandler#unparsedEntityDecl
572 |  | */
573 | 0 | public void externalEntityDecl(String name, String publicId, String sysId)
574 |  | throws SAXException {
575 | 0 | ExternalEntityDecl declaration = new ExternalEntityDecl(name, publicId,
576 |  | sysId);
577 |  |
578 | 0 | if (internalDTDsubset) {
579 | 0 | if (includeInternalDTDDeclarations) {
580 | 0 | addDTDDeclaration(declaration);
581 |  | }
582 |  | } else {
583 | 0 | if (includeExternalDTDDeclarations) {
584 | 0 | addExternalDTDDeclaration(declaration);
585 |  | }
586 |  | }
587 |  | }
588 |  |
589 |  | // DTDHandler interface
590 |  | // -------------------------------------------------------------------------
591 |  |
592 |  | /**
593 |  | * Receive notification of a notation declaration event.
594 |  | *
595 |  | * <p>
596 |  | * It is up to the application to record the notation for later reference,
597 |  | * if necessary.
598 |  | * </p>
599 |  | *
600 |  | * <p>
601 |  | * At least one of publicId and systemId must be non-null. If a system
602 |  | * identifier is present, and it is a URL, the SAX parser must resolve it
603 |  | * fully before passing it to the application through this event.
604 |  | * </p>
605 |  | *
606 |  | * <p>
607 |  | * There is no guarantee that the notation declaration will be reported
608 |  | * before any unparsed entities that use it.
609 |  | * </p>
610 |  | *
611 |  | * @param name
612 |  | * The notation name.
613 |  | * @param publicId
614 |  | * The notation's public identifier, or null if none was given.
615 |  | * @param systemId
616 |  | * The notation's system identifier, or null if none was given.
617 |  | *
618 |  | * @exception SAXException
619 |  | * Any SAX exception, possibly wrapping another exception.
620 |  | *
621 |  | * @see #unparsedEntityDecl
622 |  | * @see org.xml.sax.AttributeList
623 |  | */
624 | 0 | public void notationDecl(String name, String publicId, String systemId)
625 |  | throws SAXException {
626 |  | // #### not supported yet!
627 |  | }
628 |  |
629 |  | /**
630 |  | * Receive notification of an unparsed entity declaration event.
631 |  | *
632 |  | * <p>
633 |  | * Note that the notation name corresponds to a notation reported by the
634 |  | * {@link #notationDecl notationDecl}event. It is up to the application to
635 |  | * record the entity for later reference, if necessary.
636 |  | * </p>
637 |  | *
638 |  | * <p>
639 |  | * If the system identifier is a URL, the parser must resolve it fully
640 |  | * before passing it to the application.
641 |  | * </p>
642 |  | *
643 |  | * @param name
644 |  | * The unparsed entity's name.
645 |  | * @param publicId
646 |  | * The entity's public identifier, or null if none was given.
647 |  | * @param systemId
648 |  | * The entity's system identifier.
649 |  | * @param notationName
650 |  | * The name of the associated notation.
651 |  | *
652 |  | * @exception SAXException
653 |  | * Any SAX exception, possibly wrapping another exception.
654 |  | *
655 |  | * @see #notationDecl
656 |  | * @see org.xml.sax.AttributeList
657 |  | */
658 | 0 | public void unparsedEntityDecl(String name, String publicId,
659 |  | String systemId, String notationName) throws SAXException {
660 |  | // #### not supported yet!
661 |  | }
662 |  |
663 |  | // Properties
664 |  | // -------------------------------------------------------------------------
665 | 0 | public ElementStack getElementStack() {
666 | 0 | return elementStack;
667 |  | }
668 |  |
669 | 0 | public void setElementStack(ElementStack elementStack) {
670 | 0 | this.elementStack = elementStack;
671 |  | }
672 |  |
673 | 0 | public EntityResolver getEntityResolver() {
674 | 0 | return entityResolver;
675 |  | }
676 |  |
677 | 5792 | public void setEntityResolver(EntityResolver entityResolver) {
678 | 5792 | this.entityResolver = entityResolver;
679 |  | }
680 |  |
681 | 0 | public InputSource getInputSource() {
682 | 0 | return inputSource;
683 |  | }
684 |  |
685 | 5792 | public void setInputSource(InputSource inputSource) {
686 | 5792 | this.inputSource = inputSource;
687 |  | }
688 |  |
689 |  | /**
690 |  | * DOCUMENT ME!
691 |  | *
692 |  | * @return whether internal DTD declarations should be expanded into the
693 |  | * DocumentType object or not.
694 |  | */
695 | 0 | public boolean isIncludeInternalDTDDeclarations() {
696 | 0 | return includeInternalDTDDeclarations;
697 |  | }
698 |  |
699 |  | /**
700 |  | * Sets whether internal DTD declarations should be expanded into the
701 |  | * DocumentType object or not.
702 |  | *
703 |  | * @param include
704 |  | * whether or not DTD declarations should be expanded and
705 |  | * included into the DocumentType object.
706 |  | */
707 | 5792 | public void setIncludeInternalDTDDeclarations(boolean include) {
708 | 5792 | this.includeInternalDTDDeclarations = include;
709 |  | }
710 |  |
711 |  | /**
712 |  | * DOCUMENT ME!
713 |  | *
714 |  | * @return whether external DTD declarations should be expanded into the
715 |  | * DocumentType object or not.
716 |  | */
717 | 0 | public boolean isIncludeExternalDTDDeclarations() {
718 | 0 | return includeExternalDTDDeclarations;
719 |  | }
720 |  |
721 |  | /**
722 |  | * Sets whether DTD external declarations should be expanded into the
723 |  | * DocumentType object or not.
724 |  | *
725 |  | * @param include
726 |  | * whether or not DTD declarations should be expanded and
727 |  | * included into the DocumentType object.
728 |  | */
729 | 5792 | public void setIncludeExternalDTDDeclarations(boolean include) {
730 | 5792 | this.includeExternalDTDDeclarations = include;
731 |  | }
732 |  |
733 |  | /**
734 |  | * Returns whether adjacent text nodes should be merged together.
735 |  | *
736 |  | * @return Value of property mergeAdjacentText.
737 |  | */
738 | 0 | public boolean isMergeAdjacentText() {
739 | 0 | return mergeAdjacentText;
740 |  | }
741 |  |
742 |  | /**
743 |  | * Sets whether or not adjacent text nodes should be merged together when
744 |  | * parsing.
745 |  | *
746 |  | * @param mergeAdjacentText
747 |  | * New value of property mergeAdjacentText.
748 |  | */
749 | 5792 | public void setMergeAdjacentText(boolean mergeAdjacentText) {
750 | 5792 | this.mergeAdjacentText = mergeAdjacentText;
751 |  | }
752 |  |
753 |  | /**
754 |  | * Sets whether whitespace between element start and end tags should be
755 |  | * ignored
756 |  | *
757 |  | * @return Value of property stripWhitespaceText.
758 |  | */
759 | 0 | public boolean isStripWhitespaceText() {
760 | 0 | return stripWhitespaceText;
761 |  | }
762 |  |
763 |  | /**
764 |  | * Sets whether whitespace between element start and end tags should be
765 |  | * ignored.
766 |  | *
767 |  | * @param stripWhitespaceText
768 |  | * New value of property stripWhitespaceText.
769 |  | */
770 | 5792 | public void setStripWhitespaceText(boolean stripWhitespaceText) {
771 | 5792 | this.stripWhitespaceText = stripWhitespaceText;
772 |  | }
773 |  |
774 |  | /**
775 |  | * Returns whether we should ignore comments or not.
776 |  | *
777 |  | * @return boolean
778 |  | */
779 | 0 | public boolean isIgnoreComments() {
780 | 0 | return ignoreComments;
781 |  | }
782 |  |
783 |  | /**
784 |  | * Sets whether we should ignore comments or not.
785 |  | *
786 |  | * @param ignoreComments
787 |  | * whether we should ignore comments or not.
788 |  | */
789 | 5792 | public void setIgnoreComments(boolean ignoreComments) {
790 | 5792 | this.ignoreComments = ignoreComments;
791 |  | }
792 |  |
793 |  | // Implementation methods
794 |  | // -------------------------------------------------------------------------
795 |  |
796 |  | /**
797 |  | * If the current text buffer contains any text then create a new text node
798 |  | * with it and add it to the current element
799 |  | */
800 | 8 | protected void completeCurrentTextNode() {
801 | 8 | if (stripWhitespaceText) {
802 | 0 | boolean whitespace = true;
803 |  |
804 | 0 | for (int i = 0, size = textBuffer.length(); i < size; i++) {
805 | 0 | if (!Character.isWhitespace(textBuffer.charAt(i))) {
806 | 0 | whitespace = false;
807 |  |
808 | 0 | break;
809 |  | }
810 |  | }
811 |  |
812 | 0 | if (!whitespace) {
813 | 0 | currentElement.addText(textBuffer.toString());
814 |  | }
815 |  | } else {
816 | 8 | currentElement.addText(textBuffer.toString());
817 |  | }
818 |  |
819 | 8 | textBuffer.setLength(0);
820 | 8 | textInTextBuffer = false;
821 |  | }
822 |  |
823 |  | /**
824 |  | * DOCUMENT ME!
825 |  | *
826 |  | * @return the current document
827 |  | */
828 | 5814 | protected Document createDocument() {
829 | 5814 | String encoding = getEncoding();
830 | 5814 | Document doc = documentFactory.createDocument(encoding);
831 |  |
832 |  | // set the EntityResolver
833 | 5814 | doc.setEntityResolver(entityResolver);
834 |  |
835 | 5814 | if (inputSource != null) {
836 | 5791 | doc.setName(inputSource.getSystemId());
837 |  | }
838 |  |
839 | 5814 | return doc;
840 |  | }
841 |  |
842 | 5814 | private String getEncoding() {
843 | 5814 | if (locator == null) {
844 | 1 | return null;
845 |  | }
846 |  |
847 |  | // use reflection to avoid dependency on Locator2
848 |  | // or other locator implemenations.
849 | 5813 | try {
850 | 5813 | Method m = locator.getClass().getMethod("getEncoding",
851 |  | new Class[] {});
852 |  |
853 | 5801 | if (m != null) {
854 | 5801 | return (String) m.invoke(locator, null);
855 |  | }
856 |  | } catch (Exception e) {
857 |  | // do nothing
858 |  | }
859 |  |
860 |  | // couldn't determine encoding, returning null...
861 | 12 | return null;
862 |  | }
863 |  |
864 |  | /**
865 |  | * a Strategy Method to determine if a given entity name is ignorable
866 |  | *
867 |  | * @param name
868 |  | * DOCUMENT ME!
869 |  | *
870 |  | * @return DOCUMENT ME!
871 |  | */
872 | 429 | protected boolean isIgnorableEntity(String name) {
873 | 429 | return "amp".equals(name) || "apos".equals(name) || "gt".equals(name)
874 |  | || "lt".equals(name) || "quot".equals(name);
875 |  | }
876 |  |
877 |  | /**
878 |  | * Add all namespaces declared before the startElement() SAX event to the
879 |  | * current element so that they are available to child elements and
880 |  | * attributes
881 |  | *
882 |  | * @param element
883 |  | * DOCUMENT ME!
884 |  | */
885 | 86721 | protected void addDeclaredNamespaces(Element element) {
886 | 86701 | Namespace elementNamespace = element.getNamespace();
887 |  |
888 | 86721 | for (int size = namespaceStack.size(); declaredNamespaceIndex < size;
889 |  | declaredNamespaceIndex++) {
890 | 5884 | Namespace namespace = namespaceStack
891 |  | .getNamespace(declaredNamespaceIndex);
892 |  |
893 |  | // if ( namespace != elementNamespace ) {
894 | 5884 | element.add(namespace);
895 |  |
896 |  | // }
897 |  | }
898 |  | }
899 |  |
900 |  | /**
901 |  | * Add all the attributes to the given elements
902 |  | *
903 |  | * @param element
904 |  | * DOCUMENT ME!
905 |  | * @param attributes
906 |  | * DOCUMENT ME!
907 |  | */
908 | 86721 | protected void addAttributes(Element element, Attributes attributes) {
909 |  | // XXXX: as an optimisation, we could deduce this value from the current
910 |  | // SAX parser settings, the SAX namespaces-prefixes feature
911 | 86721 | boolean noNamespaceAttributes = false;
912 |  |
913 | 86721 | if (element instanceof AbstractElement) {
914 |  | // optimised method
915 | 86700 | AbstractElement baseElement = (AbstractElement) element;
916 | 86721 | baseElement.setAttributes(attributes, namespaceStack,
917 |  | noNamespaceAttributes);
918 |  | } else {
919 | 0 | int size = attributes.getLength();
920 |  |
921 | 0 | for (int i = 0; i < size; i++) {
922 | 0 | String attributeQName = attributes.getQName(i);
923 |  |
924 | 0 | if (noNamespaceAttributes
925 |  | || !attributeQName.startsWith("xmlns")) {
926 | 0 | String attributeURI = attributes.getURI(i);
927 | 0 | String attributeLocalName = attributes.getLocalName(i);
928 | 0 | String attributeValue = attributes.getValue(i);
929 |  |
930 | 0 | QName qName = namespaceStack.getAttributeQName(
931 |  | attributeURI, attributeLocalName, attributeQName);
932 | 0 | element.addAttribute(qName, attributeValue);
933 |  | }
934 |  | }
935 |  | }
936 |  | }
937 |  |
938 |  | /**
939 |  | * Adds an internal DTD declaration to the list of declarations
940 |  | *
941 |  | * @param declaration
942 |  | * DOCUMENT ME!
943 |  | */
944 | 19 | protected void addDTDDeclaration(Object declaration) {
945 | 19 | if (internalDTDDeclarations == null) {
946 | 4 | internalDTDDeclarations = new ArrayList();
947 |  | }
948 |  |
949 | 19 | internalDTDDeclarations.add(declaration);
950 |  | }
951 |  |
952 |  | /**
953 |  | * Adds an external DTD declaration to the list of declarations
954 |  | *
955 |  | * @param declaration
956 |  | * DOCUMENT ME!
957 |  | */
958 | 3 | protected void addExternalDTDDeclaration(Object declaration) {
959 | 3 | if (externalDTDDeclarations == null) {
960 | 3 | externalDTDDeclarations = new ArrayList();
961 |  | }
962 |  |
963 | 3 | externalDTDDeclarations.add(declaration);
964 |  | }
965 |  |
966 | 5811 | protected ElementStack createElementStack() {
967 | 5811 | return new ElementStack();
968 |  | }
969 |  | }
970 |  |
971 |  | /*
972 |  | * Redistribution and use of this software and associated documentation
973 |  | * ("Software"), with or without modification, are permitted provided that the
974 |  | * following conditions are met:
975 |  | *
976 |  | * 1. Redistributions of source code must retain copyright statements and
977 |  | * notices. Redistributions must also contain a copy of this document.
978 |  | *
979 |  | * 2. Redistributions in binary form must reproduce the above copyright notice,
980 |  | * this list of conditions and the following disclaimer in the documentation
981 |  | * and/or other materials provided with the distribution.
982 |  | *
983 |  | * 3. The name "DOM4J" must not be used to endorse or promote products derived
984 |  | * from this Software without prior written permission of MetaStuff, Ltd. For
985 |  | * written permission, please contact dom4j-info@metastuff.com.
986 |  | *
987 |  | * 4. Products derived from this Software may not be called "DOM4J" nor may
988 |  | * "DOM4J" appear in their names without prior written permission of MetaStuff,
989 |  | * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
990 |  | *
991 |  | * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
992 |  | *
993 |  | * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
994 |  | * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
995 |  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
996 |  | * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
997 |  | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
998 |  | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
999 |  | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1000 |  | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1001 |  | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1002 |  | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1003 |  | * POSSIBILITY OF SUCH DAMAGE.
1004 |  | *
1005 |  | * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
1006 |  | */