View Javadoc

1   package org.apache.maven.doxia.module.docbook;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.util.Collection;
24  import java.util.HashSet;
25  import java.util.Stack;
26  
27  import org.apache.maven.doxia.macro.MacroExecutionException;
28  import org.apache.maven.doxia.markup.HtmlMarkup;
29  import org.apache.maven.doxia.parser.AbstractXmlParser;
30  import org.apache.maven.doxia.sink.Sink;
31  import org.apache.maven.doxia.sink.SinkEventAttributeSet;
32  
33  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
34  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
35  
36  /**
37   * Parse a <a href="http://www.docbook.org/schemas/simplified"><code>Simplified DocBook</code></a> document
38   * and emit events into the specified doxia Sink.
39   *
40   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
41   * @version $Id: DocBookParser.java 1090706 2011-04-09 23:15:28Z hboutemy $
42   * @since 1.0
43   * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="docbook"
44   */
45  public class DocBookParser
46      extends AbstractXmlParser
47      implements DocbookMarkup, SimplifiedDocbookMarkup
48  {
49      /**
50       * Level counter for calculating the section level.
51       */
52      private int level;
53  
54      /**
55       * Used to distinguish italic from bold.
56       */
57      private boolean isBold;
58  
59      private boolean inHead;
60  
61      private boolean ignore;
62  
63      private boolean simpleTag;
64  
65      private char trademark;
66  
67      /**
68       * A selective stack of parent elements
69       */
70      private final Stack<String> parent = new Stack<String>();
71  
72      /**
73       * The list of DocBook elements that introduce a new level of hierarchy.
74       */
75      private static final Collection<String> HIER_ELEMENTS = new HashSet<String>();
76  
77      /**
78       * Simplified DocBook elements that are direct children of &lt;article&gt;
79       * and that should be emitted into the Sink's head.
80       */
81      private static final Collection<String> META_ELEMENTS = new HashSet<String>();
82  
83      /**
84       * Simplified DocBook elements that occur within &lt;articleinfo&gt;
85       * and that are currently recognized by the parser.
86       */
87      private static final Collection<String> ARTICLEINFO_ELEMENTS = new HashSet<String>();
88  
89      /**
90       * The list of DocBook elements that will be rendered verbatim
91       */
92      private static final Collection<String> VERBATIM_ELEMENTS = new HashSet<String>();
93  
94      /**
95       * The list of DocBook elements that will be rendered inline and bold
96       */
97      private static final Collection<String> BOLD_ELEMENTS = new HashSet<String>();
98  
99      /**
100      * The list of DocBook elements that will be rendered inline and italic
101      */
102     private static final Collection<String> ITALIC_ELEMENTS = new HashSet<String>();
103 
104     /**
105      * The list of DocBook elements that will be rendered inline and monospace
106      */
107     private static final Collection<String> MONOSPACE_ELEMENTS = new HashSet<String>();
108 
109     /**
110      * The list of DocBook elements that may be ignored, either because they don't
111      * require any special processing or because they are not yet implemented.
112      */
113     private static final Collection<String> IGNORABLE_ELEMENTS = new HashSet<String>();
114     static
115     {
116         META_ELEMENTS.add( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() );
117         META_ELEMENTS.add( SimplifiedDocbookMarkup.AUTHORBLURB_TAG.toString() );
118         META_ELEMENTS.add( SimplifiedDocbookMarkup.SUBTITLE_TAG.toString() );
119         META_ELEMENTS.add( SimplifiedDocbookMarkup.TITLE_TAG.toString() );
120         META_ELEMENTS.add( SimplifiedDocbookMarkup.TITLEABBREV_TAG.toString() );
121 
122         ARTICLEINFO_ELEMENTS.add( SimplifiedDocbookMarkup.TITLE_TAG.toString() );
123         ARTICLEINFO_ELEMENTS.add( SimplifiedDocbookMarkup.CORPAUTHOR_TAG.toString() );
124         ARTICLEINFO_ELEMENTS.add( SimplifiedDocbookMarkup.DATE_TAG.toString() );
125 
126         HIER_ELEMENTS.add( SimplifiedDocbookMarkup.SECTION_TAG.toString() );
127         HIER_ELEMENTS.add( SimplifiedDocbookMarkup.APPENDIX_TAG.toString() );
128         HIER_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIOGRAPHY_TAG.toString() );
129         HIER_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIODIV_TAG.toString() );
130 
131         VERBATIM_ELEMENTS.add( SimplifiedDocbookMarkup.PROGRAMLISTING_TAG.toString() );
132         VERBATIM_ELEMENTS.add( SimplifiedDocbookMarkup.LITERALLAYOUT_TAG.toString() );
133 
134         BOLD_ELEMENTS.add( SimplifiedDocbookMarkup.COMMAND_TAG.toString() );
135         BOLD_ELEMENTS.add( SimplifiedDocbookMarkup.USERINPUT_TAG.toString() );
136 
137         ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.REPLACEABLE_TAG.toString() );
138         ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.SYSTEMITEM_TAG.toString() );
139         ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.CITETITLE_TAG.toString() );
140         ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.EMPHASIS_TAG.toString() );
141         ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.ATTRIBUTION_TAG.toString() );
142         ITALIC_ELEMENTS.add( SimplifiedDocbookMarkup.LINEANNOTATION_TAG.toString() );
143 
144         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.COMPUTEROUTPUT_TAG.toString() );
145         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.REPLACEABLE_TAG.toString() );
146         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.LITERAL_TAG.toString() );
147         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.OPTION_TAG.toString() );
148         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.SYSTEMITEM_TAG.toString() );
149         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.USERINPUT_TAG.toString() );
150         MONOSPACE_ELEMENTS.add( SimplifiedDocbookMarkup.FILENAME_TAG.toString() );
151 
152         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.ABBREV_TAG.toString() );
153         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.ABSTRACT_TAG.toString() );
154         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIOMIXED_TAG.toString() );
155         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.BIBLIOMSET_TAG.toString() );
156         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.COLSPEC_TAG.toString() );
157         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.EPIGRAPH_TAG.toString() );
158         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.EXAMPLE_TAG.toString() );
159         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.FOOTNOTEREF_TAG.toString() );
160         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG.toString() );
161         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.INLINEMEDIAOBJECT_TAG.toString() );
162         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.ISSUENUM_TAG.toString() );
163         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.PHRASE_TAG.toString() );
164         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.PUBDATE_TAG.toString() );
165         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.PUBLISHERNAME_TAG.toString() );
166         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.SPANSPEC_TAG.toString() );
167         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.TEXTOBJECT_TAG.toString() );
168         IGNORABLE_ELEMENTS.add( SimplifiedDocbookMarkup.VOLUMENUM_TAG.toString() );
169     }
170 
171     /** {@inheritDoc} */
172     protected void init()
173     {
174         super.init();
175 
176         this.parent.clear();
177         this.trademark = 0;
178         this.level = 0;
179         this.isBold = false;
180         this.inHead = false;
181         this.ignore = false;
182         this.simpleTag = false;
183     }
184 
185     // ----------------------------------------------------------------------
186     //
187     // ----------------------------------------------------------------------
188 
189     /** {@inheritDoc} */
190     protected void handleStartTag( XmlPullParser parser, Sink sink )
191         throws XmlPullParserException, MacroExecutionException
192     {
193         if ( inHead && !META_ELEMENTS.contains( parser.getName() )
194                 && isParent( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
195         {
196             sink.head_();
197             inHead = false;
198 
199             // assume any element that is not meta starts the body
200             sink.body();
201         }
202 
203         final SinkEventAttributeSet attribs = getAttributesFromParser( parser );
204         simpleTag = parser.isEmptyElementTag();
205 
206         if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
207         {
208             handleArticleStart( sink, attribs );
209         }
210         else if ( isParent( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
211         {
212             handleArticleInfoStartTags( parser.getName(), sink, attribs );
213         }
214         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
215         {
216             parent.push( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() );
217         }
218         else if ( parser.getName().equals( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() )
219                 || parser.getName().equals( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
220                 || parser.getName().equals( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
221                 || parser.getName().equals( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() ) )
222         {
223             parent.push( parser.getName() );
224             ignore = true;
225         }
226         else if ( isParent( ( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() ) )
227                 || isParent( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() )
228                 || isParent( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
229                 || isParent( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
230                 || isParent( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
231         {
232             return; // TODO: implement footnotes, entrytbl
233         }
234         else if ( HIER_ELEMENTS.contains( parser.getName() ) )
235         {
236             handleSectionElements( sink, parser.getName(), attribs );
237         }
238         else if ( listStartTags ( parser.getName(), sink, attribs ) )
239         {
240             return;
241         }
242         else if ( mediaStartTag( parser.getName(), sink, attribs ) )
243         {
244             return;
245         }
246         else if ( tableStartTags( parser.getName(), sink, attribs ) )
247         {
248             return;
249         }
250         else if ( parser.getName().equals( SimplifiedDocbookMarkup.PARA_TAG.toString() ) )
251         {
252             handleParaStart( sink, attribs );
253         }
254         else if ( styleStartTags( parser.getName(), sink, attribs ) )
255         {
256             return;
257         }
258         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
259         {
260             handleTitleStart( sink, attribs );
261         }
262         else if ( parser.getName().equals( SimplifiedDocbookMarkup.EMAIL_TAG.toString() ) )
263         {
264             handleEmailStart( parser, sink, attribs );
265         }
266         else if ( linkStartTag( parser.getName(), sink, attribs ) )
267         {
268             return;
269         }
270         else if ( parser.getName().equals( SimplifiedDocbookMarkup.QUOTE_TAG.toString() ) )
271         {
272             sink.text( "\"", null );
273         }
274         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TRADEMARK_TAG.toString() ) )
275         {
276             trademark = '\u2122';
277             final Object trade = attribs.getAttribute( "class" );
278 
279             if ( trade != null )
280             {
281                 trademark = DocbookUtils.trademarkFromClass( trade.toString() );
282             }
283         }
284         else
285         {
286             if ( !ignorable( parser.getName() ) )
287             {
288                 if ( simpleTag )
289                 {
290                     handleUnknown( parser, sink, HtmlMarkup.TAG_TYPE_SIMPLE );
291                 }
292                 else
293                 {
294                     handleUnknown( parser, sink, HtmlMarkup.TAG_TYPE_START );
295                 }
296             }
297         }
298     }
299 
300     /** {@inheritDoc} */
301     protected void handleEndTag( XmlPullParser parser, Sink sink )
302         throws XmlPullParserException, MacroExecutionException
303     {
304         if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
305         {
306             sink.body_();
307         }
308         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
309         {
310             parent.pop();
311         }
312         else if ( isParent( SimplifiedDocbookMarkup.ARTICLEINFO_TAG.toString() ) )
313         {
314              handleArticleInfoEndTags( parser.getName(), sink );
315         }
316         else if ( HIER_ELEMENTS.contains( parser.getName() ) )
317         {
318             sink.section_( level );
319 
320             //decrease the nesting level
321             level--;
322             parent.pop();
323         }
324         else if ( parser.getName().equals( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() )
325                 || parser.getName().equals( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() )
326                 || parser.getName().equals( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
327                 || parser.getName().equals( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
328                 || parser.getName().equals( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
329         {
330             parent.pop();
331             ignore = false;
332         }
333         else if ( isParent( ( SimplifiedDocbookMarkup.FOOTNOTE_TAG.toString() ) )
334                 || isParent( SimplifiedDocbookMarkup.AUDIOOBJECT_TAG.toString() )
335                 || isParent( SimplifiedDocbookMarkup.VIDEOOBJECT_TAG.toString() )
336                 || isParent( SimplifiedDocbookMarkup.SECTIONINFO_TAG.toString() )
337                 || isParent( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
338         {
339             return;
340         }
341         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG.toString() ) )
342         {
343             sink.list_();
344             parent.pop();
345         }
346         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
347         {
348             sink.numberedList_();
349             parent.pop();
350         }
351         else if ( parser.getName().equals( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
352         {
353             parent.pop();
354 
355             if ( isParent( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
356             {
357                 sink.definition_();
358             }
359             else if ( isParent( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
360             {
361                 sink.numberedListItem_();
362             }
363             else
364             {
365                 sink.listItem_();
366             }
367         }
368         else if ( parser.getName().equals( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
369         {
370             sink.definitionList_();
371         }
372         else if ( parser.getName().equals( SimplifiedDocbookMarkup.VARLISTENTRY_TAG.toString() ) )
373         {
374             sink.definitionListItem_();
375         }
376         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TERM_TAG.toString() ) )
377         {
378             sink.definedTerm_();
379         }
380         else if ( parser.getName().equals( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
381         {
382             sink.figure_();
383             parent.pop();
384         }
385         else if ( parser.getName().equals( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG.toString() )
386                 || parser.getName().equals( SimplifiedDocbookMarkup.FIGURE_TAG.toString() )
387                 || parser.getName().equals( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
388                 || parser.getName().equals( SimplifiedDocbookMarkup.TFOOT_TAG.toString() )
389                 || parser.getName().equals( SimplifiedDocbookMarkup.TBODY_TAG.toString() ) )
390         {
391             parent.pop();
392         }
393         else if ( parser.getName().equals( SimplifiedDocbookMarkup.CAPTION_TAG.toString() ) )
394         {
395             handleCaptionEnd(sink);
396         }
397         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
398             || parser.getName().equals( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
399         {
400             sink.table_();
401 
402             parent.pop();
403         }
404         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TR_TAG.toString() )
405                 || parser.getName().equals( SimplifiedDocbookMarkup.ROW_TAG.toString() ) )
406         {
407             sink.tableRow_();
408         }
409         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TGROUP_TAG.toString() ) )
410         {
411             sink.tableRows_();
412         }
413         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() )
414                 && isParent( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
415             || parser.getName().equals( TH_TAG.toString() ) )
416         {
417             sink.tableHeaderCell_();
418         }
419         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() ) )
420         {
421             sink.tableCell_();
422         }
423         else if ( parser.getName().equals( SimplifiedDocbookMarkup.PARA_TAG.toString() ) )
424         {
425             handleParaEnd( sink );
426         }
427         else if ( VERBATIM_ELEMENTS.contains( parser.getName() ) )
428         {
429             sink.verbatim_();
430         }
431         else if ( BOLD_ELEMENTS.contains( parser.getName() )
432             && MONOSPACE_ELEMENTS.contains( parser.getName() ) )
433         {
434             sink.monospaced_();
435             sink.bold_();
436         }
437         else if ( ITALIC_ELEMENTS.contains( parser.getName() )
438             && MONOSPACE_ELEMENTS.contains( parser.getName() ) )
439         {
440             sink.monospaced_();
441             sink.italic_();
442         }
443         else if ( BOLD_ELEMENTS.contains( parser.getName() ) )
444         {
445             sink.bold_();
446         }
447         else if ( ITALIC_ELEMENTS.contains( parser.getName() ) )
448         {
449             if ( isBold )
450             {
451                 sink.bold_();
452 
453                 isBold = false;
454             }
455             else
456             {
457                 sink.italic_();
458             }
459         }
460         else if ( MONOSPACE_ELEMENTS.contains( parser.getName() ) )
461         {
462             sink.monospaced_();
463         }
464         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
465         {
466             handleTitleEnd( sink );
467         }
468         else if ( parser.getName().equals( SimplifiedDocbookMarkup.ULINK_TAG.toString() )
469                 || parser.getName().equals( SimplifiedDocbookMarkup.LINK_TAG.toString() ) )
470         {
471             if ( isParent( parser.getName() ) )
472             {
473                 parent.pop();
474                 sink.link_();
475             }
476         }
477         else if ( parser.getName().equals( SimplifiedDocbookMarkup.QUOTE_TAG.toString() ) )
478         {
479             sink.text( "\"", null );
480         }
481         else if ( parser.getName().equals( SimplifiedDocbookMarkup.TRADEMARK_TAG.toString() ) )
482         {
483             sink.text( Character.toString( trademark ), null );
484         }
485         else if ( !simpleTag && !ignorable( parser.getName() ) )
486         {
487             handleUnknown( parser, sink, HtmlMarkup.TAG_TYPE_END );
488         }
489     }
490 
491     /** {@inheritDoc} */
492     protected void handleComment( XmlPullParser parser, Sink sink )
493         throws XmlPullParserException
494     {
495         final String text = parser.getText();
496 
497         if ( "PB".equals( text.trim() ) )
498         {
499             sink.pageBreak();
500         }
501         else if ( "HR".equals( text.trim() ) )
502         {
503             sink.horizontalRule();
504         }
505         else if ( "LB".equals( text.trim() ) )
506         {
507             sink.lineBreak();
508         }
509         else if ( "anchor_end".equals( text.trim() ) )
510         {
511             sink.anchor_();
512         }
513         else
514         {
515             sink.comment( text.trim() );
516         }
517     }
518 
519     /** {@inheritDoc} */
520     protected void handleCdsect( XmlPullParser parser, Sink sink )
521             throws XmlPullParserException
522     {
523         if ( !ignore )
524         {
525             super.handleCdsect( parser, sink );
526         }
527     }
528 
529     /** {@inheritDoc} */
530     protected void handleEntity( XmlPullParser parser, Sink sink )
531             throws XmlPullParserException
532     {
533         if ( !ignore )
534         {
535             super.handleEntity( parser, sink );
536         }
537     }
538 
539     /** {@inheritDoc} */
540     protected void handleText( XmlPullParser parser, Sink sink )
541             throws XmlPullParserException
542     {
543         if ( !ignore )
544         {
545             super.handleText( parser, sink );
546         }
547     }
548 
549     // ----------------------------------------------------------------------
550     //
551     // ----------------------------------------------------------------------
552 
553     private void handleArticleInfoStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
554     {
555         if ( !ARTICLEINFO_ELEMENTS.contains( name ) )
556         {
557             ignore = true;
558             return; // TODO: other meta data are ignored, implement!
559         }
560 
561         if ( name.equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
562         {
563             sink.title( attribs );
564         }
565         else if ( name.equals( SimplifiedDocbookMarkup.CORPAUTHOR_TAG.toString() ) )
566         {
567             sink.author( attribs );
568         }
569         else if ( name.equals( SimplifiedDocbookMarkup.DATE_TAG.toString() ) )
570         {
571             sink.date( attribs );
572         }
573     }
574 
575     private void handleArticleInfoEndTags( String name, Sink sink )
576     {
577         if ( !ARTICLEINFO_ELEMENTS.contains( name ) )
578         {
579             ignore = false;
580             return; // TODO: other meta data are ignored, implement!
581         }
582 
583         if ( name.equals( SimplifiedDocbookMarkup.TITLE_TAG.toString() ) )
584         {
585             sink.title_();
586         }
587         else if ( name.equals( SimplifiedDocbookMarkup.CORPAUTHOR_TAG.toString() ) )
588         {
589             sink.author_();
590         }
591         else if ( name.equals( SimplifiedDocbookMarkup.DATE_TAG.toString() ) )
592         {
593             sink.date_();
594         }
595     }
596 
597     private void handleCaptionStart( Sink sink, SinkEventAttributeSet attribs )
598     {
599         if ( isParent( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
600         {
601             sink.figureCaption( attribs );
602         }
603         else if ( isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() )
604             || isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() ) )
605         {
606             sink.tableCaption( attribs );
607         }
608 
609         parent.push( SimplifiedDocbookMarkup.CAPTION_TAG.toString() );
610     }
611 
612     private void handleCaptionEnd( Sink sink )
613     {
614         parent.pop();
615 
616         if ( isParent( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
617         {
618             sink.figureCaption_();
619         }
620         else if ( isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() )
621             || isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() ) )
622         {
623             sink.tableCaption_();
624         }
625     }
626 
627     private void handleEmailStart( XmlPullParser parser, Sink sink, SinkEventAttributeSet attribs )
628             throws XmlPullParserException
629     {
630         try
631         {
632             final String mailto = parser.nextText();
633             sink.link( "mailto:" + mailto, attribs );
634             sink.monospaced();
635             sink.text( mailto, null );
636             sink.monospaced_();
637             sink.link_();
638         }
639         catch ( IOException e )
640         {
641             throw new XmlPullParserException( "IOException: " + e.getMessage(), parser, e );
642         }
643     }
644 
645     private void handleFigureStart( Sink sink, SinkEventAttributeSet attribs )
646     {
647         sink.figure( attribs );
648         parent.push( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() );
649     }
650 
651     private void handleArticleStart( Sink sink, SinkEventAttributeSet attribs )
652     {
653         sink.head( attribs );
654         inHead = true;
655 
656         parent.push( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() );
657     }
658 
659     //If the element introduces a new level of hierarchy, raise the stack
660     private void handleSectionElements( Sink sink, String name, SinkEventAttributeSet attribs )
661     {
662         //increase the nesting level
663         level++;
664 
665         sink.section( level, attribs );
666 
667         parent.push( name );
668     }
669 
670     private void handleAnchorStart( Sink sink, SinkEventAttributeSet attribs  )
671     {
672         final Object id = attribs.getAttribute( SimplifiedDocbookMarkup.ID_ATTRIBUTE );
673 
674         if ( id != null )
675         {
676             sink.anchor( id.toString(), attribs );
677         }
678     }
679 
680     private void handleImageDataStart( Sink sink, SinkEventAttributeSet attribs )
681             throws XmlPullParserException
682     {
683         final Object fileref = attribs.getAttribute( SimplifiedDocbookMarkup.FILEREF_ATTRIBUTE );
684 
685         if ( fileref == null )
686         {
687             throw new XmlPullParserException( "Missing fileref attribute in imagedata!" );
688         }
689 
690         sink.figureGraphics( fileref.toString(), attribs );
691     }
692 
693     private void handleItemizedListStart( Sink sink, SinkEventAttributeSet attribs )
694     {
695         sink.list( attribs );
696         //for itemizedlists in variablelists
697         parent.push( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG.toString() );
698     }
699 
700     private void handleLinkStart( Sink sink, SinkEventAttributeSet attribs )
701             throws XmlPullParserException
702     {
703         final Object linkend = attribs.getAttribute( SimplifiedDocbookMarkup.LINKEND_ATTRIBUTE );
704 
705         if ( linkend == null )
706         {
707             throw new XmlPullParserException( "Missing linkend attribute in link!" );
708         }
709 
710         parent.push( SimplifiedDocbookMarkup.LINK_TAG.toString() );
711         sink.link( "#" + linkend.toString(), attribs );
712     }
713 
714     private void handleListItemStart( Sink sink, SinkEventAttributeSet attribs )
715     {
716         if ( isParent( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
717         {
718             sink.definition( attribs );
719         }
720         else if ( isParent( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
721         {
722             sink.numberedListItem( attribs );
723         }
724         else
725         {
726             sink.listItem( attribs );
727         }
728 
729         parent.push( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() );
730     }
731 
732     private void handleOrderedListStart( Sink sink, SinkEventAttributeSet attribs )
733     {
734         //default enumeration style is decimal
735         int numeration = Sink.NUMBERING_DECIMAL;
736 
737         final Object num = attribs.getAttribute( SimplifiedDocbookMarkup.NUMERATION_ATTRIBUTE );
738 
739         if ( num != null )
740         {
741             numeration = DocbookUtils.doxiaListNumbering( num.toString() );
742         }
743 
744         sink.numberedList( numeration, attribs );
745         parent.push( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() );
746     }
747 
748     private void handleParaEnd( Sink sink )
749     {
750         if ( !isParent( SimplifiedDocbookMarkup.CAPTION_TAG.toString() )
751                 && ! isParent( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
752         {
753             sink.paragraph_();
754         }
755     }
756 
757     private void handleParaStart( Sink sink, SinkEventAttributeSet attribs )
758     {
759         if ( !isParent( SimplifiedDocbookMarkup.CAPTION_TAG.toString() )
760                 && ! isParent( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
761         {
762             sink.paragraph( attribs );
763         }
764     }
765 
766     private void handleTableStart( Sink sink, SinkEventAttributeSet attribs )
767     {
768         final Object frame = attribs.getAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE );
769         if ( frame != null )
770         {
771             attribs.addAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE,
772                     DocbookUtils.doxiaTableFrameAttribute( frame.toString() ) );
773         }
774 
775         sink.table( attribs );
776 
777         parent.push( SimplifiedDocbookMarkup.TABLE_TAG.toString() );
778     }
779 
780     private void handleTitleStart( Sink sink, SinkEventAttributeSet attribs )
781     {
782         if ( isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
783                 || isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
784         {
785             sink.tableCaption( attribs );
786         }
787         else if ( isParent( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
788         {
789             sink.title( attribs );
790         }
791         else if ( isParent( SimplifiedDocbookMarkup.SECTION_TAG.toString() ) )
792         {
793             sink.sectionTitle( level, attribs );
794         }
795         else
796         {
797             sink.bold();
798         }
799     }
800 
801     private void handleTitleEnd( Sink sink )
802     {
803         if ( isParent( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
804                 || isParent( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
805         {
806             sink.tableCaption_();
807         }
808         else if ( isParent( SimplifiedDocbookMarkup.SECTION_TAG.toString() ) )
809         {
810             sink.sectionTitle_( level );
811         }
812         else if ( isParent( SimplifiedDocbookMarkup.ARTICLE_TAG.toString() ) )
813         {
814             sink.title_();
815         }
816         else
817         {
818             sink.bold_();
819         }
820     }
821 
822     private void handleUlinkStart( Sink sink, SinkEventAttributeSet attribs )
823             throws XmlPullParserException
824     {
825         final Object url = attribs.getAttribute( SimplifiedDocbookMarkup.URL_ATTRIBUTE );
826 
827         if ( url == null )
828         {
829             throw new XmlPullParserException( "Missing url attribute in ulink!" );
830         }
831 
832         parent.push( SimplifiedDocbookMarkup.ULINK_TAG.toString() );
833         sink.link( url.toString(), attribs );
834     }
835 
836     private void handleVariableListStart( Sink sink, SinkEventAttributeSet attribs )
837     {
838         sink.definitionList( attribs );
839         parent.push( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() );
840     }
841 
842     private void handleXrefStart( Sink sink, SinkEventAttributeSet attribs )
843             throws XmlPullParserException
844     {
845         final Object linkend = attribs.getAttribute( SimplifiedDocbookMarkup.LINKEND_ATTRIBUTE );
846 
847         if ( linkend == null )
848         {
849             throw new XmlPullParserException( "Missing linkend attribute in xref!" );
850         }
851 
852         sink.link( "#" + linkend.toString(), attribs );
853         sink.text( "Link" ); //TODO: determine text of link target
854         sink.link_();
855     }
856 
857     private boolean ignorable( String name )
858     {
859         return IGNORABLE_ELEMENTS.contains( name );
860     }
861 
862     /**
863      * Determines if the given element is a parent element.
864      *
865      * @param element the element to determine.
866      * @return true if the given element is a parent element.
867      */
868     private boolean isParent( String element )
869     {
870         if ( parent.size() > 0 )
871         {
872             return parent.peek().equals( element );
873         }
874 
875         return false;
876     }
877 
878     private boolean linkStartTag( String name, Sink sink, SinkEventAttributeSet attribs )
879             throws XmlPullParserException
880     {
881         if ( name.equals( SimplifiedDocbookMarkup.ULINK_TAG.toString() ) )
882         {
883             handleUlinkStart( sink, attribs );
884         }
885         else if ( name.equals( SimplifiedDocbookMarkup.LINK_TAG.toString() ) )
886         {
887             handleLinkStart( sink, attribs );
888         }
889         else if ( name.equals( SimplifiedDocbookMarkup.XREF_TAG.toString() ) )
890         {
891             handleXrefStart( sink, attribs );
892         }
893         else if ( name.equals( SimplifiedDocbookMarkup.ANCHOR_TAG.toString() ) )
894         {
895             handleAnchorStart( sink, attribs );
896         }
897         else
898         {
899             return false;
900         }
901 
902         return true;
903     }
904 
905     private boolean listStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
906     {
907         if ( name.equals( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG.toString() ) )
908         {
909             handleItemizedListStart( sink, attribs );
910         }
911         else if ( name.equals( SimplifiedDocbookMarkup.ORDEREDLIST_TAG.toString() ) )
912         {
913             handleOrderedListStart( sink, attribs );
914         }
915         else if ( name.equals( SimplifiedDocbookMarkup.LISTITEM_TAG.toString() ) )
916         {
917             handleListItemStart( sink, attribs );
918         }
919         else if ( name.equals( SimplifiedDocbookMarkup.VARIABLELIST_TAG.toString() ) )
920         {
921             handleVariableListStart( sink, attribs );
922         }
923         else if ( name.equals( SimplifiedDocbookMarkup.VARLISTENTRY_TAG.toString() ) )
924         {
925             sink.definitionListItem( attribs );
926         }
927         else if ( name.equals( SimplifiedDocbookMarkup.TERM_TAG.toString() ) )
928         {
929             sink.definedTerm( attribs );
930         }
931         else
932         {
933             return false;
934         }
935 
936         return true;
937     }
938 
939     private boolean mediaStartTag( String name, Sink sink, SinkEventAttributeSet attribs )
940             throws XmlPullParserException
941     {
942         if ( name.equals( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG.toString() ) )
943         {
944             handleFigureStart( sink, attribs );
945         }
946         else if ( name.equals( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG.toString() )
947                 || name.equals( SimplifiedDocbookMarkup.FIGURE_TAG.toString() ) )
948         {
949             parent.push( name );
950         }
951         else if ( name.equals( SimplifiedDocbookMarkup.IMAGEDATA_TAG.toString() ) )
952         {
953             handleImageDataStart( sink, attribs );
954         }
955         else if ( name.equals( SimplifiedDocbookMarkup.CAPTION_TAG.toString() ) )
956         {
957             handleCaptionStart( sink, attribs );
958         }
959         else
960         {
961             return false;
962         }
963 
964         return true;
965     }
966 
967     private boolean styleStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
968     {
969         if ( VERBATIM_ELEMENTS.contains( name ) )
970         {
971             sink.verbatim( SinkEventAttributeSet.BOXED );
972         }
973         else if ( BOLD_ELEMENTS.contains( name ) && MONOSPACE_ELEMENTS.contains( name ) )
974         {
975             sink.bold();
976             sink.monospaced();
977         }
978         else if ( ITALIC_ELEMENTS.contains( name ) && MONOSPACE_ELEMENTS.contains( name ) )
979         {
980             sink.italic();
981             sink.monospaced();
982         }
983         else if ( BOLD_ELEMENTS.contains( name ) )
984         {
985             sink.bold();
986         }
987         else if ( ITALIC_ELEMENTS.contains( name ) && "bold".equals( attribs.getAttribute( "role" ) ) )
988         {
989             sink.bold();
990             isBold = true;
991         }
992         else if ( ITALIC_ELEMENTS.contains( name ) )
993         {
994             sink.italic();
995         }
996         else if ( MONOSPACE_ELEMENTS.contains( name ) )
997         {
998             sink.monospaced();
999         }
1000         else
1001         {
1002             return false;
1003         }
1004 
1005         return true;
1006     }
1007 
1008     private boolean tableStartTags( String name, Sink sink, SinkEventAttributeSet attribs )
1009     {
1010         if ( name.equals( SimplifiedDocbookMarkup.ENTRYTBL_TAG.toString() ) )
1011         {
1012             parent.push( name );
1013             ignore = true;
1014             // insert empty table cell instead
1015             sink.tableCell( (SinkEventAttributeSet) null );
1016             sink.tableCell_();
1017         }
1018         else if ( name.equals( SimplifiedDocbookMarkup.TABLE_TAG.toString() )
1019             || name.equals( SimplifiedDocbookMarkup.INFORMALTABLE_TAG.toString() ) )
1020         {
1021             handleTableStart( sink, attribs );
1022         }
1023         else if ( name.equals( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
1024                 || name.equals( SimplifiedDocbookMarkup.TFOOT_TAG.toString() )
1025                 || name.equals( SimplifiedDocbookMarkup.TBODY_TAG.toString() ) )
1026         {
1027             parent.push( name );
1028         }
1029         else if ( name.equals( SimplifiedDocbookMarkup.TGROUP_TAG.toString() ) )
1030         {
1031             // this is required by the DTD
1032             final int cols = Integer.parseInt( (String) attribs.getAttribute( "cols" ) );
1033             int[] justification = new int[cols];
1034             int justif = Sink.JUSTIFY_LEFT;
1035 
1036             final Object align = attribs.getAttribute( SinkEventAttributeSet.ALIGN );
1037 
1038             if ( align != null )
1039             {
1040                 final String al = align.toString();
1041 
1042                 if ( "right".equals( al ) )
1043                 {
1044                     justif = Sink.JUSTIFY_RIGHT;
1045                 }
1046                 else if ( "center".equals( al ) )
1047                 {
1048                     justif = Sink.JUSTIFY_CENTER;
1049                 }
1050             }
1051 
1052             for ( int i = 0; i < justification.length; i++ )
1053             {
1054                 justification[i] = justif;
1055             }
1056 
1057             boolean grid = false;
1058             final Object rowsep = attribs.getAttribute( "rowsep" );
1059 
1060             if ( rowsep != null && Integer.parseInt( (String) rowsep ) == 1 )
1061             {
1062                 grid = true;
1063             }
1064 
1065             final Object colsep = attribs.getAttribute( "colsep" );
1066 
1067             if ( colsep != null && Integer.parseInt( (String) colsep ) == 1 )
1068             {
1069                 grid = true;
1070             }
1071 
1072             sink.tableRows( justification, grid );
1073         }
1074         else if ( name.equals( SimplifiedDocbookMarkup.TR_TAG.toString() )
1075                 || name.equals( SimplifiedDocbookMarkup.ROW_TAG.toString() ) )
1076         {
1077             sink.tableRow( attribs );
1078         }
1079         else if ( name.equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() )
1080                 && isParent( SimplifiedDocbookMarkup.THEAD_TAG.toString() )
1081                 || name.equals( SimplifiedDocbookMarkup.TH_TAG.toString() ) )
1082         {
1083             sink.tableHeaderCell( attribs );
1084         }
1085         else if ( name.equals( SimplifiedDocbookMarkup.ENTRY_TAG.toString() ) )
1086         {
1087             sink.tableCell( attribs );
1088         }
1089         else
1090         {
1091             return false;
1092         }
1093 
1094         return true;
1095     }
1096 }