001: /*
002: * Copyright (C) Chaperon. All rights reserved.
003: * -------------------------------------------------------------------------
004: * This software is published under the terms of the Apache Software License
005: * version 1.1, a copy of which has been included with this distribution in
006: * the LICENSE file.
007: */
008:
009: package net.sourceforge.chaperon.process.extended;
010:
011: import net.sourceforge.chaperon.common.Decoder;
012: import net.sourceforge.chaperon.model.Violations;
013: import net.sourceforge.chaperon.model.extended.ExtendedGrammar;
014: import net.sourceforge.chaperon.model.extended.Pattern;
015: import net.sourceforge.chaperon.model.extended.PatternIterator;
016:
017: import org.apache.commons.logging.Log;
018:
019: import org.xml.sax.Attributes;
020: import org.xml.sax.ContentHandler;
021: import org.xml.sax.Locator;
022: import org.xml.sax.SAXException;
023: import org.xml.sax.ext.LexicalHandler;
024: import org.xml.sax.helpers.AttributesImpl;
025: import org.xml.sax.helpers.LocatorImpl;
026:
027: import java.util.Stack;
028:
029: /**
030: * This class represents a simulation of a pushdown automata using the parser automaton class.
031: *
032: * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
033: * @version CVS $Id: ExtendedDirectParserProcessor.java,v 1.12 2004/01/09 10:34:51 benedikta Exp $
034: */
035: public class ExtendedDirectParserProcessor implements ContentHandler,
036: LexicalHandler {
037: private static final String NS = "http://chaperon.sourceforge.net/schema/text/1.0";
038: private static final String TEXT = "text";
039:
040: /** Namespace for the generated SAX events. */
041: public static final String NS_OUTPUT = "http://chaperon.sourceforge.net/schema/syntaxtree/2.0";
042: private static final String OUTPUT = "output";
043:
044: // SAX variables
045: private ContentHandler contentHandler = null;
046: private LexicalHandler lexicalHandler = null;
047: private Locator locator = null;
048: private LocatorImpl locatorImpl = null;
049:
050: // State of SAX events
051: private static final int STATE_OUTER = 0;
052: private static final int STATE_INNER = 1;
053: private int state = STATE_OUTER;
054:
055: // Help variables
056: private ExtendedGrammar grammar;
057: private boolean flatten = false;
058: private StackNodeSet current = new StackNodeSet();
059: private StackNodeSet next = new StackNodeSet();
060: private Log log;
061: private StackNodeList root;
062: private int line = 1;
063: private int column = 1;
064: private static final int MAXWATCHDOG = 1000;
065:
066: /**
067: * Create a new parser processor.
068: */
069: public ExtendedDirectParserProcessor() {
070: }
071:
072: /**
073: * Create a new parser processor.
074: *
075: * @param automaton Parser automaton, which the processor should ues.
076: * @param handler Handler, which should receives the parser events.
077: * @param log Log, which should used.
078: */
079: public ExtendedDirectParserProcessor(ExtendedGrammar grammar,
080: Log log) {
081: setExtendedGrammar(grammar);
082: this .log = log;
083: }
084:
085: /**
086: * Set the parser automaton for the processor.
087: *
088: * @param automaton Parser automaton.
089: */
090: public void setExtendedGrammar(ExtendedGrammar grammar) {
091: this .grammar = grammar;
092:
093: Violations violations = grammar.validate();
094:
095: if ((violations != null)
096: && (violations.getViolationCount() > 0))
097: throw new IllegalArgumentException("Grammar is not valid: "
098: + violations.getViolation(0));
099:
100: if ((log != null) && (log.isDebugEnabled()))
101: log.debug("grammar:\n" + grammar);
102:
103: grammar.update();
104:
105: if ((log != null) && (log.isDebugEnabled())) {
106: StringBuffer buffer = new StringBuffer();
107: buffer.append("Successors:\n");
108: for (PatternIterator i = grammar.getAllPattern()
109: .getPattern(); i.hasNext();) {
110: Pattern pattern = i.next();
111: if (pattern.getSuccessors().hasNext()) {
112: buffer.append(pattern + "->{");
113: for (PatternIterator j = pattern.getSuccessors(); j
114: .hasNext();) {
115: buffer.append(j.next());
116: if (j.hasNext())
117: buffer.append(",");
118: }
119: buffer.append("}\n");
120: }
121: }
122:
123: buffer.append("\nAscending successors:\n");
124: for (PatternIterator i = grammar.getAllPattern()
125: .getPattern(); i.hasNext();) {
126: Pattern pattern = i.next();
127: if (pattern.getAscendingSuccessors().hasNext()) {
128: buffer.append(pattern + "->{");
129: for (PatternIterator j = pattern
130: .getAscendingSuccessors(); j.hasNext();) {
131: buffer.append(j.next());
132: if (j.hasNext())
133: buffer.append(",");
134: }
135: buffer.append("}\n");
136: }
137: }
138:
139: buffer.append("\nDescending successors:\n");
140: for (PatternIterator i = grammar.getAllPattern()
141: .getPattern(); i.hasNext();) {
142: Pattern pattern = i.next();
143: if (pattern.getDescendingSuccessors().hasNext()) {
144: buffer.append(pattern + "->{");
145: for (PatternIterator j = pattern
146: .getDescendingSuccessors(); j.hasNext();) {
147: buffer.append(j.next());
148: if (j.hasNext())
149: buffer.append(",");
150: }
151: buffer.append("}\n");
152: }
153: }
154: log.debug(buffer.toString());
155: }
156: }
157:
158: /**
159: * Set the <code>ContentHandler</code> that will receive XML data.
160: */
161: public void setContentHandler(ContentHandler handler) {
162: this .contentHandler = handler;
163: }
164:
165: /**
166: * Set the <code>LexicalHandler</code> that will receive XML data.
167: */
168: public void setLexicalHandler(LexicalHandler handler) {
169: this .lexicalHandler = handler;
170: }
171:
172: /**
173: * Provide processor with a log.
174: *
175: * @param log The log.
176: */
177: public void setLog(Log log) {
178: this .log = log;
179: }
180:
181: /**
182: * If the adapter should produce a more flatten XML hirachy, which means elements which the same
183: * name will be collapsed
184: *
185: * @param flatten True, if a more flatten hirachy should be produced.
186: */
187: public void setFlatten(boolean flatten) {
188: this .flatten = flatten;
189: }
190:
191: /**
192: * Receive an object for locating the origin of SAX document events.
193: */
194: public void setDocumentLocator(Locator locator) {
195: this .locator = locator;
196:
197: if (locator != null) {
198: this .locatorImpl = new LocatorImpl(locator);
199: contentHandler.setDocumentLocator(locatorImpl);
200: }
201: }
202:
203: /**
204: * Receive notification of the beginning of a document.
205: */
206: public void startDocument() throws SAXException {
207: locatorImpl.setLineNumber(locator.getLineNumber());
208: locatorImpl.setColumnNumber(locator.getColumnNumber());
209: contentHandler.startDocument();
210: state = STATE_OUTER;
211: }
212:
213: /**
214: * Receive notification of the beginning of an element.
215: */
216: public void startElement(String namespaceURI, String localName,
217: String qName, Attributes atts) throws SAXException {
218: locatorImpl.setLineNumber(locator.getLineNumber());
219: locatorImpl.setColumnNumber(locator.getColumnNumber());
220:
221: if (state == STATE_INNER)
222: throw new SAXException("Unexpected element " + qName);
223:
224: if (state == STATE_OUTER) {
225: if ((namespaceURI != null) && (namespaceURI.equals(NS))) {
226: if (!localName.equals(TEXT))
227: throw new SAXException("Unknown element " + qName);
228: } else {
229: contentHandler.startElement(namespaceURI, localName,
230: qName, atts);
231: return;
232: }
233: }
234:
235: state = STATE_INNER;
236:
237: // ======================= Start Text Document =======================
238: current.clear();
239: current.push(new TerminalStackNode(null, 0, grammar
240: .getStartPattern(), null));
241: next.clear();
242: line = 1;
243: column = 1;
244: }
245:
246: /**
247: * Receive notification of character data.
248: */
249: public void characters(char[] text, int textstart, int textlength)
250: throws SAXException {
251: locatorImpl.setLineNumber(locator.getLineNumber());
252: locatorImpl.setColumnNumber(locator.getColumnNumber());
253:
254: if (state == STATE_OUTER) {
255: contentHandler.characters(text, textstart, textlength);
256:
257: return;
258: }
259:
260: for (int position = textstart; position < (textstart + textlength); position++) {
261: if ((log != null) && (log.isDebugEnabled()))
262: log
263: .debug("===================================\nProcess "
264: + Decoder.toChar(text[position]));
265:
266: if (current.isEmpty())
267: throw new IllegalStateException(
268: "Parsing process is aborted");
269:
270: if ((log != null) && (log.isDebugEnabled()))
271: log.debug(getStatesAsString());
272:
273: while (!current.isEmpty()) {
274: StackNode node = current.pop();
275:
276: for (PatternIterator nextPattern = node.pattern
277: .getDescendingSuccessors(); nextPattern
278: .hasNext();)
279: if (nextPattern.next().contains(text[position])) {
280: reduce(
281: node.pattern.getDefinition()
282: .getSymbol(), node, null);
283: break;
284: }
285:
286: reduceEmpty(node);
287:
288: shift(node, text, position);
289:
290: if ((current.watchdog > MAXWATCHDOG)
291: || (next.watchdog > MAXWATCHDOG)) {
292: if ((log != null) && (log.isInfoEnabled()))
293: log.info(getStatesAsString());
294: throw new IllegalStateException(
295: "Aborted parsing because of a high ambiguous grammar"
296: + " [" + line + ":" + column + "]");
297: }
298: }
299:
300: if ((log != null) && (log.isDebugEnabled()))
301: log.debug(getStatesAsString());
302:
303: if (next.isEmpty()) {
304: if ((log != null) && (log.isInfoEnabled()))
305: log.info(getStatesAsString());
306: throw new IllegalArgumentException("Character '"
307: + text[position] + "' is not expected" + " ["
308: + line + ":" + column + "]");
309: }
310:
311: swapStacks();
312:
313: increasePosition(text, position, position + 1);
314: }
315: }
316:
317: /**
318: * Receive notification of the end of an element.
319: */
320: public void endElement(String namespaceURI, String localName,
321: String qName) throws SAXException {
322: locatorImpl.setLineNumber(locator.getLineNumber());
323: locatorImpl.setColumnNumber(locator.getColumnNumber());
324:
325: if (state == STATE_OUTER) {
326: contentHandler.endElement(namespaceURI, localName, qName);
327: return;
328: }
329:
330: if (state == STATE_INNER) {
331: if ((namespaceURI != null) && (namespaceURI.equals(NS))) {
332: if (!localName.equals(TEXT))
333: throw new SAXException("Unknown element " + qName);
334: } else
335: throw new SAXException("Unexpected element " + qName);
336: }
337:
338: state = STATE_OUTER;
339:
340: // ======================= End Text Document =======================
341: if ((log != null) && (log.isDebugEnabled()))
342: log
343: .debug("===================================\nProcess end of text");
344:
345: root = null;
346:
347: Pattern eot = grammar.getEndPattern();
348:
349: while (!current.isEmpty()) {
350: StackNode node = current.pop();
351:
352: for (PatternIterator nextPattern = node.pattern
353: .getDescendingSuccessors(); nextPattern.hasNext();)
354: if (nextPattern.next() == eot) {
355: reduce(node.pattern.getDefinition().getSymbol(),
356: node, null);
357: break;
358: }
359:
360: reduceEmpty(node);
361:
362: if ((current.watchdog > MAXWATCHDOG)
363: || (next.watchdog > MAXWATCHDOG)) {
364: if ((log != null) && (log.isInfoEnabled()))
365: log.info(getStatesAsString());
366: throw new IllegalStateException(
367: "Aborted parsing because of a high ambiguous grammar"
368: + " [" + line + ":" + column + "]");
369: }
370: }
371:
372: if ((log != null) && (log.isDebugEnabled()))
373: log.debug(getStatesAsString());
374:
375: if (root == null) {
376: if ((log != null) && (log.isInfoEnabled()))
377: log.info(getStatesAsString());
378: throw new IllegalStateException("Unexpected end of text"
379: + " [" + line + ":" + column + "]");
380: }
381:
382: fireEvents();
383: }
384:
385: /**
386: * Receive notification of ignorable whitespace in element content.
387: */
388: public void ignorableWhitespace(char[] ch, int start, int length)
389: throws SAXException {
390: locatorImpl.setLineNumber(locator.getLineNumber());
391: locatorImpl.setColumnNumber(locator.getColumnNumber());
392:
393: if (state == STATE_OUTER)
394: contentHandler.ignorableWhitespace(ch, start, length);
395: }
396:
397: /**
398: * Begin the scope of a prefix-URI Namespace mapping.
399: */
400: public void startPrefixMapping(String prefix, String uri)
401: throws SAXException {
402: locatorImpl.setLineNumber(locator.getLineNumber());
403: locatorImpl.setColumnNumber(locator.getColumnNumber());
404:
405: contentHandler.startPrefixMapping(prefix, uri);
406: }
407:
408: /**
409: * End the scope of a prefix-URI mapping.
410: */
411: public void endPrefixMapping(String prefix) throws SAXException {
412: locatorImpl.setLineNumber(locator.getLineNumber());
413: locatorImpl.setColumnNumber(locator.getColumnNumber());
414:
415: contentHandler.endPrefixMapping(prefix);
416: }
417:
418: /**
419: * Receive notification of a processing instruction.
420: */
421: public void processingInstruction(String target, String data)
422: throws SAXException {
423: locatorImpl.setLineNumber(locator.getLineNumber());
424: locatorImpl.setColumnNumber(locator.getColumnNumber());
425:
426: if (state == STATE_OUTER)
427: contentHandler.processingInstruction(target, data);
428: }
429:
430: /**
431: * Receive notification of a skipped entity.
432: */
433: public void skippedEntity(String name) throws SAXException {
434: locatorImpl.setLineNumber(locator.getLineNumber());
435: locatorImpl.setColumnNumber(locator.getColumnNumber());
436:
437: if (state == STATE_OUTER)
438: contentHandler.skippedEntity(name);
439: }
440:
441: /**
442: * Receive notification of the end of a document.
443: */
444: public void endDocument() throws SAXException {
445: locatorImpl.setLineNumber(locator.getLineNumber());
446: locatorImpl.setColumnNumber(locator.getColumnNumber());
447:
448: contentHandler.endDocument();
449: }
450:
451: /**
452: * Report the start of DTD declarations, if any.
453: */
454: public void startDTD(String name, String publicId, String systemId)
455: throws SAXException {
456: lexicalHandler.startDTD(name, publicId, systemId);
457: }
458:
459: /**
460: * Report the end of DTD declarations.
461: */
462: public void endDTD() throws SAXException {
463: lexicalHandler.endDTD();
464: }
465:
466: /**
467: * Report the beginning of an entity.
468: */
469: public void startEntity(String name) throws SAXException {
470: if (lexicalHandler != null)
471: lexicalHandler.startEntity(name);
472: }
473:
474: /**
475: * Report the end of an entity.
476: */
477: public void endEntity(String name) throws SAXException {
478: if (lexicalHandler != null)
479: lexicalHandler.endEntity(name);
480: }
481:
482: /**
483: * Report the start of a CDATA section.
484: */
485: public void startCDATA() throws SAXException {
486: if (lexicalHandler != null)
487: lexicalHandler.startCDATA();
488: }
489:
490: /**
491: * Report the end of a CDATA section.
492: */
493: public void endCDATA() throws SAXException {
494: if (lexicalHandler != null)
495: lexicalHandler.endCDATA();
496: }
497:
498: /**
499: * Report an XML comment anywhere in the document.
500: */
501: public void comment(char[] ch, int start, int len)
502: throws SAXException {
503: if (lexicalHandler != null)
504: lexicalHandler.comment(ch, start, len);
505: }
506:
507: private String getStatesAsString() {
508: StringBuffer buffer = new StringBuffer();
509: buffer.append("current:\n");
510: buffer.append(current);
511: buffer.append(current.dump());
512: buffer.append("Count of states:");
513: buffer.append(current.size());
514: buffer.append("\nnext:\n");
515: buffer.append(next);
516: buffer.append(next.dump());
517: buffer.append("Count of states:");
518: buffer.append(next.size());
519: return buffer.toString();
520: }
521:
522: private void swapStacks() {
523: StackNodeSet dummy = next;
524: next = current;
525: current = dummy;
526: next.clear();
527: }
528:
529: private void shift(StackNode node, char[] text, int position) {
530: for (PatternIterator i = node.pattern.getSuccessors(); i
531: .hasNext();) {
532: Pattern nextPattern = i.next();
533:
534: if (nextPattern.contains(text[position])) {
535: if (node instanceof NonterminalStackNode) {
536: for (PatternIterator j = node.last.pattern
537: .getSuccessors(); j.hasNext();)
538: if (j.next().contains(text[position]))
539: return;
540:
541: for (PatternIterator j = node.last.pattern
542: .getAscendingSuccessors(); j.hasNext();)
543: if (j.next().contains(text[position]))
544: return;
545: }
546:
547: StackNode newNode = new TerminalStackNode(text,
548: position, nextPattern, node);
549:
550: if ((log != null) && (log.isDebugEnabled()))
551: log.debug("shift " + newNode);
552:
553: next.push(newNode);
554: }
555: }
556:
557: for (PatternIterator i = node.pattern.getAscendingSuccessors(); i
558: .hasNext();) {
559: Pattern firstPattern = i.next();
560:
561: if (firstPattern.contains(text[position])) {
562: if (node instanceof NonterminalStackNode) {
563: for (PatternIterator j = node.last.pattern
564: .getSuccessors(); j.hasNext();)
565: if (j.next().contains(text[position]))
566: return;
567:
568: for (PatternIterator j = node.last.pattern
569: .getAscendingSuccessors(); j.hasNext();)
570: if (j.next().contains(text[position]))
571: return;
572: }
573:
574: StackNode newNode = new TerminalStackNode(text,
575: position, firstPattern, node);
576:
577: if ((log != null) && (log.isDebugEnabled()))
578: log.debug("shift " + newNode);
579:
580: next.push(newNode);
581: }
582: }
583: }
584:
585: private void reduce(String symbol, StackNode node,
586: StackNodeList list) {
587: if (node.sibling != null)
588: reduce(symbol, node.sibling, list);
589: list = new StackNodeList(node, list);
590: while (node.ancestor.pattern.hasSuccessor(node.pattern)) {
591: node = node.ancestor;
592: if (node.sibling != null)
593: reduce(symbol, node.sibling, list);
594: list = new StackNodeList(node, list);
595: }
596:
597: for (PatternIterator i = node.ancestor.pattern.getSuccessors(); i
598: .hasNext();) {
599: Pattern nextPattern = i.next();
600: if (symbol.equals(nextPattern.getSymbol())) {
601: StackNode newNode = new NonterminalStackNode(list,
602: nextPattern, node.ancestor);
603:
604: if ((log != null) && (log.isDebugEnabled()))
605: log.debug("reduce " + newNode + " with " + list);
606:
607: current.push(newNode);
608: }
609: }
610:
611: for (PatternIterator i = node.ancestor.pattern
612: .getAscendingSuccessors(); i.hasNext();) {
613: Pattern firstPattern = i.next();
614: if (symbol.equals(firstPattern.getSymbol())) {
615: StackNode newNode = new NonterminalStackNode(list,
616: firstPattern, node.ancestor);
617:
618: if ((log != null) && (log.isDebugEnabled()))
619: log.debug("reduce " + newNode + " with " + list);
620:
621: current.push(newNode);
622: }
623: }
624:
625: if ((root == null)
626: && (node.ancestor.pattern == grammar.getStartPattern())
627: && (symbol.equals(grammar.getStartSymbol()))) {
628: root = list;
629:
630: if ((log != null) && (log.isDebugEnabled()))
631: log.debug("accept " + symbol + " with " + list);
632: }
633: }
634:
635: private void reduceEmpty(StackNode node) {
636: for (PatternIterator i = node.pattern.getSuccessors(); i
637: .hasNext();) {
638: Pattern nextPattern = i.next();
639: if ((nextPattern.getSymbol() != null)
640: && (grammar.isNullable(nextPattern.getSymbol()))) {
641: StackNode newNode = new NonterminalStackNode(null,
642: nextPattern, node);
643:
644: if ((log != null) && (log.isDebugEnabled()))
645: log.debug("reduce " + newNode);
646:
647: current.push(newNode);
648: }
649: }
650:
651: for (PatternIterator i = node.pattern.getAscendingSuccessors(); i
652: .hasNext();) {
653: Pattern firstPattern = i.next();
654: if ((firstPattern.getSymbol() != null)
655: && (grammar.isNullable(firstPattern.getSymbol()))) {
656: // TODO: check for empty elements, which can occur in the ascending successors
657: if (firstPattern == node.pattern) {
658: //System.out.println("prevent empty element "+firstPattern);
659: continue;
660: }
661:
662: StackNode newNode = new NonterminalStackNode(null,
663: firstPattern, node);
664:
665: if ((log != null) && (log.isDebugEnabled()))
666: log.debug("reduce " + newNode);
667:
668: current.push(newNode);
669: }
670: }
671: }
672:
673: private void increasePosition(char[] text, int position,
674: int lastposition) {
675: for (int i = position; i < lastposition; i++) {
676: if (text[i] == '\n') {
677: column = 1;
678: line++;
679: } else if ((text[i] == '\r')
680: && ((i == (text.length - 1)) || (text[i + 1] != '\n'))) {
681: column = 1;
682: line++;
683: } else
684: column++;
685: }
686: }
687:
688: private void fireEvents() throws SAXException {
689: contentHandler.startPrefixMapping("", NS_OUTPUT);
690: contentHandler.startElement(NS_OUTPUT, OUTPUT, OUTPUT,
691: new AttributesImpl());
692:
693: String symbol = grammar.getStartSymbol();
694: contentHandler.startElement(NS_OUTPUT, symbol, symbol,
695: new AttributesImpl());
696:
697: Stack stack = new Stack();
698: StackNodeList next = root;
699: char[] text = null;
700: int position = 0;
701: int lastposition = 0;
702: line = 1;
703: column = 1;
704:
705: if (locatorImpl != null) {
706: locatorImpl.setLineNumber(line);
707: locatorImpl.setColumnNumber(column);
708: }
709:
710: while (next != null) {
711: if (next.node instanceof NonterminalStackNode) {
712: if (text != null) {
713: contentHandler.characters(text, position,
714: (lastposition + 1) - position);
715: increasePosition(text, position, (lastposition + 1)
716: - position);
717:
718: if (locatorImpl != null) {
719: locatorImpl.setLineNumber(line);
720: locatorImpl.setColumnNumber(column);
721: }
722:
723: text = null;
724: }
725:
726: NonterminalStackNode nonterminal = (NonterminalStackNode) next.node;
727:
728: AttributesImpl atts = new AttributesImpl();
729:
730: /*if (localizable)
731: {
732: atts.addAttribute("", "line", "line", "CDATA", String.valueOf(next.linenumber));
733: atts.addAttribute("", "column", "column", "CDATA", String.valueOf(next.columnnumber));
734: }*/
735: contentHandler.startElement(NS_OUTPUT,
736: next.node.pattern.getSymbol(),
737: next.node.pattern.getSymbol(), atts);
738: stack.push(next);
739: next = nonterminal.definition;
740: } else {
741: TerminalStackNode terminal = (TerminalStackNode) next.node;
742: if (text == null) {
743: text = terminal.text;
744: position = terminal.position;
745: } else if (text != terminal.text) {
746: contentHandler.characters(text, position,
747: (lastposition + 1) - position);
748: increasePosition(text, position, (lastposition + 1)
749: - position);
750:
751: if (locatorImpl != null) {
752: locatorImpl.setLineNumber(line);
753: locatorImpl.setColumnNumber(column);
754: }
755:
756: text = terminal.text;
757: position = terminal.position;
758: }
759:
760: lastposition = terminal.position;
761:
762: next = next.next;
763: }
764:
765: while ((next == null) && (!stack.isEmpty())) {
766: next = (StackNodeList) stack.pop();
767:
768: if (text != null) {
769: contentHandler.characters(text, position,
770: (lastposition + 1) - position);
771: increasePosition(text, position, (lastposition + 1)
772: - position);
773:
774: if (locatorImpl != null) {
775: locatorImpl.setLineNumber(line);
776: locatorImpl.setColumnNumber(column);
777: }
778:
779: text = null;
780: }
781:
782: contentHandler.endElement(NS_OUTPUT, next.node.pattern
783: .getSymbol(), next.node.pattern.getSymbol());
784: next = next.next;
785: }
786: }
787:
788: if (text != null) {
789: contentHandler.characters(text, position,
790: (lastposition + 1) - position);
791: increasePosition(text, position, (lastposition + 1)
792: - position);
793:
794: if (locatorImpl != null) {
795: locatorImpl.setLineNumber(line);
796: locatorImpl.setColumnNumber(column);
797: }
798:
799: text = null;
800: }
801:
802: contentHandler.endElement(NS_OUTPUT, symbol, symbol);
803:
804: contentHandler.endElement(NS_OUTPUT, OUTPUT, OUTPUT);
805: contentHandler.endPrefixMapping("");
806: }
807: }
|