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 java.io.Writer;
23  
24  import javax.swing.text.MutableAttributeSet;
25  import javax.swing.text.html.HTML.Attribute;
26  
27  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
28  import org.apache.maven.doxia.sink.impl.XhtmlBaseSink;
29  import org.apache.maven.doxia.util.HtmlTools;
30  
31  import org.codehaus.plexus.util.StringUtils;
32  
33  /**
34   * <a href="http://www.w3.org/TR/xhtml1/">Xhtml 1.0 Transitional</a> sink implementation.
35   * <br>
36   * It uses the DTD/xhtml1-transitional <a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
37   * http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</a>.
38   *
39   * @author Jason van Zyl
40   * @author ltheussl
41   * @since 1.0
42   */
43  public class XhtmlSink
44      extends XhtmlBaseSink
45      implements XhtmlMarkup
46  {
47      // ----------------------------------------------------------------------
48      // Instance fields
49      // ----------------------------------------------------------------------
50  
51      private String encoding;
52  
53      private String languageId;
54  
55      /** An indication on if we're inside a head title. */
56      private boolean headTitleFlag;
57  
58      // ----------------------------------------------------------------------
59      // Constructors
60      // ----------------------------------------------------------------------
61  
62      /**
63       * Constructor, initialize the Writer.
64       *
65       * @param writer not null writer to write the result.
66       */
67      protected XhtmlSink( Writer writer )
68      {
69          super( writer );
70      }
71  
72      /**
73       * Constructor, initialize the Writer and tells which encoding is used.
74       *
75       * @param writer not null writer to write the result.
76       * @param encoding the encoding used, that should be written to the generated HTML content
77       * if not <code>null</code>.
78       */
79      protected XhtmlSink( Writer writer, String encoding )
80      {
81          super( writer );
82  
83          this.encoding = encoding;
84      }
85  
86      /**
87       * Constructor, initialize the Writer and tells which encoding and languageId are used.
88       *
89       * @param writer not null writer to write the result.
90       * @param encoding the encoding used, that should be written to the generated HTML content
91       * if not <code>null</code>.
92       * @param languageId language identifier for the root element as defined by
93       * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
94       * in addition, the empty string may be specified.
95       */
96      protected XhtmlSink( Writer writer, String encoding, String languageId )
97      {
98          this( writer, encoding );
99  
100         this.languageId = languageId;
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     public void head()
107     {
108         init();
109 
110         setHeadFlag( true );
111 
112         write( "<!DOCTYPE html PUBLIC \"" + XHTML_TRANSITIONAL_PUBLIC_ID + "\" \"" + XHTML_TRANSITIONAL_SYSTEM_ID
113             + "\">" );
114 
115         MutableAttributeSet atts = new SinkEventAttributeSet();
116         atts.addAttribute( "xmlns", XHTML_NAMESPACE );
117 
118         if ( languageId != null )
119         {
120             atts.addAttribute( Attribute.LANG.toString(), languageId );
121             atts.addAttribute( "xml:lang", languageId );
122         }
123 
124         writeStartTag( HTML, atts );
125 
126         writeStartTag( HEAD );
127     }
128 
129     /**
130      * {@inheritDoc}
131      */
132     public void head_()
133     {
134         if ( !isHeadTitleFlag() )
135         {
136             // The content of element type "head" must match
137             // "((script|style|meta|link|object|isindex)*,
138             //  ((title,(script|style|meta|link|object|isindex)*,
139             //  (base,(script|style|meta|link|object|isindex)*)?)|(base,(script|style|meta|link|object|isindex)*,
140             //  (title,(script|style|meta|link|object|isindex)*))))"
141             writeStartTag( TITLE );
142             writeEndTag( TITLE );
143         }
144 
145         setHeadFlag( false );
146         setHeadTitleFlag( false );
147 
148         if ( encoding != null )
149         {
150             write( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + encoding + "\"/>" );
151         }
152 
153         writeEndTag( HEAD );
154     }
155 
156     /**
157      * {@inheritDoc}
158      *
159      * @see javax.swing.text.html.HTML.Tag#TITLE
160      */
161     public void title()
162     {
163         setHeadTitleFlag( true );
164 
165         writeStartTag( TITLE );
166     }
167 
168     /**
169      * {@inheritDoc}
170      *
171      * @see javax.swing.text.html.HTML.Tag#TITLE
172      */
173     public void title_()
174     {
175         content( getTextBuffer().toString() );
176 
177         writeEndTag( TITLE );
178 
179         resetTextBuffer();
180 
181     }
182 
183     /**
184      * {@inheritDoc}
185      *
186      * @see javax.swing.text.html.HTML.Tag#META
187      */
188     public void author_()
189     {
190         if ( getTextBuffer().length() > 0 )
191         {
192             MutableAttributeSet att = new SinkEventAttributeSet();
193             att.addAttribute( Attribute.NAME, "author" );
194             String text = HtmlTools.escapeHTML( getTextBuffer().toString() );
195             // hack: un-escape numerical entities that have been escaped above
196             // note that numerical entities should really be added as one unicode character in the first place
197             text = StringUtils.replace( text, "&amp;#", "&#" );
198             att.addAttribute( Attribute.CONTENT, text );
199 
200             writeSimpleTag( META, att );
201 
202             resetTextBuffer();
203         }
204     }
205 
206     /**
207      * {@inheritDoc}
208      *
209      * @see javax.swing.text.html.HTML.Tag#META
210      */
211     public void date_()
212     {
213         if ( getTextBuffer().length() > 0 )
214         {
215             MutableAttributeSet att = new SinkEventAttributeSet();
216             att.addAttribute( Attribute.NAME, "date" );
217             att.addAttribute( Attribute.CONTENT, getTextBuffer().toString() );
218 
219             writeSimpleTag( META, att );
220 
221             resetTextBuffer();
222         }
223     }
224 
225     /**
226      * {@inheritDoc}
227      *
228      * @see javax.swing.text.html.HTML.Tag#BODY
229      */
230     public void body()
231     {
232         writeStartTag( BODY );
233     }
234 
235     /**
236      * {@inheritDoc}
237      *
238      * @see javax.swing.text.html.HTML.Tag#BODY
239      * @see javax.swing.text.html.HTML.Tag#HTML
240      */
241     public void body_()
242     {
243         writeEndTag( BODY );
244 
245         writeEndTag( HTML );
246 
247         flush();
248 
249         init();
250     }
251 
252     // ----------------------------------------------------------------------
253     // Public protected methods
254     // ----------------------------------------------------------------------
255 
256     /**
257      * <p>Setter for the field <code>headTitleFlag</code>.</p>
258      *
259      * @param headTitleFlag an header title flag.
260      * @since 1.1
261      */
262     protected void setHeadTitleFlag( boolean headTitleFlag )
263     {
264         this.headTitleFlag = headTitleFlag;
265     }
266 
267     /**
268      * <p>isHeadTitleFlag.</p>
269      *
270      * @return the current headTitleFlag.
271      * @since 1.1
272      */
273     protected boolean isHeadTitleFlag()
274     {
275         return this.headTitleFlag ;
276     }
277 }