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, "&#", "&#" );
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 }