001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.doxia.module.xdoc; 020 021import javax.swing.text.MutableAttributeSet; 022import javax.swing.text.html.HTML.Attribute; 023 024import java.io.Writer; 025 026import org.apache.commons.lang3.StringUtils; 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.Xhtml5BaseSink; 031import org.apache.maven.doxia.util.HtmlTools; 032 033/** 034 * <a href="https://maven.apache.org/doxia/references/xdoc-format.html">Xdoc</a> Sink implementation. 035 * <br> 036 * It uses the Xdoc XSD <a href="https://maven.apache.org/xsd/xdoc-2.0.xsd"> 037 * https://maven.apache.org/xsd/xdoc-2.0.xsd</a>. 038 * 039 * @author <a href="mailto:james@jamestaylor.org">James Taylor</a> 040 * @since 1.0 041 */ 042public class XdocSink extends Xhtml5BaseSink implements XdocMarkup { 043 // ---------------------------------------------------------------------- 044 // Instance fields 045 // ---------------------------------------------------------------------- 046 047 private String encoding; 048 049 private String languageId; 050 051 // ---------------------------------------------------------------------- 052 // Constructors 053 // ---------------------------------------------------------------------- 054 055 /** 056 * Constructor, initialize the Writer. 057 * 058 * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer. 059 * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}. 060 */ 061 protected XdocSink(Writer writer) { 062 super(writer); 063 } 064 065 /** 066 * Constructor, initialize the Writer and tells which encoding is used. 067 * 068 * @param writer not null writer to write the result. 069 * @param encoding the encoding used, that should be written to the generated HTML content 070 * if not <code>null</code>. 071 * @since 1.1 072 */ 073 protected XdocSink(Writer writer, String encoding) { 074 this(writer); 075 this.encoding = encoding; 076 } 077 078 /** 079 * Constructor, initialize the Writer and tells which encoding and languageId are used. 080 * 081 * @param writer not null writer to write the result. 082 * @param encoding the encoding used, that should be written to the generated HTML content 083 * if not <code>null</code>. 084 * @param languageId language identifier for the root element as defined by 085 * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages; 086 * in addition, the empty string may be specified. 087 * @since 1.1 088 */ 089 protected XdocSink(Writer writer, String encoding, String languageId) { 090 this(writer, encoding); 091 092 this.languageId = languageId; 093 } 094 095 // ---------------------------------------------------------------------- 096 // Public protected methods 097 // ---------------------------------------------------------------------- 098 099 /** 100 * {@inheritDoc} 101 */ 102 protected void init() { 103 super.init(); 104 } 105 106 /** 107 * {@inheritDoc} 108 * @see XdocMarkup#DOCUMENT_TAG 109 * @see XdocMarkup#PROPERTIES_TAG 110 */ 111 public void head(SinkEventAttributes attributes) { 112 init(); 113 114 setHeadFlag(true); 115 116 write("<?xml version=\"1.0\""); 117 if (encoding != null) { 118 write(" encoding=\"" + encoding + "\""); 119 } 120 write("?>"); 121 122 MutableAttributeSet atts = new SinkEventAttributeSet(); 123 atts.addAttribute("xmlns", XDOC_NAMESPACE); 124 atts.addAttribute("xmlns:xsi", XML_NAMESPACE); 125 atts.addAttribute("xsi:schemaLocation", XDOC_NAMESPACE + " " + XDOC_SYSTEM_ID); 126 127 if (languageId != null) { 128 atts.addAttribute(Attribute.LANG.toString(), languageId); 129 atts.addAttribute("xml:lang", languageId); 130 } 131 132 if (attributes != null) { 133 atts.addAttributes(attributes); 134 } 135 136 writeStartTag(DOCUMENT_TAG, atts); 137 138 writeStartTag(PROPERTIES_TAG); 139 } 140 141 /** 142 * {@inheritDoc} 143 * 144 * @see XdocMarkup#DOCUMENT_TAG 145 * @see XdocMarkup#PROPERTIES_TAG 146 */ 147 public void head_() { 148 setHeadFlag(false); 149 150 writeEndTag(PROPERTIES_TAG); 151 } 152 153 /** 154 * {@inheritDoc} 155 * 156 * @see javax.swing.text.html.HTML.Tag#TITLE 157 */ 158 @Override 159 public void title(SinkEventAttributes attributes) { 160 writeStartTag(TITLE); 161 } 162 163 /** 164 * {@inheritDoc} 165 * 166 * @see javax.swing.text.html.HTML.Tag#TITLE 167 */ 168 public void title_() { 169 content(getTextBuffer().toString()); 170 171 writeEndTag(TITLE); 172 173 resetTextBuffer(); 174 } 175 176 /** 177 * {@inheritDoc} 178 * 179 * @see XdocMarkup#AUTHOR_TAG 180 */ 181 public void author_() { 182 if (getTextBuffer().length() > 0) { 183 writeStartTag(AUTHOR_TAG); 184 String text = HtmlTools.escapeHTML(getTextBuffer().toString()); 185 // hack: un-escape numerical entities that have been escaped above 186 // note that numerical entities should really be written as one unicode character in the first place 187 text = StringUtils.replace(text, "&#", "&#"); 188 write(text); 189 writeEndTag(AUTHOR_TAG); 190 resetTextBuffer(); 191 } 192 } 193 194 /** 195 * {@inheritDoc} 196 * 197 * @see XdocMarkup#DATE_TAG 198 */ 199 public void date_() { 200 if (getTextBuffer().length() > 0) { 201 writeStartTag(DATE_TAG); 202 content(getTextBuffer().toString()); 203 writeEndTag(DATE_TAG); 204 resetTextBuffer(); 205 } 206 } 207 208 /** 209 * {@inheritDoc} 210 * @see javax.swing.text.html.HTML.Tag#BODY 211 */ 212 public void body(SinkEventAttributes attributes) { 213 writeStartTag(BODY, attributes); 214 } 215 216 /** 217 * {@inheritDoc} 218 * 219 * @see javax.swing.text.html.HTML.Tag#BODY 220 * @see XdocMarkup#DOCUMENT_TAG 221 */ 222 public void body_() { 223 writeEndTag(BODY); 224 225 writeEndTag(DOCUMENT_TAG); 226 227 flush(); 228 229 init(); 230 } 231 232 // ---------------------------------------------------------------------- 233 // Sections 234 // ---------------------------------------------------------------------- 235 236 /** 237 * {@inheritDoc} 238 * 239 * Starts a section. 240 * @see XdocMarkup#SECTION_TAG 241 * @see XdocMarkup#SUBSECTION_TAG 242 */ 243 protected void onSection(int depth, SinkEventAttributes attributes) { 244 if (depth == SECTION_LEVEL_1) { 245 write(LESS_THAN 246 + SECTION_TAG.toString() 247 + SinkUtils.getAttributeString( 248 SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES)) 249 + SPACE 250 + Attribute.NAME 251 + EQUAL 252 + QUOTE); 253 } else if (depth == SECTION_LEVEL_2) { 254 write(LESS_THAN 255 + SUBSECTION_TAG.toString() 256 + SinkUtils.getAttributeString( 257 SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES)) 258 + SPACE 259 + Attribute.NAME 260 + EQUAL 261 + QUOTE); 262 } 263 } 264 265 /** 266 * {@inheritDoc} 267 * 268 * Ends a section. 269 * @see XdocMarkup#SECTION_TAG 270 * @see XdocMarkup#SUBSECTION_TAG 271 */ 272 protected void onSection_(int depth) { 273 if (depth == SECTION_LEVEL_1) { 274 writeEndTag(SECTION_TAG); 275 } else if (depth == SECTION_LEVEL_2) { 276 writeEndTag(SUBSECTION_TAG); 277 } 278 } 279 280 /** 281 * {@inheritDoc} 282 * 283 * Starts a section title. 284 * @see #H3 285 * @see #H4 286 * @see #H5 287 * @see #H6 288 */ 289 protected void onSectionTitle(int depth, SinkEventAttributes attributes) { 290 MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES); 291 292 if (depth == SECTION_LEVEL_3) { 293 writeStartTag(H3, atts); 294 } else if (depth == SECTION_LEVEL_4) { 295 writeStartTag(H4, atts); 296 } else if (depth == SECTION_LEVEL_5) { 297 writeStartTag(H5, atts); 298 } else if (depth == SECTION_LEVEL_6) { 299 writeStartTag(H6, atts); 300 } 301 } 302 303 /** 304 * {@inheritDoc} 305 * 306 * Ends a section title. 307 * @see #H3 308 * @see #H4 309 * @see #H5 310 * @see #H6 311 */ 312 protected void onSectionTitle_(int depth) { 313 if (depth == SECTION_LEVEL_1 || depth == SECTION_LEVEL_2) { 314 write(String.valueOf(QUOTE) + GREATER_THAN); 315 } else if (depth == SECTION_LEVEL_3) { 316 writeEndTag(H3); 317 } else if (depth == SECTION_LEVEL_4) { 318 writeEndTag(H4); 319 } else if (depth == SECTION_LEVEL_5) { 320 writeEndTag(H5); 321 } else if (depth == SECTION_LEVEL_6) { 322 writeEndTag(H6); 323 } 324 } 325 326 // ----------------------------------------------------------------------- 327 // 328 // ----------------------------------------------------------------------- 329 330 /** 331 * {@inheritDoc} 332 * 333 * @see XdocMarkup#SOURCE_TAG 334 * @see javax.swing.text.html.HTML.Tag#PRE 335 * @param attributes a {@link org.apache.maven.doxia.sink.SinkEventAttributes} object. 336 */ 337 public void verbatim(SinkEventAttributes attributes) { 338 339 MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES); 340 341 if (atts == null) { 342 atts = new SinkEventAttributeSet(); 343 } 344 345 this.setVerbatimMode(VerbatimMode.ON); 346 if (atts.isDefined(SinkEventAttributes.DECORATION)) { 347 if ("source".equals(atts.getAttribute(SinkEventAttributes.DECORATION))) { 348 this.setVerbatimMode(VerbatimMode.ON_WITH_CODE); 349 } 350 } 351 352 atts.removeAttribute(SinkEventAttributes.DECORATION); 353 354 if (getVerbatimMode() == VerbatimMode.ON_WITH_CODE) { 355 writeStartTag(SOURCE_TAG, atts); 356 } else { 357 writeStartTag(PRE, atts); 358 } 359 } 360 361 /** 362 * {@inheritDoc} 363 * 364 * @see XdocMarkup#SOURCE_TAG 365 * @see javax.swing.text.html.HTML.Tag#PRE 366 */ 367 public void verbatim_() { 368 if (getVerbatimMode() == VerbatimMode.ON_WITH_CODE) { 369 writeEndTag(SOURCE_TAG); 370 } else { 371 writeEndTag(PRE); 372 } 373 374 this.setVerbatimMode(VerbatimMode.OFF); 375 } 376 377 /** 378 * {@inheritDoc} 379 * 380 * @see javax.swing.text.html.HTML.Tag#TABLE 381 */ 382 public void tableRows(int[] justification, boolean grid) { 383 // similar to super.tableRows(justification, grid) but without class. 384 385 setCellJustif(justification); 386 387 MutableAttributeSet att = new SinkEventAttributeSet(); 388 389 if (!tableAttributes.isDefined(Attribute.BORDER.toString())) { 390 att.addAttribute(Attribute.BORDER, (grid ? "1" : "0")); 391 } 392 393 att.addAttributes(tableAttributes); 394 395 tableAttributes.removeAttributes(tableAttributes); 396 397 writeStartTag(TABLE, att); 398 } 399 400 /** 401 * {@inheritDoc} 402 * 403 * @see javax.swing.text.html.HTML.Tag#TR 404 */ 405 @Override 406 public void tableRow(SinkEventAttributes attributes) { 407 408 writeStartTag(TR, attributes); 409 410 setCellCount(0); 411 } 412 413 /** 414 * <p>close.</p> 415 */ 416 public void close() { 417 super.close(); 418 419 init(); 420 } 421 422 // ---------------------------------------------------------------------- 423 // 424 // ---------------------------------------------------------------------- 425 426}