blob: 846d524e583f25c99cc0419438ff10bbb8ce1175 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.doxia.sink.impl;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML.Attribute;
import javax.swing.text.html.HTML.Tag;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.doxia.markup.HtmlMarkup;
import org.apache.maven.doxia.markup.Markup;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.doxia.sink.SinkEventAttributes;
import org.apache.maven.doxia.util.DoxiaUtils;
import org.apache.maven.doxia.util.HtmlTools;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract base xhtml5 sink implementation.
*/
public class Xhtml5BaseSink extends AbstractXmlSink implements HtmlMarkup {
private static final Logger LOGGER = LoggerFactory.getLogger(Xhtml5BaseSink.class);
// ----------------------------------------------------------------------
// Instance fields
// ----------------------------------------------------------------------
/** The PrintWriter to write the result. */
private final PrintWriter writer;
/** Used to identify if a class string contains `hidden` */
private static final Pattern HIDDEN_CLASS_PATTERN = Pattern.compile("(?:.*\\s|^)hidden(?:\\s.*|$)");
/** Used to collect text events mainly for the head events. */
private StringBuffer textBuffer = new StringBuffer();
/** An indication on if we're inside a head. */
private boolean headFlag;
/** Keep track of the main and div tags for content events. */
protected Stack<Tag> contentStack = new Stack<>();
/** Keep track of the closing tags for inline events. */
protected Stack<List<Tag>> inlineStack = new Stack<>();
/** An indication on if we're inside a paragraph flag. */
private boolean paragraphFlag;
/** An indication on if we're in verbatim mode. */
private boolean verbatimFlag;
/** Stack of alignment int[] of table cells. */
private final LinkedList<int[]> cellJustifStack;
/** Stack of justification of table cells. */
private final LinkedList<Boolean> isCellJustifStack;
/** Stack of current table cell. */
private final LinkedList<Integer> cellCountStack;
/** Used to style successive table rows differently. */
private boolean evenTableRow = true;
/** The stack of StringWriter to write the table result temporary, so we could play with the output DOXIA-177. */
private final LinkedList<StringWriter> tableContentWriterStack;
private final LinkedList<StringWriter> tableCaptionWriterStack;
private final LinkedList<PrettyPrintXMLWriter> tableCaptionXMLWriterStack;
/** The stack of table caption */
private final LinkedList<String> tableCaptionStack;
/** used to store attributes passed to table(). */
protected MutableAttributeSet tableAttributes;
// ----------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------
/**
* Constructor, initialize the PrintWriter.
*
* @param out The writer to write the result.
*/
public Xhtml5BaseSink(Writer out) {
this.writer = new PrintWriter(out);
this.cellJustifStack = new LinkedList<>();
this.isCellJustifStack = new LinkedList<>();
this.cellCountStack = new LinkedList<>();
this.tableContentWriterStack = new LinkedList<>();
this.tableCaptionWriterStack = new LinkedList<>();
this.tableCaptionXMLWriterStack = new LinkedList<>();
this.tableCaptionStack = new LinkedList<>();
init();
}
// ----------------------------------------------------------------------
// Accessor methods
// ----------------------------------------------------------------------
/**
* To use mainly when playing with the head events.
*
* @return the current buffer of text events.
*/
protected StringBuffer getTextBuffer() {
return this.textBuffer;
}
/**
* <p>Setter for the field <code>headFlag</code>.</p>
*
* @param headFlag an header flag.
*/
protected void setHeadFlag(boolean headFlag) {
this.headFlag = headFlag;
}
/**
* <p>isHeadFlag.</p>
*
* @return the current headFlag.
*/
protected boolean isHeadFlag() {
return this.headFlag;
}
/**
* <p>Setter for the field <code>verbatimFlag</code>.</p>
*
* @param verb a verbatim flag.
*/
protected void setVerbatimFlag(boolean verb) {
this.verbatimFlag = verb;
}
/**
* <p>isVerbatimFlag.</p>
*
* @return the current verbatim flag.
*/
protected boolean isVerbatimFlag() {
return this.verbatimFlag;
}
/**
* <p>Setter for the field <code>cellJustif</code>.</p>
*
* @param justif the new cell justification array.
*/
protected void setCellJustif(int[] justif) {
this.cellJustifStack.addLast(justif);
this.isCellJustifStack.addLast(Boolean.TRUE);
}
/**
* <p>Getter for the field <code>cellJustif</code>.</p>
*
* @return the current cell justification array.
*/
protected int[] getCellJustif() {
return this.cellJustifStack.getLast();
}
/**
* <p>Setter for the field <code>cellCount</code>.</p>
*
* @param count the new cell count.
*/
protected void setCellCount(int count) {
this.cellCountStack.addLast(count);
}
/**
* <p>Getter for the field <code>cellCount</code>.</p>
*
* @return the current cell count.
*/
protected int getCellCount() {
return this.cellCountStack.getLast();
}
/** {@inheritDoc} */
@Override
protected void init() {
super.init();
resetTextBuffer();
this.cellJustifStack.clear();
this.isCellJustifStack.clear();
this.cellCountStack.clear();
this.tableContentWriterStack.clear();
this.tableCaptionWriterStack.clear();
this.tableCaptionXMLWriterStack.clear();
this.tableCaptionStack.clear();
this.inlineStack.clear();
this.headFlag = false;
this.paragraphFlag = false;
this.verbatimFlag = false;
this.evenTableRow = true;
this.tableAttributes = null;
}
/**
* Reset the text buffer.
*/
protected void resetTextBuffer() {
this.textBuffer = new StringBuffer();
}
// ----------------------------------------------------------------------
// Sections
// ----------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public void article() {
article(null);
}
/** {@inheritDoc} */
@Override
public void article(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.ARTICLE, atts);
}
/** {@inheritDoc} */
@Override
public void article_() {
writeEndTag(HtmlMarkup.ARTICLE);
}
/** {@inheritDoc} */
@Override
public void navigation() {
navigation(null);
}
/** {@inheritDoc} */
@Override
public void navigation(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.NAV, atts);
}
/** {@inheritDoc} */
@Override
public void navigation_() {
writeEndTag(HtmlMarkup.NAV);
}
/** {@inheritDoc} */
@Override
public void sidebar() {
sidebar(null);
}
/** {@inheritDoc} */
@Override
public void sidebar(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.ASIDE, atts);
}
/** {@inheritDoc} */
@Override
public void sidebar_() {
writeEndTag(HtmlMarkup.ASIDE);
}
/** {@inheritDoc} */
@Override
public void section(int level, SinkEventAttributes attributes) {
onSection(level, attributes);
}
/** {@inheritDoc} */
@Override
public void sectionTitle(int level, SinkEventAttributes attributes) {
onSectionTitle(level, attributes);
}
/** {@inheritDoc} */
@Override
public void sectionTitle_(int level) {
onSectionTitle_(level);
}
/** {@inheritDoc} */
@Override
public void section_(int level) {
onSection_(level);
}
/** {@inheritDoc} */
@Override
public void section1() {
onSection(SECTION_LEVEL_1, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle1() {
onSectionTitle(SECTION_LEVEL_1, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle1_() {
onSectionTitle_(SECTION_LEVEL_1);
}
/** {@inheritDoc} */
@Override
public void section1_() {
onSection_(SECTION_LEVEL_1);
}
/** {@inheritDoc} */
@Override
public void section2() {
onSection(SECTION_LEVEL_2, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle2() {
onSectionTitle(SECTION_LEVEL_2, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle2_() {
onSectionTitle_(SECTION_LEVEL_2);
}
/** {@inheritDoc} */
@Override
public void section2_() {
onSection_(SECTION_LEVEL_2);
}
/** {@inheritDoc} */
@Override
public void section3() {
onSection(SECTION_LEVEL_3, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle3() {
onSectionTitle(SECTION_LEVEL_3, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle3_() {
onSectionTitle_(SECTION_LEVEL_3);
}
/** {@inheritDoc} */
@Override
public void section3_() {
onSection_(SECTION_LEVEL_3);
}
/** {@inheritDoc} */
@Override
public void section4() {
onSection(SECTION_LEVEL_4, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle4() {
onSectionTitle(SECTION_LEVEL_4, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle4_() {
onSectionTitle_(SECTION_LEVEL_4);
}
/** {@inheritDoc} */
@Override
public void section4_() {
onSection_(SECTION_LEVEL_4);
}
/** {@inheritDoc} */
@Override
public void section5() {
onSection(SECTION_LEVEL_5, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle5() {
onSectionTitle(SECTION_LEVEL_5, null);
}
/** {@inheritDoc} */
@Override
public void sectionTitle5_() {
onSectionTitle_(SECTION_LEVEL_5);
}
/** {@inheritDoc} */
@Override
public void section5_() {
onSection_(SECTION_LEVEL_5);
}
/**
* Starts a section.
*
* @param depth The level of the section.
* @param attributes some attributes. May be null.
*/
protected void onSection(int depth, SinkEventAttributes attributes) {
if (depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5) {
MutableAttributeSet att = new SinkEventAttributeSet();
att.addAttributes(SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES));
writeStartTag(HtmlMarkup.SECTION, att);
}
}
/**
* Ends a section.
*
* @param depth The level of the section.
* @see javax.swing.text.html.HTML.Tag#DIV
*/
protected void onSection_(int depth) {
if (depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5) {
writeEndTag(HtmlMarkup.SECTION);
}
}
/**
* Starts a section title.
*
* @param depth The level of the section title.
* @param attributes some attributes. May be null.
* @see javax.swing.text.html.HTML.Tag#H1
* @see javax.swing.text.html.HTML.Tag#H2
* @see javax.swing.text.html.HTML.Tag#H3
* @see javax.swing.text.html.HTML.Tag#H4
* @see javax.swing.text.html.HTML.Tag#H5
*/
protected void onSectionTitle(int depth, SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
if (depth == SECTION_LEVEL_1) {
writeStartTag(HtmlMarkup.H1, atts);
} else if (depth == SECTION_LEVEL_2) {
writeStartTag(HtmlMarkup.H2, atts);
} else if (depth == SECTION_LEVEL_3) {
writeStartTag(HtmlMarkup.H3, atts);
} else if (depth == SECTION_LEVEL_4) {
writeStartTag(HtmlMarkup.H4, atts);
} else if (depth == SECTION_LEVEL_5) {
writeStartTag(HtmlMarkup.H5, atts);
}
}
/**
* Ends a section title.
*
* @param depth The level of the section title.
* @see javax.swing.text.html.HTML.Tag#H1
* @see javax.swing.text.html.HTML.Tag#H2
* @see javax.swing.text.html.HTML.Tag#H3
* @see javax.swing.text.html.HTML.Tag#H4
* @see javax.swing.text.html.HTML.Tag#H5
*/
protected void onSectionTitle_(int depth) {
if (depth == SECTION_LEVEL_1) {
writeEndTag(HtmlMarkup.H1);
} else if (depth == SECTION_LEVEL_2) {
writeEndTag(HtmlMarkup.H2);
} else if (depth == SECTION_LEVEL_3) {
writeEndTag(HtmlMarkup.H3);
} else if (depth == SECTION_LEVEL_4) {
writeEndTag(HtmlMarkup.H4);
} else if (depth == SECTION_LEVEL_5) {
writeEndTag(HtmlMarkup.H5);
}
}
/** {@inheritDoc} */
@Override
public void header() {
header(null);
}
/** {@inheritDoc} */
@Override
public void header(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.HEADER, atts);
}
/** {@inheritDoc} */
@Override
public void header_() {
writeEndTag(HtmlMarkup.HEADER);
}
/** {@inheritDoc} */
@Override
public void content() {
content((SinkEventAttributes) null);
}
/** {@inheritDoc} */
@Override
public void content(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
if (contentStack.empty()) {
writeStartTag(contentStack.push(HtmlMarkup.MAIN), atts);
} else {
if (atts == null) {
atts = new SinkEventAttributeSet(1);
}
if (!atts.isDefined(SinkEventAttributes.CLASS)) {
atts.addAttribute(SinkEventAttributes.CLASS, "content");
}
writeStartTag(contentStack.push(HtmlMarkup.DIV), atts);
}
}
/** {@inheritDoc} */
@Override
public void content_() {
try {
writeEndTag(contentStack.pop());
} catch (EmptyStackException ese) {
/* do nothing if the stack is empty */
}
}
/** {@inheritDoc} */
@Override
public void footer() {
footer(null);
}
/** {@inheritDoc} */
@Override
public void footer(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.FOOTER, atts);
}
/** {@inheritDoc} */
@Override
public void footer_() {
writeEndTag(HtmlMarkup.FOOTER);
}
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#UL
*/
@Override
public void list() {
list(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#UL
*/
@Override
public void list(SinkEventAttributes attributes) {
if (paragraphFlag) {
// The content of element type "p" must match
// "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
// dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
paragraph_();
}
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
writeStartTag(HtmlMarkup.UL, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#UL
*/
@Override
public void list_() {
writeEndTag(HtmlMarkup.UL);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#LI
*/
@Override
public void listItem() {
listItem(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#LI
*/
@Override
public void listItem(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
writeStartTag(HtmlMarkup.LI, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#LI
*/
@Override
public void listItem_() {
writeEndTag(HtmlMarkup.LI);
}
/**
* The default list style depends on the numbering.
*
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#OL
*/
@Override
public void numberedList(int numbering) {
numberedList(numbering, null);
}
/**
* The default list style depends on the numbering.
*
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#OL
*/
@Override
public void numberedList(int numbering, SinkEventAttributes attributes) {
if (paragraphFlag) {
// The content of element type "p" must match
// "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
// dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
paragraph_();
}
String style;
switch (numbering) {
case NUMBERING_UPPER_ALPHA:
style = "upper-alpha";
break;
case NUMBERING_LOWER_ALPHA:
style = "lower-alpha";
break;
case NUMBERING_UPPER_ROMAN:
style = "upper-roman";
break;
case NUMBERING_LOWER_ROMAN:
style = "lower-roman";
break;
case NUMBERING_DECIMAL:
default:
style = "decimal";
}
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
if (atts == null) {
atts = new SinkEventAttributeSet(1);
}
atts.addAttribute(Attribute.STYLE, "list-style-type: " + style);
writeStartTag(HtmlMarkup.OL, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#OL
*/
@Override
public void numberedList_() {
writeEndTag(HtmlMarkup.OL);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#LI
*/
@Override
public void numberedListItem() {
numberedListItem(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#LI
*/
@Override
public void numberedListItem(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
writeStartTag(HtmlMarkup.LI, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#LI
*/
@Override
public void numberedListItem_() {
writeEndTag(HtmlMarkup.LI);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DL
*/
@Override
public void definitionList() {
definitionList(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DL
*/
@Override
public void definitionList(SinkEventAttributes attributes) {
if (paragraphFlag) {
// The content of element type "p" must match
// "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
// dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
paragraph_();
}
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
writeStartTag(HtmlMarkup.DL, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DL
*/
@Override
public void definitionList_() {
writeEndTag(HtmlMarkup.DL);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DT
*/
@Override
public void definedTerm(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
writeStartTag(HtmlMarkup.DT, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DT
*/
@Override
public void definedTerm() {
definedTerm(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DT
*/
@Override
public void definedTerm_() {
writeEndTag(HtmlMarkup.DT);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DD
*/
@Override
public void definition() {
definition(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DD
*/
@Override
public void definition(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
writeStartTag(HtmlMarkup.DD, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DD
*/
@Override
public void definition_() {
writeEndTag(HtmlMarkup.DD);
}
/** {@inheritDoc} */
@Override
public void figure() {
figure(null);
}
/** {@inheritDoc} */
@Override
public void figure(SinkEventAttributes attributes) {
writeStartTag(HtmlMarkup.FIGURE, attributes);
}
/** {@inheritDoc} */
@Override
public void figure_() {
writeEndTag(HtmlMarkup.FIGURE);
}
/** {@inheritDoc} */
@Override
public void figureGraphics(String name) {
figureGraphics(name, null);
}
/** {@inheritDoc} */
@Override
public void figureGraphics(String src, SinkEventAttributes attributes) {
MutableAttributeSet filtered = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_IMG_ATTRIBUTES);
if (filtered != null) {
filtered.removeAttribute(Attribute.SRC.toString());
}
int count = (attributes == null ? 1 : attributes.getAttributeCount() + 1);
MutableAttributeSet atts = new SinkEventAttributeSet(count);
atts.addAttribute(Attribute.SRC, HtmlTools.escapeHTML(src, true));
atts.addAttributes(filtered);
writeStartTag(HtmlMarkup.IMG, atts, true);
}
/** {@inheritDoc} */
@Override
public void figureCaption() {
figureCaption(null);
}
/** {@inheritDoc} */
@Override
public void figureCaption(SinkEventAttributes attributes) {
writeStartTag(HtmlMarkup.FIGCAPTION, attributes);
}
/** {@inheritDoc} */
@Override
public void figureCaption_() {
writeEndTag(HtmlMarkup.FIGCAPTION);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#P
*/
@Override
public void paragraph() {
paragraph(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#P
*/
@Override
public void paragraph(SinkEventAttributes attributes) {
paragraphFlag = true;
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.P, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#P
*/
@Override
public void paragraph_() {
if (paragraphFlag) {
writeEndTag(HtmlMarkup.P);
paragraphFlag = false;
}
}
/** {@inheritDoc} */
@Override
public void data(String value) {
data(value, null);
}
/** {@inheritDoc} */
@Override
public void data(String value, SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
MutableAttributeSet att = new SinkEventAttributeSet();
if (value != null) {
att.addAttribute(Attribute.VALUE, value);
}
att.addAttributes(atts);
writeStartTag(HtmlMarkup.DATA, att);
}
/** {@inheritDoc} */
@Override
public void data_() {
writeEndTag(HtmlMarkup.DATA);
}
/** {@inheritDoc} */
@Override
public void time(String datetime) {
time(datetime, null);
}
/** {@inheritDoc} */
@Override
public void time(String datetime, SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
MutableAttributeSet att = new SinkEventAttributeSet();
if (datetime != null) {
att.addAttribute("datetime", datetime);
}
att.addAttributes(atts);
writeStartTag(HtmlMarkup.TIME, att);
}
/** {@inheritDoc} */
@Override
public void time_() {
writeEndTag(HtmlMarkup.TIME);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#ADDRESS
*/
@Override
public void address() {
address(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#ADDRESS
*/
@Override
public void address(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.ADDRESS, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#ADDRESS
*/
@Override
public void address_() {
writeEndTag(HtmlMarkup.ADDRESS);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#BLOCKQUOTE
*/
@Override
public void blockquote() {
blockquote(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#BLOCKQUOTE
*/
@Override
public void blockquote(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.BLOCKQUOTE, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#BLOCKQUOTE
*/
@Override
public void blockquote_() {
writeEndTag(HtmlMarkup.BLOCKQUOTE);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DIV
*/
@Override
public void division() {
division(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DIV
*/
@Override
public void division(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.DIV, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DIV
*/
@Override
public void division_() {
writeEndTag(HtmlMarkup.DIV);
}
/** {@inheritDoc} */
@Override
public void verbatim() {
verbatim(null);
}
/**
* The default class style is <code>verbatim</code>, for source is {@code verbatim source}.
*
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DIV
* @see javax.swing.text.html.HTML.Tag#PRE
*/
@Override
public void verbatim(SinkEventAttributes attributes) {
if (paragraphFlag) {
// The content of element type "p" must match
// "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
// dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
paragraph_();
}
verbatimFlag = true;
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES);
if (atts == null) {
atts = new SinkEventAttributeSet();
}
boolean source = false;
if (atts.isDefined(SinkEventAttributes.DECORATION)) {
source = "source"
.equals(atts.getAttribute(SinkEventAttributes.DECORATION).toString());
}
SinkEventAttributes divAtts = null;
String divClass = "verbatim";
if (source) {
divClass += " source";
}
divAtts = new SinkEventAttributeSet(Attribute.CLASS.toString(), divClass);
atts.removeAttribute(SinkEventAttributes.DECORATION);
writeStartTag(HtmlMarkup.DIV, divAtts);
writeStartTag(HtmlMarkup.PRE, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#DIV
* @see javax.swing.text.html.HTML.Tag#PRE
*/
@Override
public void verbatim_() {
writeEndTag(HtmlMarkup.PRE);
writeEndTag(HtmlMarkup.DIV);
verbatimFlag = false;
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#HR
*/
@Override
public void horizontalRule() {
horizontalRule(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#HR
*/
@Override
public void horizontalRule(SinkEventAttributes attributes) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_HR_ATTRIBUTES);
writeSimpleTag(HtmlMarkup.HR, atts);
}
/** {@inheritDoc} */
@Override
public void table() {
// start table with tableRows
table(null);
}
/** {@inheritDoc} */
@Override
public void table(SinkEventAttributes attributes) {
this.tableContentWriterStack.addLast(new StringWriter());
if (paragraphFlag) {
// The content of element type "p" must match
// "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
// dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
paragraph_();
}
// start table with tableRows
if (attributes == null) {
this.tableAttributes = new SinkEventAttributeSet(0);
} else {
this.tableAttributes = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_TABLE_ATTRIBUTES);
}
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#TABLE
*/
@Override
public void table_() {
writeEndTag(HtmlMarkup.TABLE);
if (!this.cellCountStack.isEmpty()) {
this.cellCountStack.removeLast().toString();
}
if (this.tableContentWriterStack.isEmpty()) {
LOGGER.warn("No table content");
return;
}
String tableContent = this.tableContentWriterStack.removeLast().toString();
String tableCaption = null;
if (!this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null) {
tableCaption = this.tableCaptionStack.removeLast();
}
if (tableCaption != null) {
// DOXIA-177
StringBuilder sb = new StringBuilder();
sb.append(tableContent, 0, tableContent.indexOf(Markup.GREATER_THAN) + 1);
sb.append(tableCaption);
sb.append(tableContent.substring(tableContent.indexOf(Markup.GREATER_THAN) + 1));
write(sb.toString());
} else {
write(tableContent);
}
}
@Override
public void tableRows() {
tableRows(null, false);
}
/**
* The default style class is <code>bodyTable</code>.
*
* @param grid if {@code true} the style class {@code bodyTableBorder} will be added
*
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#TABLE
*/
@Override
public void tableRows(int[] justification, boolean grid) {
setCellJustif(justification);
MutableAttributeSet att = new SinkEventAttributeSet();
if (!this.tableAttributes.isDefined(Attribute.CLASS.toString())) {
att.addAttribute(Attribute.CLASS, "bodyTable" + (grid ? " bodyTableBorder" : ""));
}
att.addAttributes(this.tableAttributes);
this.tableAttributes.removeAttributes(this.tableAttributes);
writeStartTag(HtmlMarkup.TABLE, att);
this.cellCountStack.addLast(0);
}
/** {@inheritDoc} */
@Override
public void tableRows_() {
if (!this.cellJustifStack.isEmpty()) {
this.cellJustifStack.removeLast();
}
if (!this.isCellJustifStack.isEmpty()) {
this.isCellJustifStack.removeLast();
}
this.evenTableRow = true;
}
/**
* Rows are striped with two colors by adding the class <code>a</code> or <code>b</code>. {@inheritDoc}
*
* @see javax.swing.text.html.HTML.Tag#TR
*/
@Override
public void tableRow() {
tableRow(null);
}
/**
* Rows are striped with two colors by adding the class <code>a</code> or <code>b</code>. If the provided attributes
* specify the <code>hidden</code> class, the next call to tableRow will set the same striping class as this one. A
* style for <code>hidden</code> or <code>table.bodyTable hidden</code> may need to be provided to actually hide
* such a row. {@inheritDoc}
*
* @see javax.swing.text.html.HTML.Tag#TR
*/
@Override
public void tableRow(SinkEventAttributes attributes) {
MutableAttributeSet attrs = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_TR_ATTRIBUTES);
if (attrs == null) {
attrs = new SinkEventAttributeSet();
}
String rowClass = evenTableRow ? "a" : "b";
boolean hidden = false;
if (attrs.isDefined(Attribute.CLASS.toString())) {
String givenRowClass = (String) attrs.getAttribute(Attribute.CLASS.toString());
if (HIDDEN_CLASS_PATTERN.matcher(givenRowClass).matches()) {
hidden = true;
}
rowClass = givenRowClass + " " + rowClass;
}
attrs.addAttribute(Attribute.CLASS, rowClass);
writeStartTag(HtmlMarkup.TR, attrs);
if (!hidden) {
evenTableRow = !evenTableRow;
}
if (!this.cellCountStack.isEmpty()) {
this.cellCountStack.removeLast();
this.cellCountStack.addLast(0);
}
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#TR
*/
@Override
public void tableRow_() {
writeEndTag(HtmlMarkup.TR);
}
/** {@inheritDoc} */
@Override
public void tableCell() {
tableCell((SinkEventAttributeSet) null);
}
/** {@inheritDoc} */
@Override
public void tableHeaderCell() {
tableHeaderCell((SinkEventAttributeSet) null);
}
/** {@inheritDoc} */
@Override
public void tableCell(SinkEventAttributes attributes) {
tableCell(false, attributes);
}
/** {@inheritDoc} */
@Override
public void tableHeaderCell(SinkEventAttributes attributes) {
tableCell(true, attributes);
}
/**
* @param headerRow true if it is an header row
* @param attributes the cell attributes
* @see javax.swing.text.html.HTML.Tag#TH
* @see javax.swing.text.html.HTML.Tag#TD
*/
private void tableCell(boolean headerRow, MutableAttributeSet attributes) {
Tag t = (headerRow ? HtmlMarkup.TH : HtmlMarkup.TD);
if (!headerRow
&& cellCountStack != null
&& !cellCountStack.isEmpty()
&& cellJustifStack != null
&& !cellJustifStack.isEmpty()
&& getCellJustif() != null) {
int cellCount = getCellCount();
if (cellCount < getCellJustif().length
&& (attributes == null || !attributes.isDefined(Attribute.STYLE.toString()))) {
Map<Integer, MutableAttributeSet> hash = new HashMap<>();
hash.put(
Sink.JUSTIFY_CENTER,
new SinkEventAttributeSet(SinkEventAttributes.STYLE, "text-align: center;").unmodifiable());
hash.put(
Sink.JUSTIFY_LEFT,
new SinkEventAttributeSet(SinkEventAttributes.STYLE, "text-align: left;").unmodifiable());
hash.put(
Sink.JUSTIFY_RIGHT,
new SinkEventAttributeSet(SinkEventAttributes.STYLE, "text-align: right;").unmodifiable());
MutableAttributeSet atts = hash.get(getCellJustif()[cellCount]);
if (attributes == null) {
attributes = new SinkEventAttributeSet();
}
if (atts != null) {
attributes.addAttributes(atts);
}
}
}
if (attributes == null) {
writeStartTag(t, null);
} else {
writeStartTag(t, SinkUtils.filterAttributes(attributes, SinkUtils.SINK_TD_ATTRIBUTES));
}
}
/** {@inheritDoc} */
@Override
public void tableCell_() {
tableCell_(false);
}
/** {@inheritDoc} */
@Override
public void tableHeaderCell_() {
tableCell_(true);
}
/**
* Ends a table cell.
*
* @param headerRow true if it is an header row
* @see javax.swing.text.html.HTML.Tag#TH
* @see javax.swing.text.html.HTML.Tag#TD
*/
private void tableCell_(boolean headerRow) {
Tag t = (headerRow ? HtmlMarkup.TH : HtmlMarkup.TD);
writeEndTag(t);
if (!this.isCellJustifStack.isEmpty()
&& this.isCellJustifStack.getLast().equals(Boolean.TRUE)
&& !this.cellCountStack.isEmpty()) {
int cellCount = Integer.parseInt(this.cellCountStack.removeLast().toString());
this.cellCountStack.addLast(++cellCount);
}
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#CAPTION
*/
@Override
public void tableCaption() {
tableCaption(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#CAPTION
*/
@Override
public void tableCaption(SinkEventAttributes attributes) {
StringWriter sw = new StringWriter();
this.tableCaptionWriterStack.addLast(sw);
this.tableCaptionXMLWriterStack.addLast(new PrettyPrintXMLWriter(sw));
// TODO: tableCaption should be written before tableRows (DOXIA-177)
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_SECTION_ATTRIBUTES);
writeStartTag(HtmlMarkup.CAPTION, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#CAPTION
*/
@Override
public void tableCaption_() {
writeEndTag(HtmlMarkup.CAPTION);
if (!this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null) {
this.tableCaptionStack.addLast(
this.tableCaptionWriterStack.removeLast().toString());
this.tableCaptionXMLWriterStack.removeLast();
}
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#A
*/
@Override
public void anchor(String name) {
anchor(name, null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#A
*/
@Override
public void anchor(String name, SinkEventAttributes attributes) {
Objects.requireNonNull(name, "name cannot be null");
if (headFlag) {
return;
}
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BASE_ATTRIBUTES);
String id = name;
if (!DoxiaUtils.isValidId(id)) {
id = DoxiaUtils.encodeId(name);
LOGGER.debug("Modified invalid anchor name '{}' to '{}'", name, id);
}
MutableAttributeSet att = new SinkEventAttributeSet();
att.addAttribute(Attribute.ID, id);
att.addAttributes(atts);
writeStartTag(HtmlMarkup.A, att);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#A
*/
@Override
public void anchor_() {
if (!headFlag) {
writeEndTag(HtmlMarkup.A);
}
}
/**
* The default style class for external link is <code>externalLink</code>.
*
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#A
**/
@Override
public void link(String name) {
link(name, null);
}
/**
* The default style class for external link is <code>externalLink</code>.
*
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#A
**/
@Override
public void link(String name, SinkEventAttributes attributes) {
Objects.requireNonNull(name, "name cannot be null");
if (headFlag) {
return;
}
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_LINK_ATTRIBUTES);
if (atts == null) {
atts = new SinkEventAttributeSet();
}
if (DoxiaUtils.isExternalLink(name)) {
String linkClass = "externalLink";
if (atts.isDefined(Attribute.CLASS.toString())) {
String givenLinkClass = (String) atts.getAttribute(Attribute.CLASS.toString());
linkClass = givenLinkClass + " " + linkClass;
}
atts.addAttribute(Attribute.CLASS, linkClass);
}
atts.addAttribute(Attribute.HREF, HtmlTools.escapeHTML(name));
writeStartTag(HtmlMarkup.A, atts);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#A
*/
@Override
public void link_() {
if (!headFlag) {
writeEndTag(HtmlMarkup.A);
}
}
/** {@inheritDoc} */
@Override
public void inline() {
inline(null);
}
private void inlineSemantics(SinkEventAttributes attributes, String semantic, List<Tag> tags, Tag tag) {
if (attributes.containsAttribute(SinkEventAttributes.SEMANTICS, semantic)) {
SinkEventAttributes attributesNoSemantics = (SinkEventAttributes) attributes.copyAttributes();
attributesNoSemantics.removeAttribute(SinkEventAttributes.SEMANTICS);
writeStartTag(tag, attributesNoSemantics);
tags.add(0, tag);
}
}
/** {@inheritDoc} */
@Override
public void inline(SinkEventAttributes attributes) {
if (!headFlag) {
List<Tag> tags = new ArrayList<>();
if (attributes != null) {
inlineSemantics(attributes, "emphasis", tags, HtmlMarkup.EM);
inlineSemantics(attributes, "strong", tags, HtmlMarkup.STRONG);
inlineSemantics(attributes, "small", tags, HtmlMarkup.SMALL);
inlineSemantics(attributes, "line-through", tags, HtmlMarkup.S);
inlineSemantics(attributes, "citation", tags, HtmlMarkup.CITE);
inlineSemantics(attributes, "quote", tags, HtmlMarkup.Q);
inlineSemantics(attributes, "definition", tags, HtmlMarkup.DFN);
inlineSemantics(attributes, "abbreviation", tags, HtmlMarkup.ABBR);
inlineSemantics(attributes, "italic", tags, HtmlMarkup.I);
inlineSemantics(attributes, "bold", tags, HtmlMarkup.B);
inlineSemantics(attributes, "code", tags, HtmlMarkup.CODE);
inlineSemantics(attributes, "variable", tags, HtmlMarkup.VAR);
inlineSemantics(attributes, "sample", tags, HtmlMarkup.SAMP);
inlineSemantics(attributes, "keyboard", tags, HtmlMarkup.KBD);
inlineSemantics(attributes, "superscript", tags, HtmlMarkup.SUP);
inlineSemantics(attributes, "subscript", tags, HtmlMarkup.SUB);
inlineSemantics(attributes, "annotation", tags, HtmlMarkup.U);
inlineSemantics(attributes, "highlight", tags, HtmlMarkup.MARK);
inlineSemantics(attributes, "ruby", tags, HtmlMarkup.RUBY);
inlineSemantics(attributes, "rubyBase", tags, HtmlMarkup.RB);
inlineSemantics(attributes, "rubyText", tags, HtmlMarkup.RT);
inlineSemantics(attributes, "rubyTextContainer", tags, HtmlMarkup.RTC);
inlineSemantics(attributes, "rubyParentheses", tags, HtmlMarkup.RP);
inlineSemantics(attributes, "bidirectionalIsolation", tags, HtmlMarkup.BDI);
inlineSemantics(attributes, "bidirectionalOverride", tags, HtmlMarkup.BDO);
inlineSemantics(attributes, "phrase", tags, HtmlMarkup.SPAN);
inlineSemantics(attributes, "insert", tags, HtmlMarkup.INS);
inlineSemantics(attributes, "delete", tags, HtmlMarkup.DEL);
}
inlineStack.push(tags);
}
}
/** {@inheritDoc} */
@Override
public void inline_() {
if (!headFlag) {
for (Tag tag : inlineStack.pop()) {
writeEndTag(tag);
}
}
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#I
*/
@Override
public void italic() {
inline(SinkEventAttributeSet.Semantics.ITALIC);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#I
*/
@Override
public void italic_() {
inline_();
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#B
*/
@Override
public void bold() {
inline(SinkEventAttributeSet.Semantics.BOLD);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#B
*/
@Override
public void bold_() {
inline_();
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#CODE
*/
@Override
public void monospaced() {
inline(SinkEventAttributeSet.Semantics.CODE);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#CODE
*/
@Override
public void monospaced_() {
inline_();
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#BR
*/
@Override
public void lineBreak() {
lineBreak(null);
}
/**
* {@inheritDoc}
* @see javax.swing.text.html.HTML.Tag#BR
*/
@Override
public void lineBreak(SinkEventAttributes attributes) {
if (headFlag || isVerbatimFlag()) {
getTextBuffer().append(EOL);
} else {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BR_ATTRIBUTES);
writeSimpleTag(HtmlMarkup.BR, atts);
}
}
/** {@inheritDoc} */
@Override
public void lineBreakOpportunity() {
lineBreakOpportunity(null);
}
/** {@inheritDoc} */
@Override
public void lineBreakOpportunity(SinkEventAttributes attributes) {
if (!headFlag && !isVerbatimFlag()) {
MutableAttributeSet atts = SinkUtils.filterAttributes(attributes, SinkUtils.SINK_BR_ATTRIBUTES);
writeSimpleTag(HtmlMarkup.WBR, atts);
}
}
/** {@inheritDoc} */
@Override
public void pageBreak() {
comment(" PB ");
}
/** {@inheritDoc} */
@Override
public void nonBreakingSpace() {
if (headFlag) {
getTextBuffer().append(' ');
} else {
write("&#160;");
}
}
/** {@inheritDoc} */
@Override
public void text(String text) {
if (headFlag) {
getTextBuffer().append(text);
} else if (verbatimFlag) {
verbatimContent(text);
} else {
content(text);
}
}
/** {@inheritDoc} */
@Override
public void text(String text, SinkEventAttributes attributes) {
text(text);
}
/** {@inheritDoc} */
@Override
public void rawText(String text) {
if (headFlag) {
getTextBuffer().append(text);
} else {
write(text);
}
}
/** {@inheritDoc} */
@Override
public void comment(String comment) {
if (comment != null) {
final String originalComment = comment;
// http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments
while (comment.contains("--")) {
comment = comment.replace("--", "- -");
}
if (comment.endsWith("-")) {
comment += " ";
}
if (!originalComment.equals(comment)) {
LOGGER.warn("Modified invalid comment '{}' to '{}'", originalComment, comment);
}
final StringBuilder buffer = new StringBuilder(comment.length() + 7);
buffer.append(LESS_THAN).append(BANG).append(MINUS).append(MINUS);
buffer.append(comment);
buffer.append(MINUS).append(MINUS).append(GREATER_THAN);
write(buffer.toString());
}
}
/**
* {@inheritDoc}
*
* Add an unknown event.
* This can be used to generate html tags for which no corresponding sink event exists.
*
* <p>
* If {@link org.apache.maven.doxia.util.HtmlTools#getHtmlTag(String) HtmlTools.getHtmlTag(name)}
* does not return null, the corresponding tag will be written.
* </p>
*
* <p>For example, the div block</p>
*
* <pre>
* &lt;div class="detail" style="display:inline"&gt;text&lt;/div&gt;
* </pre>
*
* <p>can be generated via the following event sequence:</p>
*
* <pre>
* SinkEventAttributeSet atts = new SinkEventAttributeSet();
* atts.addAttribute(SinkEventAttributes.CLASS, "detail");
* atts.addAttribute(SinkEventAttributes.STYLE, "display:inline");
* sink.unknown("div", new Object[]{new Integer(HtmlMarkup.TAG_TYPE_START)}, atts);
* sink.text("text");
* sink.unknown("div", new Object[]{new Integer(HtmlMarkup.TAG_TYPE_END)}, null);
* </pre>
*
* @param name the name of the event. If this is not a valid xhtml tag name
* as defined in {@link org.apache.maven.doxia.markup.HtmlMarkup} then the event is ignored.
* @param requiredParams If this is null or the first argument is not an Integer then the event is ignored.
* The first argument should indicate the type of the unknown event, its integer value should be one of
* {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_START TAG_TYPE_START},
* {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_END TAG_TYPE_END},
* {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_SIMPLE TAG_TYPE_SIMPLE},
* {@link org.apache.maven.doxia.markup.HtmlMarkup#ENTITY_TYPE ENTITY_TYPE}, or
* {@link org.apache.maven.doxia.markup.HtmlMarkup#CDATA_TYPE CDATA_TYPE},
* otherwise the event will be ignored.
* @param attributes a set of attributes for the event. May be null.
* The attributes will always be written, no validity check is performed.
*/
@Override
public void unknown(String name, Object[] requiredParams, SinkEventAttributes attributes) {
if (requiredParams == null || !(requiredParams[0] instanceof Integer)) {
LOGGER.warn("No type information for unknown event '{}', ignoring!", name);
return;
}
int tagType = (Integer) requiredParams[0];
if (tagType == ENTITY_TYPE) {
rawText(name);
return;
}
if (tagType == CDATA_TYPE) {
rawText(EOL + "//<![CDATA[" + requiredParams[1] + "]]>" + EOL);
return;
}
Tag tag = HtmlTools.getHtmlTag(name);
if (tag == null) {
LOGGER.warn("No HTML tag found for unknown event '{}', ignoring!", name);
} else {
if (tagType == TAG_TYPE_SIMPLE) {
writeSimpleTag(tag, escapeAttributeValues(attributes));
} else if (tagType == TAG_TYPE_START) {
writeStartTag(tag, escapeAttributeValues(attributes));
} else if (tagType == TAG_TYPE_END) {
writeEndTag(tag);
} else {
LOGGER.warn("No type information for unknown event '{}', ignoring!", name);
}
}
}
private SinkEventAttributes escapeAttributeValues(SinkEventAttributes attributes) {
SinkEventAttributeSet set = new SinkEventAttributeSet(attributes.getAttributeCount());
Enumeration<?> names = attributes.getAttributeNames();
while (names.hasMoreElements()) {
Object name = names.nextElement();
set.addAttribute(name, escapeHTML(attributes.getAttribute(name).toString()));
}
return set;
}
/** {@inheritDoc} */
@Override
public void flush() {
writer.flush();
}
/** {@inheritDoc} */
@Override
public void close() {
writer.close();
init();
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
/**
* Write HTML escaped text to output.
*
* @param text The text to write.
*/
protected void content(String text) {
// small hack due to DOXIA-314
String txt = escapeHTML(text);
txt = StringUtils.replace(txt, "&amp;#", "&#");
write(txt);
}
/**
* Write HTML escaped text to output.
*
* @param text The text to write.
*/
protected void verbatimContent(String text) {
write(escapeHTML(text));
}
/**
* Forward to HtmlTools.escapeHTML(text).
*
* @param text the String to escape, may be null
* @return the text escaped, "" if null String input
* @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
*/
protected static String escapeHTML(String text) {
return HtmlTools.escapeHTML(text, false);
}
/**
* Forward to HtmlTools.encodeURL(text).
*
* @param text the String to encode, may be null.
* @return the text encoded, null if null String input.
* @see org.apache.maven.doxia.util.HtmlTools#encodeURL(String)
*/
protected static String encodeURL(String text) {
return HtmlTools.encodeURL(text);
}
/** {@inheritDoc} */
protected void write(String text) {
if (!this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null) {
this.tableCaptionXMLWriterStack.getLast().writeMarkup(unifyEOLs(text));
} else if (!this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null) {
this.tableContentWriterStack.getLast().write(unifyEOLs(text));
} else {
writer.write(unifyEOLs(text));
}
}
/** {@inheritDoc} */
@Override
protected void writeStartTag(Tag t, MutableAttributeSet att, boolean isSimpleTag) {
if (this.tableCaptionXMLWriterStack.isEmpty()) {
super.writeStartTag(t, att, isSimpleTag);
} else {
String tag = (getNameSpace() != null ? getNameSpace() + ":" : "") + t.toString();
this.tableCaptionXMLWriterStack.getLast().startElement(tag);
if (att != null) {
Enumeration<?> names = att.getAttributeNames();
while (names.hasMoreElements()) {
Object key = names.nextElement();
Object value = att.getAttribute(key);
this.tableCaptionXMLWriterStack.getLast().addAttribute(key.toString(), value.toString());
}
}
if (isSimpleTag) {
this.tableCaptionXMLWriterStack.getLast().endElement();
}
}
}
/** {@inheritDoc} */
@Override
protected void writeEndTag(Tag t) {
if (this.tableCaptionXMLWriterStack.isEmpty()) {
super.writeEndTag(t);
} else {
this.tableCaptionXMLWriterStack.getLast().endElement();
}
}
}