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