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