/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.seam.wiki.core.wikitext.engine;
import antlr.ANTLRException;
import antlr.SemanticException;
import antlr.Token;
import org.jboss.seam.text.SeamTextLexer;
import org.jboss.seam.text.SeamTextParser;
import org.jboss.seam.wiki.core.model.*;
import org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer;
import java.io.StringReader;
import java.util.*;
/**
* Parses SeamText markup and also resolves link and macro tags as wiki links and wiki plugins.
* <p>
* Don't forget to set the resolver and renderer base with <tt>setCurrentAreaNumber()</tt>!
* </p><p>
* Picks the <tt>WikiLinkResolver</tt> present in the contextual variable <tt>wikiLinkResolver</tt>. Calls
* out to a <tt>WikiTextRender</tt> for the actual in-document rendering of wiki links and wiki plugins. Might update
* the <tt>currentDocument</tt>'s content, this change should be flushed to the datastore after calling
* the parser.
* </p><p>
* After parsing, all links to attachments and all external links are pushed onto the renderer, where they
* can be used to render an attachment list or appendixes to the text.
* </p>
*
* @see org.jboss.seam.wiki.core.wikitext.renderer.WikiTextRenderer
*
* @author Christian Bauer
*/
public class WikiTextParser extends SeamTextParser {
private int macroPosition = 0;
private WikiTextRenderer renderer;
private boolean resolveLinks;
private WikiLinkResolver resolver;
private Long currentAreaNumber;
private Map<String, WikiLink> resolvedLinks = new HashMap<String, WikiLink>();
private List<WikiLink> attachments = new ArrayList<WikiLink>();
private List<WikiLink> externalLinks = new ArrayList<WikiLink>();
private Set<String> macroNames = new HashSet<String>();
private boolean renderDuplicateMacros;
public WikiTextParser(String wikiText, boolean renderDuplicateMacros, boolean resolveLinks) {
super(new SeamTextLexer(new StringReader(wikiText)));
this.renderDuplicateMacros = renderDuplicateMacros;
this.resolveLinks = resolveLinks;
setSanitizer(
new DefaultSanitizer() {
@Override
public void validateLinkTagURI(Token token, String s) throws SemanticException {
// NOOP, we validate that later in linkTag()
}
}
);
}
/**
* Mandatory, you need to set a renderer before starting the parer.
*
* @param renderer an implementation of WikiTextRenderer
* @return the called instance
*/
public WikiTextParser setRenderer(WikiTextRenderer renderer) {
this.renderer = renderer;
return this;
}
/**
* Mandatory, you need to set a resolver before starting the parer.
*
* @param resolver an implementation of WikiLinkresolver
* @return the called instance
*/
public WikiTextParser setResolver(WikiLinkResolver resolver) {
this.resolver = resolver;
return this;
}
/*
* The render/link resolving base
* @return the called instance
*/
public void setCurrentAreaNumber(Long currentAreaNumber) {
this.currentAreaNumber = currentAreaNumber;
}
/**
* Start parsing the wiki text and resolve wiki links and wiki plugins.
* <p>
* @throws ANTLRException if lexer or parser errors occur, see
*/
public void parse() throws ANTLRException {
if (renderer == null) throw new IllegalStateException("WikiTextParser requires not null setRenderer()");
if (resolveLinks) {
if (resolver == null) throw new IllegalStateException("WikiTextParser requires not null setResolver()");
if (currentAreaNumber == null) throw new IllegalStateException("WikiTextParser requires not null setCurrentAreaNumber()");
}
startRule();
renderer.setAttachmentLinks(attachments);
renderer.setExternalLinks(externalLinks);
}
protected String linkTag(String descriptionText, String linkText) {
if (!resolveLinks) {
// Don't resolve links, just call back to renderer for simple inline rendering of what we have
WikiLink unresolvedLink = new WikiLink(false, false);
unresolvedLink.setDescription(descriptionText);
unresolvedLink.setUrl(linkText);
return renderer.renderInternalLink(unresolvedLink);
}
resolver.resolveLinkText(currentAreaNumber, resolvedLinks, linkText);
WikiLink link = resolvedLinks.get((linkText));
if (link == null) return "";
// Override the description of the WikiLink with description found in tag
String finalDescriptionText =
(descriptionText!=null && descriptionText.length() > 0 ? descriptionText : link.getDescription());
link.setDescription(finalDescriptionText);
// Link to upload (inline or attached)
if (link.getFile() != null && link.getFile().isInstance(WikiUpload.class)) {
WikiUpload upload = (WikiUpload)link.getFile();
if (upload.isAttachedToDocuments()) {
if (!attachments.contains(link)) {
attachments.add(link);
}
return renderer.renderFileAttachmentLink((attachments.indexOf(link)+1), link);
} else {
return renderer.renderThumbnailImageLink(link);
}
}
// External link
if (link.isExternal()) {
if (!externalLinks.contains(link)) externalLinks.add(link);
return renderer.renderExternalLink(link);
}
// Regular link
return renderer.renderInternalLink(link);
}
protected String paragraphOpenTag() {
return renderer.renderParagraphOpenTag();
}
protected String preformattedText(String s) {
return renderer.preformattedText(s);
}
protected String blockquoteOpenTag() {
return renderer.renderBlockquoteOpenTag();
}
protected String headline1(String headline) {
return renderer.renderHeadline1(headline);
}
protected String headline2(String headline) {
return renderer.renderHeadline2(headline);
}
protected String headline3(String headline) {
return renderer.renderHeadline3(headline);
}
protected String headline4(String headline) {
return renderer.renderHeadline4(headline);
}
protected String orderedListOpenTag() {
return renderer.renderOrderedListOpenTag();
}
protected String orderedListItemOpenTag() {
return renderer.renderOrderedListItemOpenTag();
}
protected String unorderedListOpenTag() {
return renderer.renderUnorderedListOpenTag();
}
protected String unorderedListItemOpenTag() {
return renderer.renderUnorderedListItemOpenTag();
}
protected String emphasisOpenTag() {
return renderer.renderEmphasisOpenTag();
}
protected String emphasisCloseTag() {
return renderer.renderEmphasisCloseTag();
}
protected String macroInclude(Macro macro) {
if (macro.name == null || macro.name.length() == 0) return "";
if ( (macroNames.contains(macro.name) && renderDuplicateMacros) || !macroNames.contains(macro.name)) {
macroNames.add(macro.name);
WikiTextMacro wikiTextMacro = new WikiTextMacro(macro.name, macroPosition++);
wikiTextMacro.setParams(macro.params);
return renderer.renderMacro(wikiTextMacro);
} else {
macroPosition++;
return "[Can't use the same macro twice!]";
}
}
}