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