001package org.apache.maven.doxia.module.xhtml5; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.Writer; 023 024import javax.swing.text.MutableAttributeSet; 025import javax.swing.text.html.HTML.Attribute; 026 027import org.apache.maven.doxia.markup.HtmlMarkup; 028import org.apache.maven.doxia.sink.SinkEventAttributes; 029import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; 030import org.apache.maven.doxia.sink.impl.SinkUtils; 031import org.apache.maven.doxia.sink.impl.Xhtml5BaseSink; 032import org.apache.maven.doxia.util.HtmlTools; 033import org.codehaus.plexus.util.StringUtils; 034 035/** 036 * <a href="https://www.w3.org/TR/html52/">XHTML 5.2</a> sink implementation. 037 */ 038public class Xhtml5Sink 039 extends Xhtml5BaseSink 040 implements Xhtml5Markup 041{ 042 // ---------------------------------------------------------------------- 043 // Instance fields 044 // ---------------------------------------------------------------------- 045 046 private String encoding; 047 048 private String languageId; 049 050 /** An indication on if we're inside a head title. */ 051 private boolean headTitleFlag; 052 053 // ---------------------------------------------------------------------- 054 // Constructors 055 // ---------------------------------------------------------------------- 056 057 /** 058 * Constructor, initialize the Writer. 059 * 060 * @param writer not null writer to write the result. 061 */ 062 protected Xhtml5Sink( Writer writer ) 063 { 064 super( writer ); 065 } 066 067 /** 068 * Constructor, initialize the Writer and tells which encoding is used. 069 * 070 * @param writer not null writer to write the result. 071 * @param encoding the encoding used, that should be written to the generated HTML content 072 * if not <code>null</code>. 073 */ 074 protected Xhtml5Sink( Writer writer, String encoding ) 075 { 076 super( writer ); 077 078 this.encoding = encoding; 079 } 080 081 /** 082 * Constructor, initialize the Writer and tells which encoding and languageId are used. 083 * 084 * @param writer not null writer to write the result. 085 * @param encoding the encoding used, that should be written to the generated HTML content 086 * if not <code>null</code>. 087 * @param languageId language identifier for the root element as defined by 088 * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages; 089 * in addition, the empty string may be specified. 090 */ 091 protected Xhtml5Sink( Writer writer, String encoding, String languageId ) 092 { 093 this( writer, encoding ); 094 095 this.languageId = languageId; 096 } 097 098 /** 099 * {@inheritDoc} 100 */ 101 public void head() 102 { 103 init(); 104 105 setHeadFlag( true ); 106 107 write( "<!DOCTYPE html>" ); 108 109 MutableAttributeSet atts = new SinkEventAttributeSet(); 110 atts.addAttribute( "xmlns", XHTML5_NAMESPACE ); 111 112 if ( languageId != null ) 113 { 114 atts.addAttribute( Attribute.LANG.toString(), languageId ); 115 atts.addAttribute( "xml:lang", languageId ); 116 } 117 118 writeStartTag( HTML, atts ); 119 120 writeStartTag( HEAD ); 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 public void head_() 127 { 128 if ( !isHeadTitleFlag() ) 129 { 130 // The content of element type "head" must match 131 // "((script|style|meta|link|object|isindex)*, 132 // ((title,(script|style|meta|link|object|isindex)*, 133 // (base,(script|style|meta|link|object|isindex)*)?)|(base,(script|style|meta|link|object|isindex)*, 134 // (title,(script|style|meta|link|object|isindex)*))))" 135 writeStartTag( TITLE ); 136 writeEndTag( TITLE ); 137 } 138 139 setHeadFlag( false ); 140 setHeadTitleFlag( false ); 141 142 if ( encoding != null ) 143 { 144 write( "<meta charset=\"" + encoding + "\"/>" ); 145 } 146 147 writeEndTag( HEAD ); 148 } 149 150 /** 151 * {@inheritDoc} 152 * 153 * @see javax.swing.text.html.HTML.Tag#TITLE 154 */ 155 public void title() 156 { 157 setHeadTitleFlag( true ); 158 159 writeStartTag( TITLE ); 160 } 161 162 /** 163 * {@inheritDoc} 164 * 165 * @see javax.swing.text.html.HTML.Tag#TITLE 166 */ 167 public void title_() 168 { 169 content( getTextBuffer().toString() ); 170 171 writeEndTag( TITLE ); 172 173 resetTextBuffer(); 174 175 } 176 177 /** 178 * {@inheritDoc} 179 * 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 * 203 * @see javax.swing.text.html.HTML.Tag#META 204 */ 205 public void date_() 206 { 207 if ( getTextBuffer().length() > 0 ) 208 { 209 MutableAttributeSet att = new SinkEventAttributeSet(); 210 att.addAttribute( Attribute.NAME, "date" ); 211 att.addAttribute( Attribute.CONTENT, getTextBuffer().toString() ); 212 213 writeSimpleTag( META, att ); 214 215 resetTextBuffer(); 216 } 217 } 218 219 /** 220 * {@inheritDoc} 221 * 222 * @see javax.swing.text.html.HTML.Tag#BODY 223 */ 224 public void body() 225 { 226 writeStartTag( BODY ); 227 } 228 229 /** 230 * {@inheritDoc} 231 * 232 * @see javax.swing.text.html.HTML.Tag#BODY 233 * @see javax.swing.text.html.HTML.Tag#HTML 234 */ 235 public void body_() 236 { 237 writeEndTag( BODY ); 238 239 writeEndTag( HTML ); 240 241 flush(); 242 243 init(); 244 } 245 246 /** 247 * {@inheritDoc} 248 * 249 * Starts a section.. 250 * @see javax.swing.text.html.HTML.Tag#DIV 251 */ 252 protected void onSection( int depth, SinkEventAttributes attributes ) 253 { 254 if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 ) 255 { 256 MutableAttributeSet att = new SinkEventAttributeSet(); 257 att.addAttributes( SinkUtils.filterAttributes( 258 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) ); 259 260 writeStartTag( HtmlMarkup.SECTION, att ); 261 } 262 } 263 264 /** 265 * {@inheritDoc} 266 * 267 * Ends a section. 268 * @see javax.swing.text.html.HTML.Tag#DIV 269 */ 270 protected void onSection_( int depth ) 271 { 272 if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 ) 273 { 274 writeEndTag( HtmlMarkup.SECTION ); 275 } 276 } 277 278 /** 279 * {@inheritDoc} 280 * 281 * Starts a section title. 282 * @see javax.swing.text.html.HTML.Tag#H2 283 * @see javax.swing.text.html.HTML.Tag#H3 284 * @see javax.swing.text.html.HTML.Tag#H4 285 * @see javax.swing.text.html.HTML.Tag#H5 286 * @see javax.swing.text.html.HTML.Tag#H6 287 */ 288 protected void onSectionTitle( int depth, SinkEventAttributes attributes ) 289 { 290 MutableAttributeSet atts = SinkUtils.filterAttributes( 291 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 292 293 if ( depth == SECTION_LEVEL_1 ) 294 { 295 writeStartTag( HtmlMarkup.H2, atts ); 296 } 297 else if ( depth == SECTION_LEVEL_2 ) 298 { 299 writeStartTag( HtmlMarkup.H3, atts ); 300 } 301 else if ( depth == SECTION_LEVEL_3 ) 302 { 303 writeStartTag( HtmlMarkup.H4, atts ); 304 } 305 else if ( depth == SECTION_LEVEL_4 ) 306 { 307 writeStartTag( HtmlMarkup.H5, atts ); 308 } 309 else if ( depth == SECTION_LEVEL_5 ) 310 { 311 writeStartTag( HtmlMarkup.H6, atts ); 312 } 313 } 314 315 /** 316 * {@inheritDoc} 317 * 318 * Ends a section title. 319 * @see javax.swing.text.html.HTML.Tag#H2 320 * @see javax.swing.text.html.HTML.Tag#H3 321 * @see javax.swing.text.html.HTML.Tag#H4 322 * @see javax.swing.text.html.HTML.Tag#H5 323 * @see javax.swing.text.html.HTML.Tag#H6 324 */ 325 protected void onSectionTitle_( int depth ) 326 { 327 if ( depth == SECTION_LEVEL_1 ) 328 { 329 writeEndTag( HtmlMarkup.H2 ); 330 } 331 else if ( depth == SECTION_LEVEL_2 ) 332 { 333 writeEndTag( HtmlMarkup.H3 ); 334 } 335 else if ( depth == SECTION_LEVEL_3 ) 336 { 337 writeEndTag( HtmlMarkup.H4 ); 338 } 339 else if ( depth == SECTION_LEVEL_4 ) 340 { 341 writeEndTag( HtmlMarkup.H5 ); 342 } 343 else if ( depth == SECTION_LEVEL_5 ) 344 { 345 writeEndTag( HtmlMarkup.H6 ); 346 } 347 } 348 349 // ---------------------------------------------------------------------- 350 // Public protected methods 351 // ---------------------------------------------------------------------- 352 353 /** 354 * <p>Setter for the field <code>headTitleFlag</code>.</p> 355 * 356 * @param headTitleFlag an header title flag. 357 * @since 1.1 358 */ 359 protected void setHeadTitleFlag( boolean headTitleFlag ) 360 { 361 this.headTitleFlag = headTitleFlag; 362 } 363 364 /** 365 * <p>isHeadTitleFlag.</p> 366 * 367 * @return the current headTitleFlag. 368 * @since 1.1 369 */ 370 protected boolean isHeadTitleFlag() 371 { 372 return this.headTitleFlag ; 373 } 374}