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