org.xwiki.rendering.internal.renderer.xhtml.XHTMLChainingRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xwiki-rendering-syntax-xhtml Show documentation
Show all versions of xwiki-rendering-syntax-xhtml Show documentation
XWiki Rendering - Syntax - XHTML
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.rendering.internal.renderer.xhtml;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.xwiki.rendering.internal.renderer.xhtml.image.XHTMLImageRenderer;
import org.xwiki.rendering.internal.renderer.xhtml.link.XHTMLLinkRenderer;
import org.xwiki.rendering.listener.Format;
import org.xwiki.rendering.listener.HeaderLevel;
import org.xwiki.rendering.listener.ListType;
import org.xwiki.rendering.listener.MetaData;
import org.xwiki.rendering.listener.chaining.BlockStateChainingListener;
import org.xwiki.rendering.listener.chaining.BlockStateChainingListener.Event;
import org.xwiki.rendering.listener.chaining.EmptyBlockChainingListener;
import org.xwiki.rendering.listener.chaining.ListenerChain;
import org.xwiki.rendering.listener.chaining.MetaDataStateChainingListener;
import org.xwiki.rendering.listener.reference.ResourceReference;
import org.xwiki.rendering.renderer.AbstractChainingPrintRenderer;
import org.xwiki.rendering.renderer.printer.WikiPrinter;
import org.xwiki.rendering.renderer.printer.XHTMLWikiPrinter;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.rendering.syntax.SyntaxType;
import org.xwiki.xml.html.HTMLConstants;
import org.xwiki.xml.html.HTMLElementSanitizer;
/**
* Convert listener events to XHTML.
*
* @version $Id: 58b61861e09ee495557cff07fa3327683ea1477d $
* @since 1.8RC1
*/
public class XHTMLChainingRenderer extends AbstractChainingPrintRenderer
{
/**
* Class attribute value that indicates if the header was generated by a macro.
*/
public static final String GENERATEDHEADERCLASS = "wikigeneratedheader";
/**
* Class attribute value that indicates if the header id attribute was generated automatically or if it was the user
* who specified an id.
*/
public static final String GENERATEDIDCLASS = "wikigeneratedid";
private XHTMLLinkRenderer linkRenderer;
private XHTMLImageRenderer imageRenderer;
private final HTMLElementSanitizer htmlElementSanitizer;
private XHTMLWikiPrinter xhtmlWikiPrinter;
/**
* @param linkRenderer the object to render link events into XHTML. This is done so that it's pluggable because
* link rendering depends on how the underlying system wants to handle it. For example for XWiki we check if the
* document exists, we get the document URL, etc.
* @param imageRenderer the object to render image events into XHTML. This is done so that it's pluggable
* because image rendering depends on how the underlying system wants to handle it. For example for XWiki we
* check if the image exists as a document attachments, we get its URL, etc.
* @param htmlElementSanitizer the sanitizer for XHTML elements
* @param listenerChain the chain of listener filters used to compute various states
*/
public XHTMLChainingRenderer(XHTMLLinkRenderer linkRenderer, XHTMLImageRenderer imageRenderer,
HTMLElementSanitizer htmlElementSanitizer, ListenerChain listenerChain)
{
setListenerChain(listenerChain);
this.linkRenderer = linkRenderer;
this.imageRenderer = imageRenderer;
this.htmlElementSanitizer = htmlElementSanitizer;
}
// State
protected BlockStateChainingListener getBlockState()
{
return (BlockStateChainingListener) getListenerChain().getListener(BlockStateChainingListener.class);
}
protected EmptyBlockChainingListener getEmptyBlockState()
{
return (EmptyBlockChainingListener) getListenerChain().getListener(EmptyBlockChainingListener.class);
}
protected MetaDataStateChainingListener getMetaDataState()
{
return (MetaDataStateChainingListener) getListenerChain().getListener(MetaDataStateChainingListener.class);
}
// Printer
@Override
protected void pushPrinter(WikiPrinter wikiPrinter)
{
super.pushPrinter(wikiPrinter);
getXHTMLWikiPrinter().setWikiPrinter(getPrinter());
}
@Override
protected void popPrinter()
{
super.popPrinter();
getXHTMLWikiPrinter().setWikiPrinter(getPrinter());
}
protected XHTMLWikiPrinter getXHTMLWikiPrinter()
{
if (this.xhtmlWikiPrinter == null) {
this.xhtmlWikiPrinter = new XHTMLWikiPrinter(getPrinter(), getHtmlElementSanitizer());
}
return this.xhtmlWikiPrinter;
}
protected HTMLElementSanitizer getHtmlElementSanitizer()
{
return this.htmlElementSanitizer;
}
// Events
@Override
public void beginGroup(Map parameters)
{
Map clonedParameters = new LinkedHashMap();
clonedParameters.putAll(parameters);
getXHTMLWikiPrinter().setStandalone();
getXHTMLWikiPrinter().printXMLStartElement("div", clonedParameters);
}
@Override
public void endGroup(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("div");
}
@Override
public void beginFormat(Format format, Map parameters)
{
switch (format) {
case BOLD:
getXHTMLWikiPrinter().printXMLStartElement("strong");
break;
case ITALIC:
getXHTMLWikiPrinter().printXMLStartElement("em");
break;
case STRIKEDOUT:
getXHTMLWikiPrinter().printXMLStartElement("del");
break;
case UNDERLINED:
getXHTMLWikiPrinter().printXMLStartElement("ins");
break;
case SUPERSCRIPT:
getXHTMLWikiPrinter().printXMLStartElement("sup");
break;
case SUBSCRIPT:
getXHTMLWikiPrinter().printXMLStartElement("sub");
break;
case MONOSPACE:
getXHTMLWikiPrinter().printXMLStartElement("tt");
break;
case NONE:
break;
// Unsupported format
default:
break;
}
if (!parameters.isEmpty()) {
getXHTMLWikiPrinter().printXMLStartElement("span", parameters);
}
}
@Override
public void endFormat(Format format, Map parameters)
{
if (!parameters.isEmpty()) {
getXHTMLWikiPrinter().printXMLEndElement("span");
}
switch (format) {
case BOLD:
getXHTMLWikiPrinter().printXMLEndElement("strong");
break;
case ITALIC:
getXHTMLWikiPrinter().printXMLEndElement("em");
break;
case STRIKEDOUT:
getXHTMLWikiPrinter().printXMLEndElement("del");
break;
case UNDERLINED:
getXHTMLWikiPrinter().printXMLEndElement("ins");
break;
case SUPERSCRIPT:
getXHTMLWikiPrinter().printXMLEndElement("sup");
break;
case SUBSCRIPT:
getXHTMLWikiPrinter().printXMLEndElement("sub");
break;
case MONOSPACE:
getXHTMLWikiPrinter().printXMLEndElement("tt");
break;
case NONE:
break;
// Unsupported format
default:
break;
}
}
@Override
public void beginParagraph(Map parameters)
{
getXHTMLWikiPrinter().setStandalone();
getXHTMLWikiPrinter().printXMLStartElement("p", parameters);
}
@Override
public void endParagraph(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("p");
}
@Override
public void onNewLine()
{
getXHTMLWikiPrinter().printXMLElement("br");
}
@Override
public void beginLink(ResourceReference reference, boolean freestanding, Map parameters)
{
// Ensure the link renderer is using the latest printer since the original printer used could have been
// superseded by another one in the printer stack.
this.linkRenderer.setXHTMLWikiPrinter(getXHTMLWikiPrinter());
// If the ResourceReference doesn't have a base reference specified, then look for one in previously sent
// events (it's sent in begin/endMetaData events).
List baseReferences = reference.getBaseReferences();
if (baseReferences.isEmpty()) {
reference.addBaseReferences(getMetaDataState().getAllMetaData(MetaData.BASE));
}
this.linkRenderer.beginLink(reference, freestanding, parameters);
}
@Override
public void endLink(ResourceReference reference, boolean freestanding, Map parameters)
{
this.linkRenderer.setHasLabel(!getEmptyBlockState().isCurrentContainerBlockEmpty());
this.linkRenderer.endLink(reference, freestanding, parameters);
}
@Override
public void beginHeader(HeaderLevel level, String id, Map parameters)
{
Map attributes = new LinkedHashMap();
attributes.put("id", id);
// Indicate that the id is generated. This is to differentiate from ids added as parameters.
// Note that we add this only if the user hasn't specified an id as an override in a parameter.
if (!parameters.containsKey("id")) {
addClassValue("class", GENERATEDIDCLASS, attributes);
}
attributes.putAll(parameters);
// Section editing feature:
// In order for the UI side to be able to add a section edit button we need to provide some information to it
// and especially we need to tell it if the header was a header generated by a macro or not. The reason is
// that macro-generated headers should not be editable by the user.
// TODO: In the future it's possible that we'll want this kind of behavior implemented using a Transformation.
// If we decide this then remove this code.
if (getBlockState().isInMacro()) {
addClassValue("class", GENERATEDHEADERCLASS, attributes);
}
getXHTMLWikiPrinter().setStandalone();
getXHTMLWikiPrinter().printXMLStartElement("h" + level.getAsInt(), attributes);
// We generate a span so that CSS rules have a hook to perform some magic that wouldn't work on just a H
// element. Like some IE6 magic and others.
getXHTMLWikiPrinter().printXMLStartElement("span");
}
@Override
public void endHeader(HeaderLevel level, String id, Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("span");
getXHTMLWikiPrinter().printXMLEndElement("h" + level.getAsInt());
}
@Override
public void onWord(String word)
{
getXHTMLWikiPrinter().printXML(word);
}
@Override
public void onSpace()
{
// The XHTML printer will decide whether to print a normal space or a
getXHTMLWikiPrinter().printSpace();
}
@Override
public void onSpecialSymbol(char symbol)
{
getXHTMLWikiPrinter().printXML(String.valueOf(symbol));
}
@Override
public void beginList(ListType type, Map parameters)
{
if (type == ListType.BULLETED) {
getXHTMLWikiPrinter().printXMLStartElement("ul", parameters);
} else {
getXHTMLWikiPrinter().printXMLStartElement("ol", parameters);
}
}
@Override
public void beginListItem()
{
getXHTMLWikiPrinter().setStandalone();
getXHTMLWikiPrinter().printXMLStartElement("li");
}
@Override
public void beginListItem(Map parameters)
{
getXHTMLWikiPrinter().printXMLStartElement("li", parameters);
}
@Override
public void endList(ListType type, Map parameters)
{
if (type == ListType.BULLETED) {
getXHTMLWikiPrinter().printXMLEndElement("ul");
} else {
getXHTMLWikiPrinter().printXMLEndElement("ol");
}
}
@Override
public void endListItem()
{
getXHTMLWikiPrinter().printXMLEndElement("li");
}
@Override
public void endListItem(Map parameters)
{
endListItem();
}
@Override
public void onId(String name)
{
// Don't use the "name" attribute (see http://www.w3.org/TR/html4/struct/links.html#h-12.2.3).
// If the id s in a paragraph use and if in a standalone block then use
// .
if (getBlockState().isInLine()) {
// Note: We're using and not since some browsers do not support the
// syntax (FF3) when the content type is set to HTML instead of XHTML.
getXHTMLWikiPrinter().printXMLStartElement("span", new String[][] { { "id", name } });
getXHTMLWikiPrinter().printXMLEndElement("span");
} else {
getXHTMLWikiPrinter().printXMLStartElement("div", new String[][] { { "id", name } });
getXHTMLWikiPrinter().printXMLEndElement("div");
}
}
@Override
public void onHorizontalLine(Map parameters)
{
getXHTMLWikiPrinter().printXMLElement("hr", parameters);
}
@Override
public void onVerbatim(String content, boolean inline, Map parameters)
{
if (inline) {
// Note: We generate a tt element rather than a pre element since pre elements cannot be located inside
// paragraphs for example. There also no tag in XHTML that has a semantic of preserving inline content so
// tt is the closest to pre for inline.
// The class is what is expected by wikimodel to understand the tt as meaning a verbatim and not a Monospace
// element.
getXHTMLWikiPrinter().printXMLStartElement("tt", new String[][] { { "class", "wikimodel-verbatim" } });
getXHTMLWikiPrinter().printXML(content);
getXHTMLWikiPrinter().printXMLEndElement("tt");
} else {
getXHTMLWikiPrinter().printXMLStartElement("pre", parameters);
getXHTMLWikiPrinter().printXML(content);
getXHTMLWikiPrinter().printXMLEndElement("pre");
}
}
@Override
public void onEmptyLines(int count)
{
// We need to use a special tag for empty lines since in XHTML the BR tag cannot be used outside of content
// tags.
// Note: We're using and not since some browsers do not support the syntax (FF3)
// when the content type is set to HTML instead of XHTML.
for (int i = 0; i < count; ++i) {
getXHTMLWikiPrinter().printXMLStartElement("div", new String[][] { { "class", "wikimodel-emptyline" } });
getXHTMLWikiPrinter().printXMLEndElement("div");
}
}
/**
* {@inheritDoc}
*
* @since 2.0RC1
*/
@Override
public void beginDefinitionList(Map parameters)
{
getXHTMLWikiPrinter().printXMLStartElement("dl", parameters);
}
/**
* {@inheritDoc}
*
* @since 2.0RC1
*/
@Override
public void endDefinitionList(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("dl");
}
@Override
public void beginDefinitionTerm()
{
getXHTMLWikiPrinter().printXMLStartElement("dt");
}
@Override
public void beginDefinitionDescription()
{
getXHTMLWikiPrinter().printXMLStartElement("dd");
}
@Override
public void endDefinitionTerm()
{
getXHTMLWikiPrinter().printXMLEndElement("dt");
}
@Override
public void endDefinitionDescription()
{
getXHTMLWikiPrinter().printXMLEndElement("dd");
}
@Override
public void beginQuotation(Map parameters)
{
if (getBlockState().isInQuotationLine()) {
getXHTMLWikiPrinter().printXMLEndElement("p");
}
getXHTMLWikiPrinter().printXMLStartElement("blockquote", parameters);
getXHTMLWikiPrinter().setStandalone();
getXHTMLWikiPrinter().printXMLStartElement("p");
}
@Override
public void endQuotation(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("p");
getXHTMLWikiPrinter().printXMLEndElement("blockquote");
if (getBlockState().isInQuotationLine()) {
getXHTMLWikiPrinter().printXMLStartElement("p");
}
}
@Override
public void beginQuotationLine()
{
// Send a new line if the previous event was endQuotationLine since we need to separate each quotation line
// or they'll printed next to each other and not on a new line each.
if (getBlockState().isInQuotation() && getBlockState().getPreviousEvent() == Event.QUOTATION_LINE) {
onNewLine();
}
}
@Override
public void beginTable(Map parameters)
{
getXHTMLWikiPrinter().printXMLStartElement("table", parameters);
}
@Override
public void beginTableRow(Map parameters)
{
getXHTMLWikiPrinter().printXMLStartElement("tr", parameters);
}
@Override
public void beginTableCell(Map parameters)
{
getXHTMLWikiPrinter().setStandalone();
getXHTMLWikiPrinter().printXMLStartElement("td", parameters);
}
@Override
public void beginTableHeadCell(Map parameters)
{
getXHTMLWikiPrinter().setStandalone();
// Find proper scope attribute value
Map parametersWithScope;
if (!parameters.containsKey("scope")) {
parametersWithScope = new LinkedHashMap(parameters);
if (getBlockState().getCellRow() == 0 || getBlockState().getCellCol() > 0) {
parametersWithScope.put("scope", "col");
} else {
parametersWithScope.put("scope", "row");
}
} else {
parametersWithScope = parameters;
}
// Write th element
getXHTMLWikiPrinter().printXMLStartElement("th", parametersWithScope);
}
@Override
public void endTable(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("table");
}
@Override
public void endTableRow(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("tr");
}
@Override
public void endTableCell(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("td");
}
@Override
public void endTableHeadCell(Map parameters)
{
getXHTMLWikiPrinter().printXMLEndElement("th");
}
/**
* {@inheritDoc}
*
* @since 2.5RC1
*/
@Override
public void onImage(ResourceReference reference, boolean freestanding, Map parameters)
{
onImage(reference, freestanding, null, parameters);
}
@Override
public void onImage(ResourceReference reference, boolean freestanding, String id, Map parameters)
{
// Ensure the image renderer is using the latest printer since the original printer used could have been
// superseded by another one in the printer stack.
this.imageRenderer.setXHTMLWikiPrinter(getXHTMLWikiPrinter());
// If the ResourceReference doesn't have a base reference specified, then look for one in previously sent
// events (it's sent in begin/endMetaData events).
List baseReferences = reference.getBaseReferences();
if (baseReferences.isEmpty()) {
reference.addBaseReferences(getMetaDataState().getAllMetaData(MetaData.BASE));
}
this.imageRenderer.onImage(reference, freestanding, id, parameters);
}
@Override
public void onRawText(String text, Syntax syntax)
{
// Directly inject the HTML content in the wiki printer (bypassing the XHTML printer)
if (SyntaxType.HTML_FAMILY_TYPES.contains(syntax.getType())) {
getXHTMLWikiPrinter().printRaw(text);
}
}
@Override
public void beginFigureCaption(Map parameters)
{
// We add a div to have some nice fallback (since
© 2015 - 2024 Weber Informatics LLC | Privacy Policy