001package org.apache.maven.doxia.module.xdoc; 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.sink.SinkEventAttributeSet; 028import org.apache.maven.doxia.sink.SinkEventAttributes; 029import org.apache.maven.doxia.sink.SinkUtils; 030import org.apache.maven.doxia.sink.XhtmlBaseSink; 031import org.apache.maven.doxia.util.HtmlTools; 032 033import org.codehaus.plexus.util.StringUtils; 034 035/** 036 * <a href="http://maven.apache.org/doxia/references/xdoc-format.html">Xdoc</a> Sink implementation. 037 * <br/> 038 * It uses the Xdoc XSD <a href="http://maven.apache.org/xsd/xdoc-2.0.xsd"> 039 * http://maven.apache.org/xsd/xdoc-2.0.xsd</a>. 040 * 041 * @author <a href="mailto:james@jamestaylor.org">James Taylor</a> 042 * @version $Id$ 043 * @since 1.0 044 */ 045public class XdocSink 046 extends XhtmlBaseSink 047 implements XdocMarkup 048{ 049 // ---------------------------------------------------------------------- 050 // Instance fields 051 // ---------------------------------------------------------------------- 052 053 /** An indication on if we're inside a box (verbatim). */ 054 private boolean boxedFlag; 055 056 private String encoding; 057 058 private String languageId; 059 060 // ---------------------------------------------------------------------- 061 // Constructors 062 // ---------------------------------------------------------------------- 063 064 /** 065 * Constructor, initialize the Writer. 066 * 067 * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer. 068 * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}. 069 */ 070 protected XdocSink( Writer writer ) 071 { 072 super( writer ); 073 } 074 075 /** 076 * Constructor, initialize the Writer and tells which encoding is used. 077 * 078 * @param writer not null writer to write the result. 079 * @param encoding the encoding used, that should be written to the generated HTML content 080 * if not <code>null</code>. 081 * @since 1.1 082 */ 083 protected XdocSink( Writer writer, String encoding ) 084 { 085 this( writer ); 086 this.encoding = encoding; 087 } 088 089 /** 090 * Constructor, initialize the Writer and tells which encoding and languageId are used. 091 * 092 * @param writer not null writer to write the result. 093 * @param encoding the encoding used, that should be written to the generated HTML content 094 * if not <code>null</code>. 095 * @param languageId language identifier for the root element as defined by 096 * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages; 097 * in addition, the empty string may be specified. 098 * @since 1.1 099 */ 100 protected XdocSink( Writer writer, String encoding, String languageId ) 101 { 102 this( writer, encoding ); 103 104 this.languageId = languageId; 105 } 106 107 // ---------------------------------------------------------------------- 108 // Public protected methods 109 // ---------------------------------------------------------------------- 110 111 /** {@inheritDoc} */ 112 protected void init() 113 { 114 super.init(); 115 116 boxedFlag = false; 117 } 118 119 /** 120 * {@inheritDoc} 121 * @see #head(org.apache.maven.doxia.sink.SinkEventAttributes) 122 */ 123 public void head() 124 { 125 head( null ); 126 } 127 128 /** 129 * {@inheritDoc} 130 * @see XdocMarkup#DOCUMENT_TAG 131 * @see XdocMarkup#PROPERTIES_TAG 132 */ 133 public void head( SinkEventAttributes attributes ) 134 { 135 init(); 136 137 setHeadFlag( true ); 138 139 write( "<?xml version=\"1.0\"" ); 140 if ( encoding != null ) 141 { 142 write( " encoding=\"" + encoding + "\"" ); 143 } 144 write( "?>" ); 145 146 MutableAttributeSet atts = new SinkEventAttributeSet(); 147 atts.addAttribute( "xmlns", XDOC_NAMESPACE ); 148 atts.addAttribute( "xmlns:xsi", XML_NAMESPACE ); 149 atts.addAttribute( "xsi:schemaLocation", XDOC_NAMESPACE + " " + XDOC_SYSTEM_ID ); 150 151 if ( languageId != null ) 152 { 153 atts.addAttribute( Attribute.LANG.toString(), languageId ); 154 atts.addAttribute( "xml:lang", languageId ); 155 } 156 157 if ( attributes != null ) 158 { 159 atts.addAttributes( attributes ); 160 } 161 162 writeStartTag( DOCUMENT_TAG, atts ); 163 164 writeStartTag( PROPERTIES_TAG ); 165 } 166 167 /** 168 * {@inheritDoc} 169 * @see XdocMarkup#DOCUMENT_TAG 170 * @see XdocMarkup#PROPERTIES_TAG 171 */ 172 public void head_() 173 { 174 setHeadFlag( false ); 175 176 writeEndTag( PROPERTIES_TAG ); 177 } 178 179 /** 180 * {@inheritDoc} 181 * @see javax.swing.text.html.HTML.Tag#TITLE 182 */ 183 public void title() 184 { 185 writeStartTag( TITLE ); 186 } 187 188 /** 189 * {@inheritDoc} 190 * @see javax.swing.text.html.HTML.Tag#TITLE 191 */ 192 public void title_() 193 { 194 content( getTextBuffer().toString() ); 195 196 writeEndTag( TITLE ); 197 198 resetTextBuffer(); 199 } 200 201 /** 202 * {@inheritDoc} 203 * @see XdocMarkup#AUTHOR_TAG 204 */ 205 public void author_() 206 { 207 if ( getTextBuffer().length() > 0 ) 208 { 209 writeStartTag( AUTHOR_TAG ); 210 String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); 211 // hack: un-escape numerical entities that have been escaped above 212 // note that numerical entities should really be written as one unicode character in the first place 213 text = StringUtils.replace( text, "&#", "&#" ); 214 write( text ); 215 writeEndTag( AUTHOR_TAG ); 216 resetTextBuffer(); 217 } 218 } 219 220 /** 221 * {@inheritDoc} 222 * @see XdocMarkup#DATE_TAG 223 */ 224 public void date_() 225 { 226 if ( getTextBuffer().length() > 0 ) 227 { 228 writeStartTag( DATE_TAG ); 229 content( getTextBuffer().toString() ); 230 writeEndTag( DATE_TAG ); 231 resetTextBuffer(); 232 } 233 } 234 235 /** 236 * {@inheritDoc} 237 * @see #body(org.apache.maven.doxia.sink.SinkEventAttributes) 238 */ 239 public void body() 240 { 241 body( null ); 242 } 243 244 /** 245 * {@inheritDoc} 246 * @see javax.swing.text.html.HTML.Tag#BODY 247 */ 248 public void body( SinkEventAttributes attributes ) 249 { 250 writeStartTag( BODY, attributes ); 251 } 252 253 /** 254 * {@inheritDoc} 255 * @see javax.swing.text.html.HTML.Tag#BODY 256 * @see XdocMarkup#DOCUMENT_TAG 257 */ 258 public void body_() 259 { 260 writeEndTag( BODY ); 261 262 writeEndTag( DOCUMENT_TAG ); 263 264 flush(); 265 266 init(); 267 } 268 269 // ---------------------------------------------------------------------- 270 // Sections 271 // ---------------------------------------------------------------------- 272 273 /** 274 * {@inheritDoc} 275 * 276 * Starts a section. 277 * @see XdocMarkup#SECTION_TAG 278 * @see XdocMarkup#SUBSECTION_TAG 279 */ 280 protected void onSection( int depth, SinkEventAttributes attributes ) 281 { 282 if ( depth == SECTION_LEVEL_1 ) 283 { 284 write( String.valueOf( LESS_THAN ) + SECTION_TAG.toString() 285 + SinkUtils.getAttributeString( 286 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) ) 287 + String.valueOf( SPACE ) + Attribute.NAME + String.valueOf( EQUAL ) + String.valueOf( QUOTE ) ); 288 } 289 else if ( depth == SECTION_LEVEL_2 ) 290 { 291 write( String.valueOf( LESS_THAN ) + SUBSECTION_TAG.toString() 292 + SinkUtils.getAttributeString( 293 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) ) 294 + String.valueOf( SPACE ) + Attribute.NAME + String.valueOf( EQUAL ) + String.valueOf( QUOTE ) ); 295 } 296 } 297 298 /** 299 * {@inheritDoc} 300 * 301 * Ends a section. 302 * @see XdocMarkup#SECTION_TAG 303 * @see XdocMarkup#SUBSECTION_TAG 304 */ 305 protected void onSection_( int depth ) 306 { 307 if ( depth == SECTION_LEVEL_1 ) 308 { 309 writeEndTag( SECTION_TAG ); 310 } 311 else if ( depth == SECTION_LEVEL_2 ) 312 { 313 writeEndTag( SUBSECTION_TAG ); 314 } 315 } 316 317 /** 318 * {@inheritDoc} 319 * 320 * Starts a section title. 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, SinkEventAttributes attributes ) 326 { 327 MutableAttributeSet atts = SinkUtils.filterAttributes( 328 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 329 330 if ( depth == SECTION_LEVEL_3 ) 331 { 332 writeStartTag( H4, atts ); 333 } 334 else if ( depth == SECTION_LEVEL_4 ) 335 { 336 writeStartTag( H5, atts ); 337 } 338 else if ( depth == SECTION_LEVEL_5 ) 339 { 340 writeStartTag( H6, atts ); 341 } 342 } 343 344 /** 345 * {@inheritDoc} 346 * 347 * Ends a section title. 348 * @see javax.swing.text.html.HTML.Tag#H4 349 * @see javax.swing.text.html.HTML.Tag#H5 350 * @see javax.swing.text.html.HTML.Tag#H6 351 */ 352 protected void onSectionTitle_( int depth ) 353 { 354 if ( depth == SECTION_LEVEL_1 || depth == SECTION_LEVEL_2 ) 355 { 356 write( String.valueOf( QUOTE ) + String.valueOf( GREATER_THAN ) ); 357 } 358 else if ( depth == SECTION_LEVEL_3 ) 359 { 360 writeEndTag( H4 ); 361 } 362 else if ( depth == SECTION_LEVEL_4 ) 363 { 364 writeEndTag( H5 ); 365 } 366 else if ( depth == SECTION_LEVEL_5 ) 367 { 368 writeEndTag( H6 ); 369 } 370 } 371 372 // ----------------------------------------------------------------------- 373 // 374 // ----------------------------------------------------------------------- 375 376 /** 377 * {@inheritDoc} 378 * @see XdocMarkup#SOURCE_TAG 379 * @see javax.swing.text.html.HTML.Tag#PRE 380 */ 381 public void verbatim( SinkEventAttributes attributes ) 382 { 383 setVerbatimFlag( true ); 384 385 MutableAttributeSet atts = SinkUtils.filterAttributes( 386 attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES ); 387 388 389 if ( atts == null ) 390 { 391 atts = new SinkEventAttributeSet(); 392 } 393 394 boolean boxed = false; 395 396 if ( atts.isDefined( SinkEventAttributes.DECORATION ) ) 397 { 398 boxed = "boxed".equals( 399 (String) atts.getAttribute( SinkEventAttributes.DECORATION ) ); 400 } 401 402 boxedFlag = boxed; 403 atts.removeAttribute( SinkEventAttributes.DECORATION ); 404 405 if ( boxed ) 406 { 407 writeStartTag( SOURCE_TAG, atts ); 408 } 409 else 410 { 411 atts.removeAttribute( Attribute.ALIGN.toString() ); 412 writeStartTag( PRE, atts ); 413 } 414 } 415 416 /** 417 * {@inheritDoc} 418 * @see XdocMarkup#SOURCE_TAG 419 * @see javax.swing.text.html.HTML.Tag#PRE 420 */ 421 public void verbatim_() 422 { 423 if ( boxedFlag ) 424 { 425 writeEndTag( SOURCE_TAG ); 426 } 427 else 428 { 429 writeEndTag( PRE ); 430 } 431 432 setVerbatimFlag( false ); 433 434 boxedFlag = false; 435 } 436 437 /** 438 * The default align is <code>center</code>. 439 * 440 * {@inheritDoc} 441 * @see javax.swing.text.html.HTML.Tag#TABLE 442 */ 443 public void tableRows( int[] justification, boolean grid ) 444 { 445 // similar to super.tableRows( justification, grid ) but without class. 446 447 this.tableRows = true; 448 449 setCellJustif( justification ); 450 451 if ( this.tableAttributes == null ) 452 { 453 this.tableAttributes = new SinkEventAttributeSet( 0 ); 454 } 455 456 MutableAttributeSet att = new SinkEventAttributeSet(); 457 458 if ( !tableAttributes.isDefined( Attribute.BORDER.toString() ) ) 459 { 460 att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) ); 461 } 462 463 att.addAttributes( tableAttributes ); 464 465 tableAttributes.removeAttributes( tableAttributes ); 466 467 writeStartTag( TABLE, att ); 468 } 469 470 /** 471 * The default valign is <code>top</code>. 472 * 473 * {@inheritDoc} 474 * @see javax.swing.text.html.HTML.Tag#TR 475 */ 476 public void tableRow() 477 { 478 MutableAttributeSet att = new SinkEventAttributeSet(); 479 att.addAttribute( Attribute.VALIGN, "top" ); 480 481 writeStartTag( TR, att ); 482 483 setCellCount( 0 ); 484 } 485 486 public void close() 487 { 488 super.close(); 489 490 init(); 491 } 492 493 /** 494 * Adds a link with an optional target. 495 * 496 * @param name the link name. 497 * @param target the link target, may be null. 498 */ 499 public void link( String name, String target ) 500 { 501 if ( isHeadFlag() ) 502 { 503 return; 504 } 505 506 MutableAttributeSet att = new SinkEventAttributeSet(); 507 508 att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( name ) ); 509 510 if ( target != null ) 511 { 512 att.addAttribute( Attribute.TARGET, target ); 513 } 514 515 writeStartTag( A, att ); 516 } 517 518 // ---------------------------------------------------------------------- 519 // 520 // ---------------------------------------------------------------------- 521 522 /** 523 * Write text to output, preserving white space. 524 * 525 * @param text The text to write. 526 * @deprecated use write(String) 527 */ 528 protected void markup( String text ) 529 { 530 write( text ); 531 } 532}