001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: SAX2DTM.java 468653 2006-10-28 07:07:05Z minchau $
020 */
021 package org.apache.xml.dtm.ref.sax2dtm;
022
023 import java.util.Hashtable;
024 import java.util.Vector;
025 import javax.xml.transform.Source;
026 import javax.xml.transform.SourceLocator;
027
028 import org.apache.xml.dtm.*;
029 import org.apache.xml.dtm.ref.*;
030 import org.apache.xml.utils.StringVector;
031 import org.apache.xml.utils.IntVector;
032 import org.apache.xml.utils.FastStringBuffer;
033 import org.apache.xml.utils.IntStack;
034 import org.apache.xml.utils.SuballocatedIntVector;
035 import org.apache.xml.utils.SystemIDResolver;
036 import org.apache.xml.utils.WrappedRuntimeException;
037 import org.apache.xml.utils.XMLString;
038 import org.apache.xml.utils.XMLStringFactory;
039 import org.apache.xml.res.XMLErrorResources;
040 import org.apache.xml.res.XMLMessages;
041 import org.xml.sax.*;
042 import org.xml.sax.ext.*;
043
044 /**
045 * This class implements a DTM that tends to be optimized more for speed than
046 * for compactness, that is constructed via SAX2 ContentHandler events.
047 */
048 public class SAX2DTM extends DTMDefaultBaseIterators
049 implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler,
050 DeclHandler, LexicalHandler
051 {
052 /** Set true to monitor SAX events and similar diagnostic info. */
053 private static final boolean DEBUG = false;
054
055 /**
056 * If we're building the model incrementally on demand, we need to
057 * be able to tell the source when to send us more data.
058 *
059 * Note that if this has not been set, and you attempt to read ahead
060 * of the current build point, we'll probably throw a null-pointer
061 * exception. We could try to wait-and-retry instead, as a very poor
062 * fallback, but that has all the known problems with multithreading
063 * on multiprocessors and we Don't Want to Go There.
064 *
065 * @see setIncrementalSAXSource
066 */
067 private IncrementalSAXSource m_incrementalSAXSource = null;
068
069 /**
070 * All the character content, including attribute values, are stored in
071 * this buffer.
072 *
073 * %REVIEW% Should this have an option of being shared across DTMs?
074 * Sequentially only; not threadsafe... Currently, I think not.
075 *
076 * %REVIEW% Initial size was pushed way down to reduce weight of RTFs.
077 * pending reduction in number of RTF DTMs. Now that we're sharing a DTM
078 * between RTFs, and tail-pruning... consider going back to the larger/faster.
079 *
080 * Made protected rather than private so SAX2RTFDTM can access it.
081 */
082 //private FastStringBuffer m_chars = new FastStringBuffer(13, 13);
083 protected FastStringBuffer m_chars;
084
085 /** This vector holds offset and length data.
086 */
087 protected SuballocatedIntVector m_data;
088
089 /** The parent stack, needed only for construction.
090 * Made protected rather than private so SAX2RTFDTM can access it.
091 */
092 transient protected IntStack m_parents;
093
094 /** The current previous node, needed only for construction time.
095 * Made protected rather than private so SAX2RTFDTM can access it.
096 */
097 transient protected int m_previous = 0;
098
099 /** Namespace support, only relevent at construction time.
100 * Made protected rather than private so SAX2RTFDTM can access it.
101 */
102 transient protected java.util.Vector m_prefixMappings =
103 new java.util.Vector();
104
105 /** Namespace support, only relevent at construction time.
106 * Made protected rather than private so SAX2RTFDTM can access it.
107 */
108 transient protected IntStack m_contextIndexes;
109
110 /** Type of next characters() event within text block in prgress. */
111 transient protected int m_textType = DTM.TEXT_NODE;
112
113 /**
114 * Type of coalesced text block. See logic in the characters()
115 * method.
116 */
117 transient protected int m_coalescedTextType = DTM.TEXT_NODE;
118
119 /** The SAX Document locator */
120 transient protected Locator m_locator = null;
121
122 /** The SAX Document system-id */
123 transient private String m_systemId = null;
124
125 /** We are inside the DTD. This is used for ignoring comments. */
126 transient protected boolean m_insideDTD = false;
127
128 /** Tree Walker for dispatchToEvents. */
129 protected DTMTreeWalker m_walker = new DTMTreeWalker();
130
131 /** pool of string values that come as strings. */
132 protected DTMStringPool m_valuesOrPrefixes;
133
134 /** End document has been reached.
135 * Made protected rather than private so SAX2RTFDTM can access it.
136 */
137 protected boolean m_endDocumentOccured = false;
138
139 /** Data or qualified name values, one array element for each node. */
140 protected SuballocatedIntVector m_dataOrQName;
141
142 /**
143 * This table holds the ID string to node associations, for
144 * XML IDs.
145 */
146 protected Hashtable m_idAttributes = new Hashtable();
147
148 /**
149 * fixed dom-style names.
150 */
151 private static final String[] m_fixednames = { null,
152 null, // nothing, Element
153 null, "#text", // Attr, Text
154 "#cdata_section", null, // CDATA, EntityReference
155 null, null, // Entity, PI
156 "#comment", "#document", // Comment, Document
157 null, "#document-fragment", // Doctype, DocumentFragment
158 null }; // Notation
159
160 /**
161 * Vector of entities. Each record is composed of four Strings:
162 * publicId, systemID, notationName, and name.
163 */
164 private Vector m_entities = null;
165
166 /** m_entities public ID offset. */
167 private static final int ENTITY_FIELD_PUBLICID = 0;
168
169 /** m_entities system ID offset. */
170 private static final int ENTITY_FIELD_SYSTEMID = 1;
171
172 /** m_entities notation name offset. */
173 private static final int ENTITY_FIELD_NOTATIONNAME = 2;
174
175 /** m_entities name offset. */
176 private static final int ENTITY_FIELD_NAME = 3;
177
178 /** Number of entries per record for m_entities. */
179 private static final int ENTITY_FIELDS_PER = 4;
180
181 /**
182 * The starting offset within m_chars for the text or
183 * CDATA_SECTION node currently being acumulated,
184 * or -1 if there is no text node in progress
185 */
186 protected int m_textPendingStart = -1;
187
188 /**
189 * Describes whether information about document source location
190 * should be maintained or not.
191 *
192 * Made protected for access by SAX2RTFDTM.
193 */
194 protected boolean m_useSourceLocationProperty = false;
195
196 /** Made protected for access by SAX2RTFDTM.
197 */
198 protected StringVector m_sourceSystemId;
199 /** Made protected for access by SAX2RTFDTM.
200 */
201 protected IntVector m_sourceLine;
202 /** Made protected for access by SAX2RTFDTM.
203 */
204 protected IntVector m_sourceColumn;
205
206 /**
207 * Construct a SAX2DTM object using the default block size.
208 *
209 * @param mgr The DTMManager who owns this DTM.
210 * @param source the JAXP 1.1 Source object for this DTM.
211 * @param dtmIdentity The DTM identity ID for this DTM.
212 * @param whiteSpaceFilter The white space filter for this DTM, which may
213 * be null.
214 * @param xstringfactory XMLString factory for creating character content.
215 * @param doIndexing true if the caller considers it worth it to use
216 * indexing schemes.
217 */
218 public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity,
219 DTMWSFilter whiteSpaceFilter,
220 XMLStringFactory xstringfactory,
221 boolean doIndexing)
222 {
223
224 this(mgr, source, dtmIdentity, whiteSpaceFilter,
225 xstringfactory, doIndexing, DEFAULT_BLOCKSIZE, true, false);
226 }
227
228 /**
229 * Construct a SAX2DTM object ready to be constructed from SAX2
230 * ContentHandler events.
231 *
232 * @param mgr The DTMManager who owns this DTM.
233 * @param source the JAXP 1.1 Source object for this DTM.
234 * @param dtmIdentity The DTM identity ID for this DTM.
235 * @param whiteSpaceFilter The white space filter for this DTM, which may
236 * be null.
237 * @param xstringfactory XMLString factory for creating character content.
238 * @param doIndexing true if the caller considers it worth it to use
239 * indexing schemes.
240 * @param blocksize The block size of the DTM.
241 * @param usePrevsib true if we want to build the previous sibling node array.
242 * @param newNameTable true if we want to use a new ExpandedNameTable for this DTM.
243 */
244 public SAX2DTM(DTMManager mgr, Source source, int dtmIdentity,
245 DTMWSFilter whiteSpaceFilter,
246 XMLStringFactory xstringfactory,
247 boolean doIndexing,
248 int blocksize,
249 boolean usePrevsib,
250 boolean newNameTable)
251 {
252
253 super(mgr, source, dtmIdentity, whiteSpaceFilter,
254 xstringfactory, doIndexing, blocksize, usePrevsib, newNameTable);
255
256 // %OPT% Use smaller sizes for all internal storage units when
257 // the blocksize is small. This reduces the cost of creating an RTF.
258 if (blocksize <= 64)
259 {
260 m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL);
261 m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS_SMALL);
262 m_valuesOrPrefixes = new DTMStringPool(16);
263 m_chars = new FastStringBuffer(7, 10);
264 m_contextIndexes = new IntStack(4);
265 m_parents = new IntStack(4);
266 }
267 else
268 {
269 m_data = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS);
270 m_dataOrQName = new SuballocatedIntVector(blocksize, DEFAULT_NUMBLOCKS);
271 m_valuesOrPrefixes = new DTMStringPool();
272 m_chars = new FastStringBuffer(10, 13);
273 m_contextIndexes = new IntStack();
274 m_parents = new IntStack();
275 }
276
277 // %REVIEW% Initial size pushed way down to reduce weight of RTFs
278 // (I'm not entirely sure 0 would work, so I'm playing it safe for now.)
279 //m_data = new SuballocatedIntVector(doIndexing ? (1024*2) : 512, 1024);
280 //m_data = new SuballocatedIntVector(blocksize);
281
282 m_data.addElement(0); // Need placeholder in case index into here must be <0.
283
284 //m_dataOrQName = new SuballocatedIntVector(blocksize);
285
286 // m_useSourceLocationProperty=org.apache.xalan.processor.TransformerFactoryImpl.m_source_location;
287 m_useSourceLocationProperty = mgr.getSource_location();
288 m_sourceSystemId = (m_useSourceLocationProperty) ? new StringVector() : null;
289 m_sourceLine = (m_useSourceLocationProperty) ? new IntVector() : null;
290 m_sourceColumn = (m_useSourceLocationProperty) ? new IntVector() : null;
291 }
292
293 /**
294 * Set whether information about document source location
295 * should be maintained or not.
296 */
297 public void setUseSourceLocation(boolean useSourceLocation)
298 {
299 m_useSourceLocationProperty = useSourceLocation;
300 }
301
302 /**
303 * Get the data or qualified name for the given node identity.
304 *
305 * @param identity The node identity.
306 *
307 * @return The data or qualified name, or DTM.NULL.
308 */
309 protected int _dataOrQName(int identity)
310 {
311
312 if (identity < m_size)
313 return m_dataOrQName.elementAt(identity);
314
315 // Check to see if the information requested has been processed, and,
316 // if not, advance the iterator until we the information has been
317 // processed.
318 while (true)
319 {
320 boolean isMore = nextNode();
321
322 if (!isMore)
323 return NULL;
324 else if (identity < m_size)
325 return m_dataOrQName.elementAt(identity);
326 }
327 }
328
329 /**
330 * Ask the CoRoutine parser to doTerminate and clear the reference.
331 */
332 public void clearCoRoutine()
333 {
334 clearCoRoutine(true);
335 }
336
337 /**
338 * Ask the CoRoutine parser to doTerminate and clear the reference. If
339 * the CoRoutine parser has already been cleared, this will have no effect.
340 *
341 * @param callDoTerminate true of doTerminate should be called on the
342 * coRoutine parser.
343 */
344 public void clearCoRoutine(boolean callDoTerminate)
345 {
346
347 if (null != m_incrementalSAXSource)
348 {
349 if (callDoTerminate)
350 m_incrementalSAXSource.deliverMoreNodes(false);
351
352 m_incrementalSAXSource = null;
353 }
354 }
355
356 /**
357 * Bind a IncrementalSAXSource to this DTM. If we discover we need nodes
358 * that have not yet been built, we will ask this object to send us more
359 * events, and it will manage interactions with its data sources.
360 *
361 * Note that we do not actually build the IncrementalSAXSource, since we don't
362 * know what source it's reading from, what thread that source will run in,
363 * or when it will run.
364 *
365 * @param incrementalSAXSource The parser that we want to recieve events from
366 * on demand.
367 */
368 public void setIncrementalSAXSource(IncrementalSAXSource incrementalSAXSource)
369 {
370
371 // Establish coroutine link so we can request more data
372 //
373 // Note: It's possible that some versions of IncrementalSAXSource may
374 // not actually use a CoroutineManager, and hence may not require
375 // that we obtain an Application Coroutine ID. (This relies on the
376 // coroutine transaction details having been encapsulated in the
377 // IncrementalSAXSource.do...() methods.)
378 m_incrementalSAXSource = incrementalSAXSource;
379
380 // Establish SAX-stream link so we can receive the requested data
381 incrementalSAXSource.setContentHandler(this);
382 incrementalSAXSource.setLexicalHandler(this);
383 incrementalSAXSource.setDTDHandler(this);
384
385 // Are the following really needed? incrementalSAXSource doesn't yet
386 // support them, and they're mostly no-ops here...
387 //incrementalSAXSource.setErrorHandler(this);
388 //incrementalSAXSource.setDeclHandler(this);
389 }
390
391 /**
392 * getContentHandler returns "our SAX builder" -- the thing that
393 * someone else should send SAX events to in order to extend this
394 * DTM model.
395 *
396 * %REVIEW% Should this return null if constrution already done/begun?
397 *
398 * @return null if this model doesn't respond to SAX events,
399 * "this" if the DTM object has a built-in SAX ContentHandler,
400 * the IncrementalSAXSource if we're bound to one and should receive
401 * the SAX stream via it for incremental build purposes...
402 */
403 public ContentHandler getContentHandler()
404 {
405
406 if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter)
407 return (ContentHandler) m_incrementalSAXSource;
408 else
409 return this;
410 }
411
412 /**
413 * Return this DTM's lexical handler.
414 *
415 * %REVIEW% Should this return null if constrution already done/begun?
416 *
417 * @return null if this model doesn't respond to lexical SAX events,
418 * "this" if the DTM object has a built-in SAX ContentHandler,
419 * the IncrementalSAXSource if we're bound to one and should receive
420 * the SAX stream via it for incremental build purposes...
421 */
422 public LexicalHandler getLexicalHandler()
423 {
424
425 if (m_incrementalSAXSource instanceof IncrementalSAXSource_Filter)
426 return (LexicalHandler) m_incrementalSAXSource;
427 else
428 return this;
429 }
430
431 /**
432 * Return this DTM's EntityResolver.
433 *
434 * @return null if this model doesn't respond to SAX entity ref events.
435 */
436 public EntityResolver getEntityResolver()
437 {
438 return this;
439 }
440
441 /**
442 * Return this DTM's DTDHandler.
443 *
444 * @return null if this model doesn't respond to SAX dtd events.
445 */
446 public DTDHandler getDTDHandler()
447 {
448 return this;
449 }
450
451 /**
452 * Return this DTM's ErrorHandler.
453 *
454 * @return null if this model doesn't respond to SAX error events.
455 */
456 public ErrorHandler getErrorHandler()
457 {
458 return this;
459 }
460
461 /**
462 * Return this DTM's DeclHandler.
463 *
464 * @return null if this model doesn't respond to SAX Decl events.
465 */
466 public DeclHandler getDeclHandler()
467 {
468 return this;
469 }
470
471 /**
472 * @return true iff we're building this model incrementally (eg
473 * we're partnered with a IncrementalSAXSource) and thus require that the
474 * transformation and the parse run simultaneously. Guidance to the
475 * DTMManager.
476 */
477 public boolean needsTwoThreads()
478 {
479 return null != m_incrementalSAXSource;
480 }
481
482 /**
483 * Directly call the
484 * characters method on the passed ContentHandler for the
485 * string-value of the given node (see http://www.w3.org/TR/xpath#data-model
486 * for the definition of a node's string-value). Multiple calls to the
487 * ContentHandler's characters methods may well occur for a single call to
488 * this method.
489 *
490 * @param nodeHandle The node ID.
491 * @param ch A non-null reference to a ContentHandler.
492 * @param normalize true if the content should be normalized according to
493 * the rules for the XPath
494 * <a href="http://www.w3.org/TR/xpath#function-normalize-space">normalize-space</a>
495 * function.
496 *
497 * @throws SAXException
498 */
499 public void dispatchCharactersEvents(int nodeHandle, ContentHandler ch,
500 boolean normalize)
501 throws SAXException
502 {
503
504 int identity = makeNodeIdentity(nodeHandle);
505
506 if (identity == DTM.NULL)
507 return;
508
509 int type = _type(identity);
510
511 if (isTextType(type))
512 {
513 int dataIndex = m_dataOrQName.elementAt(identity);
514 int offset = m_data.elementAt(dataIndex);
515 int length = m_data.elementAt(dataIndex + 1);
516
517 if(normalize)
518 m_chars.sendNormalizedSAXcharacters(ch, offset, length);
519 else
520 m_chars.sendSAXcharacters(ch, offset, length);
521 }
522 else
523 {
524 int firstChild = _firstch(identity);
525
526 if (DTM.NULL != firstChild)
527 {
528 int offset = -1;
529 int length = 0;
530 int startNode = identity;
531
532 identity = firstChild;
533
534 do {
535 type = _type(identity);
536
537 if (isTextType(type))
538 {
539 int dataIndex = _dataOrQName(identity);
540
541 if (-1 == offset)
542 {
543 offset = m_data.elementAt(dataIndex);
544 }
545
546 length += m_data.elementAt(dataIndex + 1);
547 }
548
549 identity = getNextNodeIdentity(identity);
550 } while (DTM.NULL != identity && (_parent(identity) >= startNode));
551
552 if (length > 0)
553 {
554 if(normalize)
555 m_chars.sendNormalizedSAXcharacters(ch, offset, length);
556 else
557 m_chars.sendSAXcharacters(ch, offset, length);
558 }
559 }
560 else if(type != DTM.ELEMENT_NODE)
561 {
562 int dataIndex = _dataOrQName(identity);
563
564 if (dataIndex < 0)
565 {
566 dataIndex = -dataIndex;
567 dataIndex = m_data.elementAt(dataIndex + 1);
568 }
569
570 String str = m_valuesOrPrefixes.indexToString(dataIndex);
571
572 if(normalize)
573 FastStringBuffer.sendNormalizedSAXcharacters(str.toCharArray(),
574 0, str.length(), ch);
575 else
576 ch.characters(str.toCharArray(), 0, str.length());
577 }
578 }
579 }
580
581
582 /**
583 * Given a node handle, return its DOM-style node name. This will
584 * include names such as #text or #document.
585 *
586 * @param nodeHandle the id of the node.
587 * @return String Name of this node, which may be an empty string.
588 * %REVIEW% Document when empty string is possible...
589 * %REVIEW-COMMENT% It should never be empty, should it?
590 */
591 public String getNodeName(int nodeHandle)
592 {
593
594 int expandedTypeID = getExpandedTypeID(nodeHandle);
595 // If just testing nonzero, no need to shift...
596 int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID);
597
598 if (0 == namespaceID)
599 {
600 // Don't retrieve name until/unless needed
601 // String name = m_expandedNameTable.getLocalName(expandedTypeID);
602 int type = getNodeType(nodeHandle);
603
604 if (type == DTM.NAMESPACE_NODE)
605 {
606 if (null == m_expandedNameTable.getLocalName(expandedTypeID))
607 return "xmlns";
608 else
609 return "xmlns:" + m_expandedNameTable.getLocalName(expandedTypeID);
610 }
611 else if (0 == m_expandedNameTable.getLocalNameID(expandedTypeID))
612 {
613 return m_fixednames[type];
614 }
615 else
616 return m_expandedNameTable.getLocalName(expandedTypeID);
617 }
618 else
619 {
620 int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle));
621
622 if (qnameIndex < 0)
623 {
624 qnameIndex = -qnameIndex;
625 qnameIndex = m_data.elementAt(qnameIndex);
626 }
627
628 return m_valuesOrPrefixes.indexToString(qnameIndex);
629 }
630 }
631
632 /**
633 * Given a node handle, return the XPath node name. This should be
634 * the name as described by the XPath data model, NOT the DOM-style
635 * name.
636 *
637 * @param nodeHandle the id of the node.
638 * @return String Name of this node, which may be an empty string.
639 */
640 public String getNodeNameX(int nodeHandle)
641 {
642
643 int expandedTypeID = getExpandedTypeID(nodeHandle);
644 int namespaceID = m_expandedNameTable.getNamespaceID(expandedTypeID);
645
646 if (0 == namespaceID)
647 {
648 String name = m_expandedNameTable.getLocalName(expandedTypeID);
649
650 if (name == null)
651 return "";
652 else
653 return name;
654 }
655 else
656 {
657 int qnameIndex = m_dataOrQName.elementAt(makeNodeIdentity(nodeHandle));
658
659 if (qnameIndex < 0)
660 {
661 qnameIndex = -qnameIndex;
662 qnameIndex = m_data.elementAt(qnameIndex);
663 }
664
665 return m_valuesOrPrefixes.indexToString(qnameIndex);
666 }
667 }
668
669 /**
670 * 5. [specified] A flag indicating whether this attribute was actually
671 * specified in the start-tag of its element, or was defaulted from the
672 * DTD.
673 *
674 * @param attributeHandle Must be a valid handle to an attribute node.
675 * @return <code>true</code> if the attribute was specified;
676 * <code>false</code> if it was defaulted.
677 */
678 public boolean isAttributeSpecified(int attributeHandle)
679 {
680
681 // I'm not sure if I want to do anything with this...
682 return true; // ??
683 }
684
685 /**
686 * A document type declaration information item has the following properties:
687 *
688 * 1. [system identifier] The system identifier of the external subset, if
689 * it exists. Otherwise this property has no value.
690 *
691 * @return the system identifier String object, or null if there is none.
692 */
693 public String getDocumentTypeDeclarationSystemIdentifier()
694 {
695
696 /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
697 error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
698
699 return null;
700 }
701
702 /**
703 * Get the next node identity value in the list, and call the iterator
704 * if it hasn't been added yet.
705 *
706 * @param identity The node identity (index).
707 * @return identity+1, or DTM.NULL.
708 */
709 protected int getNextNodeIdentity(int identity)
710 {
711
712 identity += 1;
713
714 while (identity >= m_size)
715 {
716 if (null == m_incrementalSAXSource)
717 return DTM.NULL;
718
719 nextNode();
720 }
721
722 return identity;
723 }
724
725 /**
726 * Directly create SAX parser events from a subtree.
727 *
728 * @param nodeHandle The node ID.
729 * @param ch A non-null reference to a ContentHandler.
730 *
731 * @throws org.xml.sax.SAXException
732 */
733 public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch)
734 throws org.xml.sax.SAXException
735 {
736
737 DTMTreeWalker treeWalker = m_walker;
738 ContentHandler prevCH = treeWalker.getcontentHandler();
739
740 if (null != prevCH)
741 {
742 treeWalker = new DTMTreeWalker();
743 }
744
745 treeWalker.setcontentHandler(ch);
746 treeWalker.setDTM(this);
747
748 try
749 {
750 treeWalker.traverse(nodeHandle);
751 }
752 finally
753 {
754 treeWalker.setcontentHandler(null);
755 }
756 }
757
758 /**
759 * Get the number of nodes that have been added.
760 *
761 * @return The number of that are currently in the tree.
762 */
763 public int getNumberOfNodes()
764 {
765 return m_size;
766 }
767
768 /**
769 * This method should try and build one or more nodes in the table.
770 *
771 * @return The true if a next node is found or false if
772 * there are no more nodes.
773 */
774 protected boolean nextNode()
775 {
776
777 if (null == m_incrementalSAXSource)
778 return false;
779
780 if (m_endDocumentOccured)
781 {
782 clearCoRoutine();
783
784 return false;
785 }
786
787 Object gotMore = m_incrementalSAXSource.deliverMoreNodes(true);
788
789 // gotMore may be a Boolean (TRUE if still parsing, FALSE if
790 // EOF) or an exception if IncrementalSAXSource malfunctioned
791 // (code error rather than user error).
792 //
793 // %REVIEW% Currently the ErrorHandlers sketched herein are
794 // no-ops, so I'm going to initially leave this also as a
795 // no-op.
796 if (!(gotMore instanceof Boolean))
797 {
798 if(gotMore instanceof RuntimeException)
799 {
800 throw (RuntimeException)gotMore;
801 }
802 else if(gotMore instanceof Exception)
803 {
804 throw new WrappedRuntimeException((Exception)gotMore);
805 }
806 // for now...
807 clearCoRoutine();
808
809 return false;
810
811 // %TBD%
812 }
813
814 if (gotMore != Boolean.TRUE)
815 {
816
817 // EOF reached without satisfying the request
818 clearCoRoutine(); // Drop connection, stop trying
819
820 // %TBD% deregister as its listener?
821 }
822
823 return true;
824 }
825
826 /**
827 * Bottleneck determination of text type.
828 *
829 * @param type oneof DTM.XXX_NODE.
830 *
831 * @return true if this is a text or cdata section.
832 */
833 private final boolean isTextType(int type)
834 {
835 return (DTM.TEXT_NODE == type || DTM.CDATA_SECTION_NODE == type);
836 }
837
838 // /**
839 // * Ensure that the size of the information arrays can hold another entry
840 // * at the given index.
841 // *
842 // * @param on exit from this function, the information arrays sizes must be
843 // * at least index+1.
844 // *
845 // * NEEDSDOC @param index
846 // */
847 // protected void ensureSize(int index)
848 // {
849 // // dataOrQName is an SuballocatedIntVector and hence self-sizing.
850 // // But DTMDefaultBase may need fixup.
851 // super.ensureSize(index);
852 // }
853
854 /**
855 * Construct the node map from the node.
856 *
857 * @param type raw type ID, one of DTM.XXX_NODE.
858 * @param expandedTypeID The expended type ID.
859 * @param parentIndex The current parent index.
860 * @param previousSibling The previous sibling index.
861 * @param dataOrPrefix index into m_data table, or string handle.
862 * @param canHaveFirstChild true if the node can have a first child, false
863 * if it is atomic.
864 *
865 * @return The index identity of the node that was added.
866 */
867 protected int addNode(int type, int expandedTypeID,
868 int parentIndex, int previousSibling,
869 int dataOrPrefix, boolean canHaveFirstChild)
870 {
871 // Common to all nodes:
872 int nodeIndex = m_size++;
873
874 // Have we overflowed a DTM Identity's addressing range?
875 if(m_dtmIdent.size() == (nodeIndex>>>DTMManager.IDENT_DTM_NODE_BITS))
876 {
877 addNewDTMID(nodeIndex);
878 }
879
880 m_firstch.addElement(canHaveFirstChild ? NOTPROCESSED : DTM.NULL);
881 m_nextsib.addElement(NOTPROCESSED);
882 m_parent.addElement(parentIndex);
883 m_exptype.addElement(expandedTypeID);
884 m_dataOrQName.addElement(dataOrPrefix);
885
886 if (m_prevsib != null) {
887 m_prevsib.addElement(previousSibling);
888 }
889
890 if (DTM.NULL != previousSibling) {
891 m_nextsib.setElementAt(nodeIndex,previousSibling);
892 }
893
894 if (m_locator != null && m_useSourceLocationProperty) {
895 setSourceLocation();
896 }
897
898 // Note that nextSibling is not processed until charactersFlush()
899 // is called, to handle successive characters() events.
900
901 // Special handling by type: Declare namespaces, attach first child
902 switch(type)
903 {
904 case DTM.NAMESPACE_NODE:
905 declareNamespaceInContext(parentIndex,nodeIndex);
906 break;
907 case DTM.ATTRIBUTE_NODE:
908 break;
909 default:
910 if (DTM.NULL == previousSibling && DTM.NULL != parentIndex) {
911 m_firstch.setElementAt(nodeIndex,parentIndex);
912 }
913 break;
914 }
915
916 return nodeIndex;
917 }
918
919 /**
920 * Get a new DTM ID beginning at the specified node index.
921 * @param nodeIndex The node identity at which the new DTM ID will begin
922 * addressing.
923 */
924 protected void addNewDTMID(int nodeIndex) {
925 try
926 {
927 if(m_mgr==null)
928 throw new ClassCastException();
929
930 // Handle as Extended Addressing
931 DTMManagerDefault mgrD=(DTMManagerDefault)m_mgr;
932 int id=mgrD.getFirstFreeDTMID();
933 mgrD.addDTM(this,id,nodeIndex);
934 m_dtmIdent.addElement(id<<DTMManager.IDENT_DTM_NODE_BITS);
935 }
936 catch(ClassCastException e)
937 {
938 // %REVIEW% Wrong error message, but I've been told we're trying
939 // not to add messages right not for I18N reasons.
940 // %REVIEW% Should this be a Fatal Error?
941 error(XMLMessages.createXMLMessage(XMLErrorResources.ER_NO_DTMIDS_AVAIL, null));//"No more DTM IDs are available";
942 }
943 }
944
945 /**
946 * Migrate a DTM built with an old DTMManager to a new DTMManager.
947 * After the migration, the new DTMManager will treat the DTM as
948 * one that is built by itself.
949 * This is used to support DTM sharing between multiple transformations.
950 * @param manager the DTMManager
951 */
952 public void migrateTo(DTMManager manager) {
953 super.migrateTo(manager);
954
955 // We have to reset the information in m_dtmIdent and
956 // register the DTM with the new manager.
957 int numDTMs = m_dtmIdent.size();
958 int dtmId = m_mgrDefault.getFirstFreeDTMID();
959 int nodeIndex = 0;
960 for (int i = 0; i < numDTMs; i++)
961 {
962 m_dtmIdent.setElementAt(dtmId << DTMManager.IDENT_DTM_NODE_BITS, i);
963 m_mgrDefault.addDTM(this, dtmId, nodeIndex);
964 dtmId++;
965 nodeIndex += (1 << DTMManager.IDENT_DTM_NODE_BITS);
966 }
967 }
968
969 /**
970 * Store the source location of the current node. This method must be called
971 * as every node is added to the DTM or for no node.
972 */
973 protected void setSourceLocation() {
974 m_sourceSystemId.addElement(m_locator.getSystemId());
975 m_sourceLine.addElement(m_locator.getLineNumber());
976 m_sourceColumn.addElement(m_locator.getColumnNumber());
977
978 //%REVIEW% %BUG% Prevent this from arising in the first place
979 // by not allowing the enabling conditions to change after we start
980 // building the document.
981 if (m_sourceSystemId.size() != m_size) {
982 String msg = "CODING ERROR in Source Location: " + m_size + " != "
983 + m_sourceSystemId.size();
984 System.err.println(msg);
985 throw new RuntimeException(msg);
986 }
987 }
988
989 /**
990 * Given a node handle, return its node value. This is mostly
991 * as defined by the DOM, but may ignore some conveniences.
992 * <p>
993 *
994 * @param nodeHandle The node id.
995 * @return String Value of this node, or null if not
996 * meaningful for this node type.
997 */
998 public String getNodeValue(int nodeHandle)
999 {
1000
1001 int identity = makeNodeIdentity(nodeHandle);
1002 int type = _type(identity);
1003
1004 if (isTextType(type))
1005 {
1006 int dataIndex = _dataOrQName(identity);
1007 int offset = m_data.elementAt(dataIndex);
1008 int length = m_data.elementAt(dataIndex + 1);
1009
1010 // %OPT% We should cache this, I guess.
1011 return m_chars.getString(offset, length);
1012 }
1013 else if (DTM.ELEMENT_NODE == type || DTM.DOCUMENT_FRAGMENT_NODE == type
1014 || DTM.DOCUMENT_NODE == type)
1015 {
1016 return null;
1017 }
1018 else
1019 {
1020 int dataIndex = _dataOrQName(identity);
1021
1022 if (dataIndex < 0)
1023 {
1024 dataIndex = -dataIndex;
1025 dataIndex = m_data.elementAt(dataIndex + 1);
1026 }
1027
1028 return m_valuesOrPrefixes.indexToString(dataIndex);
1029 }
1030 }
1031
1032 /**
1033 * Given a node handle, return its XPath-style localname.
1034 * (As defined in Namespaces, this is the portion of the name after any
1035 * colon character).
1036 *
1037 * @param nodeHandle the id of the node.
1038 * @return String Local name of this node.
1039 */
1040 public String getLocalName(int nodeHandle)
1041 {
1042 return m_expandedNameTable.getLocalName(_exptype(makeNodeIdentity(nodeHandle)));
1043 }
1044
1045 /**
1046 * The getUnparsedEntityURI function returns the URI of the unparsed
1047 * entity with the specified name in the same document as the context
1048 * node (see [3.3 Unparsed Entities]). It returns the empty string if
1049 * there is no such entity.
1050 * <p>
1051 * XML processors may choose to use the System Identifier (if one
1052 * is provided) to resolve the entity, rather than the URI in the
1053 * Public Identifier. The details are dependent on the processor, and
1054 * we would have to support some form of plug-in resolver to handle
1055 * this properly. Currently, we simply return the System Identifier if
1056 * present, and hope that it a usable URI or that our caller can
1057 * map it to one.
1058 * TODO: Resolve Public Identifiers... or consider changing function name.
1059 * <p>
1060 * If we find a relative URI
1061 * reference, XML expects it to be resolved in terms of the base URI
1062 * of the document. The DOM doesn't do that for us, and it isn't
1063 * entirely clear whether that should be done here; currently that's
1064 * pushed up to a higher level of our application. (Note that DOM Level
1065 * 1 didn't store the document's base URI.)
1066 * TODO: Consider resolving Relative URIs.
1067 * <p>
1068 * (The DOM's statement that "An XML processor may choose to
1069 * completely expand entities before the structure model is passed
1070 * to the DOM" refers only to parsed entities, not unparsed, and hence
1071 * doesn't affect this function.)
1072 *
1073 * @param name A string containing the Entity Name of the unparsed
1074 * entity.
1075 *
1076 * @return String containing the URI of the Unparsed Entity, or an
1077 * empty string if no such entity exists.
1078 */
1079 public String getUnparsedEntityURI(String name)
1080 {
1081
1082 String url = "";
1083
1084 if (null == m_entities)
1085 return url;
1086
1087 int n = m_entities.size();
1088
1089 for (int i = 0; i < n; i += ENTITY_FIELDS_PER)
1090 {
1091 String ename = (String) m_entities.elementAt(i + ENTITY_FIELD_NAME);
1092
1093 if (null != ename && ename.equals(name))
1094 {
1095 String nname = (String) m_entities.elementAt(i
1096 + ENTITY_FIELD_NOTATIONNAME);
1097
1098 if (null != nname)
1099 {
1100
1101 // The draft says: "The XSLT processor may use the public
1102 // identifier to generate a URI for the entity instead of the URI
1103 // specified in the system identifier. If the XSLT processor does
1104 // not use the public identifier to generate the URI, it must use
1105 // the system identifier; if the system identifier is a relative
1106 // URI, it must be resolved into an absolute URI using the URI of
1107 // the resource containing the entity declaration as the base
1108 // URI [RFC2396]."
1109 // So I'm falling a bit short here.
1110 url = (String) m_entities.elementAt(i + ENTITY_FIELD_SYSTEMID);
1111
1112 if (null == url)
1113 {
1114 url = (String) m_entities.elementAt(i + ENTITY_FIELD_PUBLICID);
1115 }
1116 }
1117
1118 break;
1119 }
1120 }
1121
1122 return url;
1123 }
1124
1125 /**
1126 * Given a namespace handle, return the prefix that the namespace decl is
1127 * mapping.
1128 * Given a node handle, return the prefix used to map to the namespace.
1129 *
1130 * <p> %REVIEW% Are you sure you want "" for no prefix? </p>
1131 * <p> %REVIEW-COMMENT% I think so... not totally sure. -sb </p>
1132 *
1133 * @param nodeHandle the id of the node.
1134 * @return String prefix of this node's name, or "" if no explicit
1135 * namespace prefix was given.
1136 */
1137 public String getPrefix(int nodeHandle)
1138 {
1139
1140 int identity = makeNodeIdentity(nodeHandle);
1141 int type = _type(identity);
1142
1143 if (DTM.ELEMENT_NODE == type)
1144 {
1145 int prefixIndex = _dataOrQName(identity);
1146
1147 if (0 == prefixIndex)
1148 return "";
1149 else
1150 {
1151 String qname = m_valuesOrPrefixes.indexToString(prefixIndex);
1152
1153 return getPrefix(qname, null);
1154 }
1155 }
1156 else if (DTM.ATTRIBUTE_NODE == type)
1157 {
1158 int prefixIndex = _dataOrQName(identity);
1159
1160 if (prefixIndex < 0)
1161 {
1162 prefixIndex = m_data.elementAt(-prefixIndex);
1163
1164 String qname = m_valuesOrPrefixes.indexToString(prefixIndex);
1165
1166 return getPrefix(qname, null);
1167 }
1168 }
1169
1170 return "";
1171 }
1172
1173 /**
1174 * Retrieves an attribute node by by qualified name and namespace URI.
1175 *
1176 * @param nodeHandle int Handle of the node upon which to look up this attribute..
1177 * @param namespaceURI The namespace URI of the attribute to
1178 * retrieve, or null.
1179 * @param name The local name of the attribute to
1180 * retrieve.
1181 * @return The attribute node handle with the specified name (
1182 * <code>nodeName</code>) or <code>DTM.NULL</code> if there is no such
1183 * attribute.
1184 */
1185 public int getAttributeNode(int nodeHandle, String namespaceURI,
1186 String name)
1187 {
1188
1189 for (int attrH = getFirstAttribute(nodeHandle); DTM.NULL != attrH;
1190 attrH = getNextAttribute(attrH))
1191 {
1192 String attrNS = getNamespaceURI(attrH);
1193 String attrName = getLocalName(attrH);
1194 boolean nsMatch = namespaceURI == attrNS
1195 || (namespaceURI != null
1196 && namespaceURI.equals(attrNS));
1197
1198 if (nsMatch && name.equals(attrName))
1199 return attrH;
1200 }
1201
1202 return DTM.NULL;
1203 }
1204
1205 /**
1206 * Return the public identifier of the external subset,
1207 * normalized as described in 4.2.2 External Entities [XML]. If there is
1208 * no external subset or if it has no public identifier, this property
1209 * has no value.
1210 *
1211 * @return the public identifier String object, or null if there is none.
1212 */
1213 public String getDocumentTypeDeclarationPublicIdentifier()
1214 {
1215
1216 /** @todo: implement this org.apache.xml.dtm.DTMDefaultBase abstract method */
1217 error(XMLMessages.createXMLMessage(XMLErrorResources.ER_METHOD_NOT_SUPPORTED, null));//"Not yet supported!");
1218
1219 return null;
1220 }
1221
1222 /**
1223 * Given a node handle, return its DOM-style namespace URI
1224 * (As defined in Namespaces, this is the declared URI which this node's
1225 * prefix -- or default in lieu thereof -- was mapped to.)
1226 *
1227 * <p>%REVIEW% Null or ""? -sb</p>
1228 *
1229 * @param nodeHandle the id of the node.
1230 * @return String URI value of this node's namespace, or null if no
1231 * namespace was resolved.
1232 */
1233 public String getNamespaceURI(int nodeHandle)
1234 {
1235
1236 return m_expandedNameTable.getNamespace(_exptype(makeNodeIdentity(nodeHandle)));
1237 }
1238
1239 /**
1240 * Get the string-value of a node as a String object
1241 * (see http://www.w3.org/TR/xpath#data-model
1242 * for the definition of a node's string-value).
1243 *
1244 * @param nodeHandle The node ID.
1245 *
1246 * @return A string object that represents the string-value of the given node.
1247 */
1248 public XMLString getStringValue(int nodeHandle)
1249 {
1250 int identity = makeNodeIdentity(nodeHandle);
1251 int type;
1252 if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it
1253 type = DTM.NULL;
1254 else
1255 type= _type(identity);
1256
1257 if (isTextType(type))
1258 {
1259 int dataIndex = _dataOrQName(identity);
1260 int offset = m_data.elementAt(dataIndex);
1261 int length = m_data.elementAt(dataIndex + 1);
1262
1263 return m_xstrf.newstr(m_chars, offset, length);
1264 }
1265 else
1266 {
1267 int firstChild = _firstch(identity);
1268
1269 if (DTM.NULL != firstChild)
1270 {
1271 int offset = -1;
1272 int length = 0;
1273 int startNode = identity;
1274
1275 identity = firstChild;
1276
1277 do {
1278 type = _type(identity);
1279
1280 if (isTextType(type))
1281 {
1282 int dataIndex = _dataOrQName(identity);
1283
1284 if (-1 == offset)
1285 {
1286 offset = m_data.elementAt(dataIndex);
1287 }
1288
1289 length += m_data.elementAt(dataIndex + 1);
1290 }
1291
1292 identity = getNextNodeIdentity(identity);
1293 } while (DTM.NULL != identity && (_parent(identity) >= startNode));
1294
1295 if (length > 0)
1296 {
1297 return m_xstrf.newstr(m_chars, offset, length);
1298 }
1299 }
1300 else if(type != DTM.ELEMENT_NODE)
1301 {
1302 int dataIndex = _dataOrQName(identity);
1303
1304 if (dataIndex < 0)
1305 {
1306 dataIndex = -dataIndex;
1307 dataIndex = m_data.elementAt(dataIndex + 1);
1308 }
1309 return m_xstrf.newstr(m_valuesOrPrefixes.indexToString(dataIndex));
1310 }
1311 }
1312
1313 return m_xstrf.emptystr();
1314 }
1315
1316 /**
1317 * Determine if the string-value of a node is whitespace
1318 *
1319 * @param nodeHandle The node Handle.
1320 *
1321 * @return Return true if the given node is whitespace.
1322 */
1323 public boolean isWhitespace(int nodeHandle)
1324 {
1325 int identity = makeNodeIdentity(nodeHandle);
1326 int type;
1327 if(identity==DTM.NULL) // Separate lines because I wanted to breakpoint it
1328 type = DTM.NULL;
1329 else
1330 type= _type(identity);
1331
1332 if (isTextType(type))
1333 {
1334 int dataIndex = _dataOrQName(identity);
1335 int offset = m_data.elementAt(dataIndex);
1336 int length = m_data.elementAt(dataIndex + 1);
1337
1338 return m_chars.isWhitespace(offset, length);
1339 }
1340 return false;
1341 }
1342
1343 /**
1344 * Returns the <code>Element</code> whose <code>ID</code> is given by
1345 * <code>elementId</code>. If no such element exists, returns
1346 * <code>DTM.NULL</code>. Behavior is not defined if more than one element
1347 * has this <code>ID</code>. Attributes (including those
1348 * with the name "ID") are not of type ID unless so defined by DTD/Schema
1349 * information available to the DTM implementation.
1350 * Implementations that do not know whether attributes are of type ID or
1351 * not are expected to return <code>DTM.NULL</code>.
1352 *
1353 * <p>%REVIEW% Presumably IDs are still scoped to a single document,
1354 * and this operation searches only within a single document, right?
1355 * Wouldn't want collisions between DTMs in the same process.</p>
1356 *
1357 * @param elementId The unique <code>id</code> value for an element.
1358 * @return The handle of the matching element.
1359 */
1360 public int getElementById(String elementId)
1361 {
1362
1363 Integer intObj;
1364 boolean isMore = true;
1365
1366 do
1367 {
1368 intObj = (Integer) m_idAttributes.get(elementId);
1369
1370 if (null != intObj)
1371 return makeNodeHandle(intObj.intValue());
1372
1373 if (!isMore || m_endDocumentOccured)
1374 break;
1375
1376 isMore = nextNode();
1377 }
1378 while (null == intObj);
1379
1380 return DTM.NULL;
1381 }
1382
1383 /**
1384 * Get a prefix either from the qname or from the uri mapping, or just make
1385 * one up!
1386 *
1387 * @param qname The qualified name, which may be null.
1388 * @param uri The namespace URI, which may be null.
1389 *
1390 * @return The prefix if there is one, or null.
1391 */
1392 public String getPrefix(String qname, String uri)
1393 {
1394
1395 String prefix;
1396 int uriIndex = -1;
1397
1398 if (null != uri && uri.length() > 0)
1399 {
1400
1401 do
1402 {
1403 uriIndex = m_prefixMappings.indexOf(uri, ++uriIndex);
1404 } while ( (uriIndex & 0x01) == 0);
1405
1406 if (uriIndex >= 0)
1407 {
1408 prefix = (String) m_prefixMappings.elementAt(uriIndex - 1);
1409 }
1410 else if (null != qname)
1411 {
1412 int indexOfNSSep = qname.indexOf(':');
1413
1414 if (qname.equals("xmlns"))
1415 prefix = "";
1416 else if (qname.startsWith("xmlns:"))
1417 prefix = qname.substring(indexOfNSSep + 1);
1418 else
1419 prefix = (indexOfNSSep > 0)
1420 ? qname.substring(0, indexOfNSSep) : null;
1421 }
1422 else
1423 {
1424 prefix = null;
1425 }
1426 }
1427 else if (null != qname)
1428 {
1429 int indexOfNSSep = qname.indexOf(':');
1430
1431 if (indexOfNSSep > 0)
1432 {
1433 if (qname.startsWith("xmlns:"))
1434 prefix = qname.substring(indexOfNSSep + 1);
1435 else
1436 prefix = qname.substring(0, indexOfNSSep);
1437 }
1438 else
1439 {
1440 if (qname.equals("xmlns"))
1441 prefix = "";
1442 else
1443 prefix = null;
1444 }
1445 }
1446 else
1447 {
1448 prefix = null;
1449 }
1450
1451 return prefix;
1452 }
1453
1454 /**
1455 * Get a prefix either from the uri mapping, or just make
1456 * one up!
1457 *
1458 * @param uri The namespace URI, which may be null.
1459 *
1460 * @return The prefix if there is one, or null.
1461 */
1462 public int getIdForNamespace(String uri)
1463 {
1464
1465 return m_valuesOrPrefixes.stringToIndex(uri);
1466
1467 }
1468
1469 /**
1470 * Get a prefix either from the qname or from the uri mapping, or just make
1471 * one up!
1472 *
1473 * @return The prefix if there is one, or null.
1474 */
1475 public String getNamespaceURI(String prefix)
1476 {
1477
1478 String uri = "";
1479 int prefixIndex = m_contextIndexes.peek() - 1 ;
1480
1481 if(null == prefix)
1482 prefix = "";
1483
1484 do
1485 {
1486 prefixIndex = m_prefixMappings.indexOf(prefix, ++prefixIndex);
1487 } while ( (prefixIndex >= 0) && (prefixIndex & 0x01) == 0x01);
1488
1489 if (prefixIndex > -1)
1490 {
1491 uri = (String) m_prefixMappings.elementAt(prefixIndex + 1);
1492 }
1493
1494
1495 return uri;
1496 }
1497
1498 /**
1499 * Set an ID string to node association in the ID table.
1500 *
1501 * @param id The ID string.
1502 * @param elem The associated element handle.
1503 */
1504 public void setIDAttribute(String id, int elem)
1505 {
1506 m_idAttributes.put(id, new Integer(elem));
1507 }
1508
1509 /**
1510 * Check whether accumulated text should be stripped; if not,
1511 * append the appropriate flavor of text/cdata node.
1512 */
1513 protected void charactersFlush()
1514 {
1515
1516 if (m_textPendingStart >= 0) // -1 indicates no-text-in-progress
1517 {
1518 int length = m_chars.size() - m_textPendingStart;
1519 boolean doStrip = false;
1520
1521 if (getShouldStripWhitespace())
1522 {
1523 doStrip = m_chars.isWhitespace(m_textPendingStart, length);
1524 }
1525
1526 if (doStrip) {
1527 m_chars.setLength(m_textPendingStart); // Discard accumulated text
1528 } else {
1529 // Guard against characters/ignorableWhitespace events that
1530 // contained no characters. They should not result in a node.
1531 if (length > 0) {
1532 int exName = m_expandedNameTable.getExpandedTypeID(DTM.TEXT_NODE);
1533 int dataIndex = m_data.size();
1534
1535 m_previous = addNode(m_coalescedTextType, exName,
1536 m_parents.peek(), m_previous, dataIndex, false);
1537
1538 m_data.addElement(m_textPendingStart);
1539 m_data.addElement(length);
1540 }
1541 }
1542
1543 // Reset for next text block
1544 m_textPendingStart = -1;
1545 m_textType = m_coalescedTextType = DTM.TEXT_NODE;
1546 }
1547 }
1548
1549 ////////////////////////////////////////////////////////////////////
1550 // Implementation of the EntityResolver interface.
1551 ////////////////////////////////////////////////////////////////////
1552
1553 /**
1554 * Resolve an external entity.
1555 *
1556 * <p>Always return null, so that the parser will use the system
1557 * identifier provided in the XML document. This method implements
1558 * the SAX default behaviour: application writers can override it
1559 * in a subclass to do special translations such as catalog lookups
1560 * or URI redirection.</p>
1561 *
1562 * @param publicId The public identifer, or null if none is
1563 * available.
1564 * @param systemId The system identifier provided in the XML
1565 * document.
1566 * @return The new input source, or null to require the
1567 * default behaviour.
1568 * @throws SAXException Any SAX exception, possibly
1569 * wrapping another exception.
1570 * @see org.xml.sax.EntityResolver#resolveEntity
1571 *
1572 * @throws SAXException
1573 */
1574 public InputSource resolveEntity(String publicId, String systemId)
1575 throws SAXException
1576 {
1577 return null;
1578 }
1579
1580 ////////////////////////////////////////////////////////////////////
1581 // Implementation of DTDHandler interface.
1582 ////////////////////////////////////////////////////////////////////
1583
1584 /**
1585 * Receive notification of a notation declaration.
1586 *
1587 * <p>By default, do nothing. Application writers may override this
1588 * method in a subclass if they wish to keep track of the notations
1589 * declared in a document.</p>
1590 *
1591 * @param name The notation name.
1592 * @param publicId The notation public identifier, or null if not
1593 * available.
1594 * @param systemId The notation system identifier.
1595 * @throws SAXException Any SAX exception, possibly
1596 * wrapping another exception.
1597 * @see org.xml.sax.DTDHandler#notationDecl
1598 *
1599 * @throws SAXException
1600 */
1601 public void notationDecl(String name, String publicId, String systemId)
1602 throws SAXException
1603 {
1604
1605 // no op
1606 }
1607
1608 /**
1609 * Receive notification of an unparsed entity declaration.
1610 *
1611 * <p>By default, do nothing. Application writers may override this
1612 * method in a subclass to keep track of the unparsed entities
1613 * declared in a document.</p>
1614 *
1615 * @param name The entity name.
1616 * @param publicId The entity public identifier, or null if not
1617 * available.
1618 * @param systemId The entity system identifier.
1619 * @param notationName The name of the associated notation.
1620 * @throws SAXException Any SAX exception, possibly
1621 * wrapping another exception.
1622 * @see org.xml.sax.DTDHandler#unparsedEntityDecl
1623 *
1624 * @throws SAXException
1625 */
1626 public void unparsedEntityDecl(
1627 String name, String publicId, String systemId, String notationName)
1628 throws SAXException
1629 {
1630
1631 if (null == m_entities)
1632 {
1633 m_entities = new Vector();
1634 }
1635
1636 try
1637 {
1638 systemId = SystemIDResolver.getAbsoluteURI(systemId,
1639 getDocumentBaseURI());
1640 }
1641 catch (Exception e)
1642 {
1643 throw new org.xml.sax.SAXException(e);
1644 }
1645
1646 // private static final int ENTITY_FIELD_PUBLICID = 0;
1647 m_entities.addElement(publicId);
1648
1649 // private static final int ENTITY_FIELD_SYSTEMID = 1;
1650 m_entities.addElement(systemId);
1651
1652 // private static final int ENTITY_FIELD_NOTATIONNAME = 2;
1653 m_entities.addElement(notationName);
1654
1655 // private static final int ENTITY_FIELD_NAME = 3;
1656 m_entities.addElement(name);
1657 }
1658
1659 ////////////////////////////////////////////////////////////////////
1660 // Implementation of ContentHandler interface.
1661 ////////////////////////////////////////////////////////////////////
1662
1663 /**
1664 * Receive a Locator object for document events.
1665 *
1666 * <p>By default, do nothing. Application writers may override this
1667 * method in a subclass if they wish to store the locator for use
1668 * with other document events.</p>
1669 *
1670 * @param locator A locator for all SAX document events.
1671 * @see org.xml.sax.ContentHandler#setDocumentLocator
1672 * @see org.xml.sax.Locator
1673 */
1674 public void setDocumentLocator(Locator locator)
1675 {
1676 m_locator = locator;
1677 m_systemId = locator.getSystemId();
1678 }
1679
1680 /**
1681 * Receive notification of the beginning of the document.
1682 *
1683 * @throws SAXException Any SAX exception, possibly
1684 * wrapping another exception.
1685 * @see org.xml.sax.ContentHandler#startDocument
1686 */
1687 public void startDocument() throws SAXException
1688 {
1689 if (DEBUG)
1690 System.out.println("startDocument");
1691
1692
1693 int doc = addNode(DTM.DOCUMENT_NODE,
1694 m_expandedNameTable.getExpandedTypeID(DTM.DOCUMENT_NODE),
1695 DTM.NULL, DTM.NULL, 0, true);
1696
1697 m_parents.push(doc);
1698 m_previous = DTM.NULL;
1699
1700 m_contextIndexes.push(m_prefixMappings.size()); // for the next element.
1701 }
1702
1703 /**
1704 * Receive notification of the end of the document.
1705 *
1706 * @throws SAXException Any SAX exception, possibly
1707 * wrapping another exception.
1708 * @see org.xml.sax.ContentHandler#endDocument
1709 */
1710 public void endDocument() throws SAXException
1711 {
1712 if (DEBUG)
1713 System.out.println("endDocument");
1714
1715 charactersFlush();
1716
1717 m_nextsib.setElementAt(NULL,0);
1718
1719 if (m_firstch.elementAt(0) == NOTPROCESSED)
1720 m_firstch.setElementAt(NULL,0);
1721
1722 if (DTM.NULL != m_previous)
1723 m_nextsib.setElementAt(DTM.NULL,m_previous);
1724
1725 m_parents = null;
1726 m_prefixMappings = null;
1727 m_contextIndexes = null;
1728
1729 m_endDocumentOccured = true;
1730
1731 // Bugzilla 4858: throw away m_locator. we cache m_systemId
1732 m_locator = null;
1733 }
1734
1735 /**
1736 * Receive notification of the start of a Namespace mapping.
1737 *
1738 * <p>By default, do nothing. Application writers may override this
1739 * method in a subclass to take specific actions at the start of
1740 * each Namespace prefix scope (such as storing the prefix mapping).</p>
1741 *
1742 * @param prefix The Namespace prefix being declared.
1743 * @param uri The Namespace URI mapped to the prefix.
1744 * @throws SAXException Any SAX exception, possibly
1745 * wrapping another exception.
1746 * @see org.xml.sax.ContentHandler#startPrefixMapping
1747 */
1748 public void startPrefixMapping(String prefix, String uri)
1749 throws SAXException
1750 {
1751
1752 if (DEBUG)
1753 System.out.println("startPrefixMapping: prefix: " + prefix + ", uri: "
1754 + uri);
1755
1756 if(null == prefix)
1757 prefix = "";
1758 m_prefixMappings.addElement(prefix); // JDK 1.1.x compat -sc
1759 m_prefixMappings.addElement(uri); // JDK 1.1.x compat -sc
1760 }
1761
1762 /**
1763 * Receive notification of the end of a Namespace mapping.
1764 *
1765 * <p>By default, do nothing. Application writers may override this
1766 * method in a subclass to take specific actions at the end of
1767 * each prefix mapping.</p>
1768 *
1769 * @param prefix The Namespace prefix being declared.
1770 * @throws SAXException Any SAX exception, possibly
1771 * wrapping another exception.
1772 * @see org.xml.sax.ContentHandler#endPrefixMapping
1773 */
1774 public void endPrefixMapping(String prefix) throws SAXException
1775 {
1776 if (DEBUG)
1777 System.out.println("endPrefixMapping: prefix: " + prefix);
1778
1779 if(null == prefix)
1780 prefix = "";
1781
1782 int index = m_contextIndexes.peek() - 1;
1783
1784 do
1785 {
1786 index = m_prefixMappings.indexOf(prefix, ++index);
1787 } while ( (index >= 0) && ((index & 0x01) == 0x01) );
1788
1789
1790 if (index > -1)
1791 {
1792 m_prefixMappings.setElementAt("%@$#^@#", index);
1793 m_prefixMappings.setElementAt("%@$#^@#", index + 1);
1794 }
1795
1796 // no op
1797 }
1798
1799 /**
1800 * Check if a declaration has already been made for a given prefix.
1801 *
1802 * @param prefix non-null prefix string.
1803 *
1804 * @return true if the declaration has already been declared in the
1805 * current context.
1806 */
1807 protected boolean declAlreadyDeclared(String prefix)
1808 {
1809
1810 int startDecls = m_contextIndexes.peek();
1811 java.util.Vector prefixMappings = m_prefixMappings;
1812 int nDecls = prefixMappings.size();
1813
1814 for (int i = startDecls; i < nDecls; i += 2)
1815 {
1816 String prefixDecl = (String) prefixMappings.elementAt(i);
1817
1818 if (prefixDecl == null)
1819 continue;
1820
1821 if (prefixDecl.equals(prefix))
1822 return true;
1823 }
1824
1825 return false;
1826 }
1827
1828 boolean m_pastFirstElement=false;
1829
1830 /**
1831 * Receive notification of the start of an element.
1832 *
1833 * <p>By default, do nothing. Application writers may override this
1834 * method in a subclass to take specific actions at the start of
1835 * each element (such as allocating a new tree node or writing
1836 * output to a file).</p>
1837 *
1838 * @param uri The Namespace URI, or the empty string if the
1839 * element has no Namespace URI or if Namespace
1840 * processing is not being performed.
1841 * @param localName The local name (without prefix), or the
1842 * empty string if Namespace processing is not being
1843 * performed.
1844 * @param qName The qualified name (with prefix), or the
1845 * empty string if qualified names are not available.
1846 * @param attributes The specified or defaulted attributes.
1847 * @throws SAXException Any SAX exception, possibly
1848 * wrapping another exception.
1849 * @see org.xml.sax.ContentHandler#startElement
1850 */
1851 public void startElement(
1852 String uri, String localName, String qName, Attributes attributes)
1853 throws SAXException
1854 {
1855 if (DEBUG)
1856 {
1857 System.out.println("startElement: uri: " + uri + ", localname: "
1858 + localName + ", qname: "+qName+", atts: " + attributes);
1859
1860 boolean DEBUG_ATTRS=true;
1861 if(DEBUG_ATTRS & attributes!=null)
1862 {
1863 int n = attributes.getLength();
1864 if(n==0)
1865 System.out.println("\tempty attribute list");
1866 else for (int i = 0; i < n; i++)
1867 System.out.println("\t attr: uri: " + attributes.getURI(i) +
1868 ", localname: " + attributes.getLocalName(i) +
1869 ", qname: " + attributes.getQName(i) +
1870 ", type: " + attributes.getType(i) +
1871 ", value: " + attributes.getValue(i)
1872 );
1873 }
1874 }
1875
1876 charactersFlush();
1877
1878 int exName = m_expandedNameTable.getExpandedTypeID(uri, localName, DTM.ELEMENT_NODE);
1879 String prefix = getPrefix(qName, uri);
1880 int prefixIndex = (null != prefix)
1881 ? m_valuesOrPrefixes.stringToIndex(qName) : 0;
1882
1883 int elemNode = addNode(DTM.ELEMENT_NODE, exName,
1884 m_parents.peek(), m_previous, prefixIndex, true);
1885
1886 if(m_indexing)
1887 indexNode(exName, elemNode);
1888
1889
1890 m_parents.push(elemNode);
1891
1892 int startDecls = m_contextIndexes.peek();
1893 int nDecls = m_prefixMappings.size();
1894 int prev = DTM.NULL;
1895
1896 if(!m_pastFirstElement)
1897 {
1898 // SPECIAL CASE: Implied declaration at root element
1899 prefix="xml";
1900 String declURL = "http://www.w3.org/XML/1998/namespace";
1901 exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
1902 int val = m_valuesOrPrefixes.stringToIndex(declURL);
1903 prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode,
1904 prev, val, false);
1905 m_pastFirstElement=true;
1906 }
1907
1908 for (int i = startDecls; i < nDecls; i += 2)
1909 {
1910 prefix = (String) m_prefixMappings.elementAt(i);
1911
1912 if (prefix == null)
1913 continue;
1914
1915 String declURL = (String) m_prefixMappings.elementAt(i + 1);
1916
1917 exName = m_expandedNameTable.getExpandedTypeID(null, prefix, DTM.NAMESPACE_NODE);
1918
1919 int val = m_valuesOrPrefixes.stringToIndex(declURL);
1920
1921 prev = addNode(DTM.NAMESPACE_NODE, exName, elemNode,
1922 prev, val, false);
1923 }
1924
1925 int n = attributes.getLength();
1926
1927 for (int i = 0; i < n; i++)
1928 {
1929 String attrUri = attributes.getURI(i);
1930 String attrQName = attributes.getQName(i);
1931 String valString = attributes.getValue(i);
1932
1933 prefix = getPrefix(attrQName, attrUri);
1934
1935 int nodeType;
1936
1937 String attrLocalName = attributes.getLocalName(i);
1938
1939 if ((null != attrQName)
1940 && (attrQName.equals("xmlns")
1941 || attrQName.startsWith("xmlns:")))
1942 {
1943 if (declAlreadyDeclared(prefix))
1944 continue; // go to the next attribute.
1945
1946 nodeType = DTM.NAMESPACE_NODE;
1947 }
1948 else
1949 {
1950 nodeType = DTM.ATTRIBUTE_NODE;
1951
1952 if (attributes.getType(i).equalsIgnoreCase("ID"))
1953 setIDAttribute(valString, elemNode);
1954 }
1955
1956 // Bit of a hack... if somehow valString is null, stringToIndex will
1957 // return -1, which will make things very unhappy.
1958 if(null == valString)
1959 valString = "";
1960
1961 int val = m_valuesOrPrefixes.stringToIndex(valString);
1962 //String attrLocalName = attributes.getLocalName(i);
1963
1964 if (null != prefix)
1965 {
1966
1967 prefixIndex = m_valuesOrPrefixes.stringToIndex(attrQName);
1968
1969 int dataIndex = m_data.size();
1970
1971 m_data.addElement(prefixIndex);
1972 m_data.addElement(val);
1973
1974 val = -dataIndex;
1975 }
1976
1977 exName = m_expandedNameTable.getExpandedTypeID(attrUri, attrLocalName, nodeType);
1978 prev = addNode(nodeType, exName, elemNode, prev, val,
1979 false);
1980 }
1981
1982 if (DTM.NULL != prev)
1983 m_nextsib.setElementAt(DTM.NULL,prev);
1984
1985 if (null != m_wsfilter)
1986 {
1987 short wsv = m_wsfilter.getShouldStripSpace(makeNodeHandle(elemNode), this);
1988 boolean shouldStrip = (DTMWSFilter.INHERIT == wsv)
1989 ? getShouldStripWhitespace()
1990 : (DTMWSFilter.STRIP == wsv);
1991
1992 pushShouldStripWhitespace(shouldStrip);
1993 }
1994
1995 m_previous = DTM.NULL;
1996
1997 m_contextIndexes.push(m_prefixMappings.size()); // for the children.
1998 }
1999
2000 /**
2001 * Receive notification of the end of an element.
2002 *
2003 * <p>By default, do nothing. Application writers may override this
2004 * method in a subclass to take specific actions at the end of
2005 * each element (such as finalising a tree node or writing
2006 * output to a file).</p>
2007 *
2008 * @param uri The Namespace URI, or the empty string if the
2009 * element has no Namespace URI or if Namespace
2010 * processing is not being performed.
2011 * @param localName The local name (without prefix), or the
2012 * empty string if Namespace processing is not being
2013 * performed.
2014 * @param qName The qualified XML 1.0 name (with prefix), or the
2015 * empty string if qualified names are not available.
2016 * @throws SAXException Any SAX exception, possibly
2017 * wrapping another exception.
2018 * @see org.xml.sax.ContentHandler#endElement
2019 */
2020 public void endElement(String uri, String localName, String qName)
2021 throws SAXException
2022 {
2023 if (DEBUG)
2024 System.out.println("endElement: uri: " + uri + ", localname: "
2025 + localName + ", qname: "+qName);
2026
2027 charactersFlush();
2028
2029 // If no one noticed, startPrefixMapping is a drag.
2030 // Pop the context for the last child (the one pushed by startElement)
2031 m_contextIndexes.quickPop(1);
2032
2033 // Do it again for this one (the one pushed by the last endElement).
2034 int topContextIndex = m_contextIndexes.peek();
2035 if (topContextIndex != m_prefixMappings.size()) {
2036 m_prefixMappings.setSize(topContextIndex);
2037 }
2038
2039 int lastNode = m_previous;
2040
2041 m_previous = m_parents.pop();
2042
2043 // If lastNode is still DTM.NULL, this element had no children
2044 if (DTM.NULL == lastNode)
2045 m_firstch.setElementAt(DTM.NULL,m_previous);
2046 else
2047 m_nextsib.setElementAt(DTM.NULL,lastNode);
2048
2049 popShouldStripWhitespace();
2050 }
2051
2052 /**
2053 * Receive notification of character data inside an element.
2054 *
2055 * <p>By default, do nothing. Application writers may override this
2056 * method to take specific actions for each chunk of character data
2057 * (such as adding the data to a node or buffer, or printing it to
2058 * a file).</p>
2059 *
2060 * @param ch The characters.
2061 * @param start The start position in the character array.
2062 * @param length The number of characters to use from the
2063 * character array.
2064 * @throws SAXException Any SAX exception, possibly
2065 * wrapping another exception.
2066 * @see org.xml.sax.ContentHandler#characters
2067 */
2068 public void characters(char ch[], int start, int length) throws SAXException
2069 {
2070 if (m_textPendingStart == -1) // First one in this block
2071 {
2072 m_textPendingStart = m_chars.size();
2073 m_coalescedTextType = m_textType;
2074 }
2075 // Type logic: If all adjacent text is CDATASections, the
2076 // concatentated text is treated as a single CDATASection (see
2077 // initialization above). If any were ordinary Text, the whole
2078 // thing is treated as Text. This may be worth %REVIEW%ing.
2079 else if (m_textType == DTM.TEXT_NODE)
2080 {
2081 m_coalescedTextType = DTM.TEXT_NODE;
2082 }
2083
2084 m_chars.append(ch, start, length);
2085 }
2086
2087 /**
2088 * Receive notification of ignorable whitespace in element content.
2089 *
2090 * <p>By default, do nothing. Application writers may override this
2091 * method to take specific actions for each chunk of ignorable
2092 * whitespace (such as adding data to a node or buffer, or printing
2093 * it to a file).</p>
2094 *
2095 * @param ch The whitespace characters.
2096 * @param start The start position in the character array.
2097 * @param length The number of characters to use from the
2098 * character array.
2099 * @throws SAXException Any SAX exception, possibly
2100 * wrapping another exception.
2101 * @see org.xml.sax.ContentHandler#ignorableWhitespace
2102 */
2103 public void ignorableWhitespace(char ch[], int start, int length)
2104 throws SAXException
2105 {
2106
2107 // %OPT% We can probably take advantage of the fact that we know this
2108 // is whitespace.
2109 characters(ch, start, length);
2110 }
2111
2112 /**
2113 * Receive notification of a processing instruction.
2114 *
2115 * <p>By default, do nothing. Application writers may override this
2116 * method in a subclass to take specific actions for each
2117 * processing instruction, such as setting status variables or
2118 * invoking other methods.</p>
2119 *
2120 * @param target The processing instruction target.
2121 * @param data The processing instruction data, or null if
2122 * none is supplied.
2123 * @throws SAXException Any SAX exception, possibly
2124 * wrapping another exception.
2125 * @see org.xml.sax.ContentHandler#processingInstruction
2126 */
2127 public void processingInstruction(String target, String data)
2128 throws SAXException
2129 {
2130 if (DEBUG)
2131 System.out.println("processingInstruction: target: " + target +", data: "+data);
2132
2133 charactersFlush();
2134
2135 int exName = m_expandedNameTable.getExpandedTypeID(null, target,
2136 DTM.PROCESSING_INSTRUCTION_NODE);
2137 int dataIndex = m_valuesOrPrefixes.stringToIndex(data);
2138
2139 m_previous = addNode(DTM.PROCESSING_INSTRUCTION_NODE, exName,
2140 m_parents.peek(), m_previous,
2141 dataIndex, false);
2142 }
2143
2144 /**
2145 * Receive notification of a skipped entity.
2146 *
2147 * <p>By default, do nothing. Application writers may override this
2148 * method in a subclass to take specific actions for each
2149 * processing instruction, such as setting status variables or
2150 * invoking other methods.</p>
2151 *
2152 * @param name The name of the skipped entity.
2153 * @throws SAXException Any SAX exception, possibly
2154 * wrapping another exception.
2155 * @see org.xml.sax.ContentHandler#processingInstruction
2156 */
2157 public void skippedEntity(String name) throws SAXException
2158 {
2159
2160 // %REVIEW% What should be done here?
2161 // no op
2162 }
2163
2164 ////////////////////////////////////////////////////////////////////
2165 // Implementation of the ErrorHandler interface.
2166 ////////////////////////////////////////////////////////////////////
2167
2168 /**
2169 * Receive notification of a parser warning.
2170 *
2171 * <p>The default implementation does nothing. Application writers
2172 * may override this method in a subclass to take specific actions
2173 * for each warning, such as inserting the message in a log file or
2174 * printing it to the console.</p>
2175 *
2176 * @param e The warning information encoded as an exception.
2177 * @throws SAXException Any SAX exception, possibly
2178 * wrapping another exception.
2179 * @see org.xml.sax.ErrorHandler#warning
2180 * @see org.xml.sax.SAXParseException
2181 */
2182 public void warning(SAXParseException e) throws SAXException
2183 {
2184
2185 // %REVIEW% Is there anyway to get the JAXP error listener here?
2186 System.err.println(e.getMessage());
2187 }
2188
2189 /**
2190 * Receive notification of a recoverable parser error.
2191 *
2192 * <p>The default implementation does nothing. Application writers
2193 * may override this method in a subclass to take specific actions
2194 * for each error, such as inserting the message in a log file or
2195 * printing it to the console.</p>
2196 *
2197 * @param e The warning information encoded as an exception.
2198 * @throws SAXException Any SAX exception, possibly
2199 * wrapping another exception.
2200 * @see org.xml.sax.ErrorHandler#warning
2201 * @see org.xml.sax.SAXParseException
2202 */
2203 public void error(SAXParseException e) throws SAXException
2204 {
2205 throw e;
2206 }
2207
2208 /**
2209 * Report a fatal XML parsing error.
2210 *
2211 * <p>The default implementation throws a SAXParseException.
2212 * Application writers may override this method in a subclass if
2213 * they need to take specific actions for each fatal error (such as
2214 * collecting all of the errors into a single report): in any case,
2215 * the application must stop all regular processing when this
2216 * method is invoked, since the document is no longer reliable, and
2217 * the parser may no longer report parsing events.</p>
2218 *
2219 * @param e The error information encoded as an exception.
2220 * @throws SAXException Any SAX exception, possibly
2221 * wrapping another exception.
2222 * @see org.xml.sax.ErrorHandler#fatalError
2223 * @see org.xml.sax.SAXParseException
2224 */
2225 public void fatalError(SAXParseException e) throws SAXException
2226 {
2227 throw e;
2228 }
2229
2230 ////////////////////////////////////////////////////////////////////
2231 // Implementation of the DeclHandler interface.
2232 ////////////////////////////////////////////////////////////////////
2233
2234 /**
2235 * Report an element type declaration.
2236 *
2237 * <p>The content model will consist of the string "EMPTY", the
2238 * string "ANY", or a parenthesised group, optionally followed
2239 * by an occurrence indicator. The model will be normalized so
2240 * that all whitespace is removed,and will include the enclosing
2241 * parentheses.</p>
2242 *
2243 * @param name The element type name.
2244 * @param model The content model as a normalized string.
2245 * @throws SAXException The application may raise an exception.
2246 */
2247 public void elementDecl(String name, String model) throws SAXException
2248 {
2249
2250 // no op
2251 }
2252
2253 /**
2254 * Report an attribute type declaration.
2255 *
2256 * <p>Only the effective (first) declaration for an attribute will
2257 * be reported. The type will be one of the strings "CDATA",
2258 * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
2259 * "ENTITIES", or "NOTATION", or a parenthesized token group with
2260 * the separator "|" and all whitespace removed.</p>
2261 *
2262 * @param eName The name of the associated element.
2263 * @param aName The name of the attribute.
2264 * @param type A string representing the attribute type.
2265 * @param valueDefault A string representing the attribute default
2266 * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
2267 * none of these applies.
2268 * @param value A string representing the attribute's default value,
2269 * or null if there is none.
2270 * @throws SAXException The application may raise an exception.
2271 */
2272 public void attributeDecl(
2273 String eName, String aName, String type, String valueDefault, String value)
2274 throws SAXException
2275 {
2276
2277 // no op
2278 }
2279
2280 /**
2281 * Report an internal entity declaration.
2282 *
2283 * <p>Only the effective (first) declaration for each entity
2284 * will be reported.</p>
2285 *
2286 * @param name The name of the entity. If it is a parameter
2287 * entity, the name will begin with '%'.
2288 * @param value The replacement text of the entity.
2289 * @throws SAXException The application may raise an exception.
2290 * @see #externalEntityDecl
2291 * @see org.xml.sax.DTDHandler#unparsedEntityDecl
2292 */
2293 public void internalEntityDecl(String name, String value)
2294 throws SAXException
2295 {
2296
2297 // no op
2298 }
2299
2300 /**
2301 * Report a parsed external entity declaration.
2302 *
2303 * <p>Only the effective (first) declaration for each entity
2304 * will be reported.</p>
2305 *
2306 * @param name The name of the entity. If it is a parameter
2307 * entity, the name will begin with '%'.
2308 * @param publicId The declared public identifier of the entity, or
2309 * null if none was declared.
2310 * @param systemId The declared system identifier of the entity.
2311 * @throws SAXException The application may raise an exception.
2312 * @see #internalEntityDecl
2313 * @see org.xml.sax.DTDHandler#unparsedEntityDecl
2314 */
2315 public void externalEntityDecl(
2316 String name, String publicId, String systemId) throws SAXException
2317 {
2318
2319 // no op
2320 }
2321
2322 ////////////////////////////////////////////////////////////////////
2323 // Implementation of the LexicalHandler interface.
2324 ////////////////////////////////////////////////////////////////////
2325
2326 /**
2327 * Report the start of DTD declarations, if any.
2328 *
2329 * <p>Any declarations are assumed to be in the internal subset
2330 * unless otherwise indicated by a {@link #startEntity startEntity}
2331 * event.</p>
2332 *
2333 * <p>Note that the start/endDTD events will appear within
2334 * the start/endDocument events from ContentHandler and
2335 * before the first startElement event.</p>
2336 *
2337 * @param name The document type name.
2338 * @param publicId The declared public identifier for the
2339 * external DTD subset, or null if none was declared.
2340 * @param systemId The declared system identifier for the
2341 * external DTD subset, or null if none was declared.
2342 * @throws SAXException The application may raise an
2343 * exception.
2344 * @see #endDTD
2345 * @see #startEntity
2346 */
2347 public void startDTD(String name, String publicId, String systemId)
2348 throws SAXException
2349 {
2350
2351 m_insideDTD = true;
2352 }
2353
2354 /**
2355 * Report the end of DTD declarations.
2356 *
2357 * @throws SAXException The application may raise an exception.
2358 * @see #startDTD
2359 */
2360 public void endDTD() throws SAXException
2361 {
2362
2363 m_insideDTD = false;
2364 }
2365
2366 /**
2367 * Report the beginning of an entity in content.
2368 *
2369 * <p><strong>NOTE:</entity> entity references in attribute
2370 * values -- and the start and end of the document entity --
2371 * are never reported.</p>
2372 *
2373 * <p>The start and end of the external DTD subset are reported
2374 * using the pseudo-name "[dtd]". All other events must be
2375 * properly nested within start/end entity events.</p>
2376 *
2377 * <p>Note that skipped entities will be reported through the
2378 * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
2379 * event, which is part of the ContentHandler interface.</p>
2380 *
2381 * @param name The name of the entity. If it is a parameter
2382 * entity, the name will begin with '%'.
2383 * @throws SAXException The application may raise an exception.
2384 * @see #endEntity
2385 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2386 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2387 */
2388 public void startEntity(String name) throws SAXException
2389 {
2390
2391 // no op
2392 }
2393
2394 /**
2395 * Report the end of an entity.
2396 *
2397 * @param name The name of the entity that is ending.
2398 * @throws SAXException The application may raise an exception.
2399 * @see #startEntity
2400 */
2401 public void endEntity(String name) throws SAXException
2402 {
2403
2404 // no op
2405 }
2406
2407 /**
2408 * Report the start of a CDATA section.
2409 *
2410 * <p>The contents of the CDATA section will be reported through
2411 * the regular {@link org.xml.sax.ContentHandler#characters
2412 * characters} event.</p>
2413 *
2414 * @throws SAXException The application may raise an exception.
2415 * @see #endCDATA
2416 */
2417 public void startCDATA() throws SAXException
2418 {
2419 m_textType = DTM.CDATA_SECTION_NODE;
2420 }
2421
2422 /**
2423 * Report the end of a CDATA section.
2424 *
2425 * @throws SAXException The application may raise an exception.
2426 * @see #startCDATA
2427 */
2428 public void endCDATA() throws SAXException
2429 {
2430 m_textType = DTM.TEXT_NODE;
2431 }
2432
2433 /**
2434 * Report an XML comment anywhere in the document.
2435 *
2436 * <p>This callback will be used for comments inside or outside the
2437 * document element, including comments in the external DTD
2438 * subset (if read).</p>
2439 *
2440 * @param ch An array holding the characters in the comment.
2441 * @param start The starting position in the array.
2442 * @param length The number of characters to use from the array.
2443 * @throws SAXException The application may raise an exception.
2444 */
2445 public void comment(char ch[], int start, int length) throws SAXException
2446 {
2447
2448 if (m_insideDTD) // ignore comments if we're inside the DTD
2449 return;
2450
2451 charactersFlush();
2452
2453 int exName = m_expandedNameTable.getExpandedTypeID(DTM.COMMENT_NODE);
2454
2455 // For now, treat comments as strings... I guess we should do a
2456 // seperate FSB buffer instead.
2457 int dataIndex = m_valuesOrPrefixes.stringToIndex(new String(ch, start,
2458 length));
2459
2460
2461 m_previous = addNode(DTM.COMMENT_NODE, exName,
2462 m_parents.peek(), m_previous, dataIndex, false);
2463 }
2464
2465 /**
2466 * Set a run time property for this DTM instance.
2467 *
2468 * %REVIEW% Now that we no longer use this method to support
2469 * getSourceLocatorFor, can we remove it?
2470 *
2471 * @param property a <code>String</code> value
2472 * @param value an <code>Object</code> value
2473 */
2474 public void setProperty(String property, Object value)
2475 {
2476 }
2477
2478 /** Retrieve the SourceLocator associated with a specific node.
2479 * This is only meaningful if the XalanProperties.SOURCE_LOCATION flag was
2480 * set True using setProperty; if it was never set, or was set false, we
2481 * will return null.
2482 *
2483 * (We _could_ return a locator with the document's base URI and bogus
2484 * line/column information. Trying that; see the else clause.)
2485 * */
2486 public SourceLocator getSourceLocatorFor(int node)
2487 {
2488 if (m_useSourceLocationProperty)
2489 {
2490
2491 node = makeNodeIdentity(node);
2492
2493
2494 return new NodeLocator(null,
2495 m_sourceSystemId.elementAt(node),
2496 m_sourceLine.elementAt(node),
2497 m_sourceColumn.elementAt(node));
2498 }
2499 else if(m_locator!=null)
2500 {
2501 return new NodeLocator(null,m_locator.getSystemId(),-1,-1);
2502 }
2503 else if(m_systemId!=null)
2504 {
2505 return new NodeLocator(null,m_systemId,-1,-1);
2506 }
2507 return null;
2508 }
2509
2510 public String getFixedNames(int type){
2511 return m_fixednames[type];
2512 }
2513 }