View Javadoc
1   package org.apache.maven.doxia.module.xhtml;
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 javax.swing.text.html.HTML.Attribute;
23  
24  import org.apache.maven.doxia.macro.MacroExecutionException;
25  import org.apache.maven.doxia.parser.Parser;
26  import org.apache.maven.doxia.parser.XhtmlBaseParser;
27  import org.apache.maven.doxia.sink.Sink;
28  import org.apache.maven.doxia.sink.SinkEventAttributeSet;
29  
30  import org.codehaus.plexus.component.annotations.Component;
31  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
32  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
33  
34  /**
35   * Parse an xhtml model and emit events into a Doxia Sink.
36   *
37   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
38   * @version $Id: XhtmlParser.java 1572532 2014-02-27 12:21:40Z stephenc $
39   * @since 1.0
40   */
41  @Component( role = Parser.class, hint = "xhtml" )
42  public class XhtmlParser
43      extends XhtmlBaseParser
44      implements XhtmlMarkup
45  {
46      /** For boxed verbatim. */
47      private boolean boxed;
48  
49      /** Empty elements don't write a closing tag. */
50      private boolean isEmptyElement;
51  
52      /** {@inheritDoc} */
53      protected void handleStartTag( XmlPullParser parser, Sink sink )
54          throws XmlPullParserException, MacroExecutionException
55      {
56          isEmptyElement = parser.isEmptyElementTag();
57  
58          SinkEventAttributeSet attribs = getAttributesFromParser( parser );
59  
60          if ( parser.getName().equals( HTML.toString() ) )
61          {
62              //Do nothing
63              return;
64          }
65          else if ( parser.getName().equals( HEAD.toString() ) )
66          {
67              sink.head( attribs );
68          }
69          else if ( parser.getName().equals( TITLE.toString() ) )
70          {
71              sink.title( attribs );
72          }
73          else if ( parser.getName().equals( META.toString() ) )
74          {
75              String name = parser.getAttributeValue( null, Attribute.NAME.toString() );
76              String content = parser.getAttributeValue( null, Attribute.CONTENT.toString() );
77  
78              if ( "author".equals( name ) )
79              {
80                  sink.author( null );
81  
82                  sink.text( content );
83  
84                  sink.author_();
85              }
86              else if ( "date".equals( name ) )
87              {
88                  sink.date( null );
89  
90                  sink.text( content );
91  
92                  sink.date_();
93              }
94              else
95              {
96                  sink.unknown( "meta", new Object[] { Integer.valueOf( TAG_TYPE_SIMPLE ) }, attribs );
97              }
98          }
99          /*
100          * The ADDRESS element may be used by authors to supply contact information
101          * for a model or a major part of a model such as a form. This element
102          *  often appears at the beginning or end of a model.
103          */
104         else if ( parser.getName().equals( ADDRESS.toString() ) )
105         {
106             sink.author( attribs );
107         }
108         else if ( parser.getName().equals( BODY.toString() ) )
109         {
110             sink.body( attribs );
111         }
112         else if ( parser.getName().equals( DIV.toString() ) )
113         {
114             String divclass = parser.getAttributeValue( null, Attribute.CLASS.toString() );
115 
116             if ( "source".equals( divclass ) )
117             {
118                 this.boxed = true;
119             }
120 
121             baseStartTag( parser, sink ); // pick up other divs
122         }
123         /*
124          * The PRE element tells visual user agents that the enclosed text is
125          * "preformatted". When handling preformatted text, visual user agents:
126          * - May leave white space intact.
127          * - May render text with a fixed-pitch font.
128          * - May disable automatic word wrap.
129          * - Must not disable bidirectional processing.
130          * Non-visual user agents are not required to respect extra white space
131          * in the content of a PRE element.
132          */
133         else if ( parser.getName().equals( PRE.toString() ) )
134         {
135             if ( boxed )
136             {
137                 attribs.addAttributes( SinkEventAttributeSet.BOXED );
138             }
139 
140             verbatim();
141 
142             sink.verbatim( attribs );
143         }
144         else if ( !baseStartTag( parser, sink ) )
145         {
146             if ( isEmptyElement )
147             {
148                 handleUnknown( parser, sink, TAG_TYPE_SIMPLE );
149             }
150             else
151             {
152                 handleUnknown( parser, sink, TAG_TYPE_START );
153             }
154 
155             if ( getLog().isDebugEnabled() )
156             {
157                 String position = "[" + parser.getLineNumber() + ":"
158                     + parser.getColumnNumber() + "]";
159                 String tag = "<" + parser.getName() + ">";
160 
161                 getLog().debug( "Unrecognized xhtml tag: " + tag + " at " + position );
162             }
163         }
164     }
165 
166     /** {@inheritDoc} */
167     protected void handleEndTag( XmlPullParser parser, Sink sink )
168         throws XmlPullParserException, MacroExecutionException
169     {
170         if ( parser.getName().equals( HTML.toString() ) )
171         {
172             //Do nothing
173             return;
174         }
175         else if ( parser.getName().equals( HEAD.toString() ) )
176         {
177             sink.head_();
178         }
179         else if ( parser.getName().equals( TITLE.toString() ) )
180         {
181             sink.title_();
182         }
183         else if ( parser.getName().equals( BODY.toString() ) )
184         {
185             consecutiveSections( 0, sink );
186 
187             sink.body_();
188         }
189         else if ( parser.getName().equals( ADDRESS.toString() ) )
190         {
191             sink.author_();
192         }
193         else if ( parser.getName().equals( DIV.toString() ) )
194         {
195             this.boxed = false;
196             baseEndTag( parser, sink );
197         }
198         else if ( !baseEndTag( parser, sink ) )
199         {
200             if ( !isEmptyElement )
201             {
202                 handleUnknown( parser, sink, TAG_TYPE_END );
203             }
204         }
205 
206         isEmptyElement = false;
207     }
208 
209     /** {@inheritDoc} */
210     protected void init()
211     {
212         super.init();
213 
214         this.boxed = false;
215         this.isEmptyElement = false;
216     }
217 }