/* * Copyright (C) 2003-2010 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.wiki.rendering.render.confluence; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.xwiki.rendering.renderer.printer.LookaheadWikiPrinter; import org.xwiki.rendering.renderer.printer.WikiPrinter; /** * Created by The eXo Platform SAS * Author : viet nguyen * viet.nguyen@exoplatform.com * Jul 2, 2010 */ /** * A Wiki printer that knows how to escape characters that would otherwise mean something different in Confluence wiki * syntax. For example if we have "*" as special symbols (and not as a Bold Format block) we need to escape them to * "~*" as otherwise they'd be considered bold after being rendered. */ public class ConfluenceSyntaxEscapeWikiPrinter extends LookaheadWikiPrinter { private static final Pattern VERBATIM_PATTERN = Pattern.compile("(\\{\\{\\{)|(\\}\\}\\})"); private ConfluenceSyntaxListenerChain listenerChain; private ConfluenceSyntaxEscapeHandler escapeHandler; private boolean escapeLastChar; private Pattern escapeFirstIfMatching; private String lastPrinted; public ConfluenceSyntaxEscapeWikiPrinter(WikiPrinter printer, ConfluenceSyntaxListenerChain listenerChain) { super(printer); this.escapeHandler = new ConfluenceSyntaxEscapeHandler(); this.listenerChain = listenerChain; } public ConfluenceSyntaxEscapeHandler getEscapeHandler() { return escapeHandler; } /** * {@inheritDoc} * * @see org.xwiki.rendering.renderer.printer.LookaheadWikiPrinter#printInternal(java.lang.String) */ @Override protected void printInternal(String text) { super.printInternal(text); int length = text.length(); if (length > 0) { this.escapeHandler.setOnNewLine(text.charAt(length - 1) == '\n'); } this.lastPrinted = text; } /** * {@inheritDoc} * * @see org.xwiki.rendering.renderer.printer.LookaheadWikiPrinter#println(java.lang.String) */ @Override protected void printlnInternal(String text) { super.printlnInternal(text); this.escapeHandler.setOnNewLine(true); this.lastPrinted = "\n"; } /** * {@inheritDoc} * * @see org.xwiki.rendering.renderer.printer.LookaheadWikiPrinter#flush() */ @Override public void flush() { if (getBuffer().length() > 0) { this.escapeHandler.escape(getBuffer(), this.listenerChain, this.escapeLastChar, this.escapeFirstIfMatching); super.flush(); } this.escapeLastChar = false; this.escapeFirstIfMatching = null; } public void printBeginBold() { flush(); boolean isOnNewLine = this.escapeHandler.isOnNewLine(); print("*"); if (isOnNewLine) { this.escapeFirstIfMatching = ConfluenceSyntaxEscapeHandler.STARLISTEND_PATTERN; } } public void setEscapeLastChar(boolean escapeLastChar) { this.escapeLastChar = escapeLastChar; } public void setBeforeLink(boolean beforeLink) { this.escapeHandler.setBeforeLink(beforeLink); } public void setOnNewLine(boolean onNewLine) { this.escapeHandler.setOnNewLine(onNewLine); } public boolean isOnNewLine() { return this.escapeHandler.isOnNewLine(); } public boolean isAfterWhiteSpace() { return isOnNewLine() || Character.isWhitespace(getLastPrinted().charAt(getLastPrinted().length() - 1)); } public String getLastPrinted() { return this.lastPrinted; } public void printBeginItalic() { // If the lookahead buffer is not empty and the last character is ":" then // we need to escape it since otherwise we would get "://" which could be confused for a URL. if (getBuffer().length() > 0 && getBuffer().charAt(getBuffer().length() - 1) == ':') { this.escapeLastChar = true; } print("_"); } public void printEndItalic() { // If the lookahead buffer is not empty and the last character is ":" then // we need to escape it since otherwise we would get "://" which could be confused for a URL. if (getBuffer().length() > 0 && getBuffer().charAt(getBuffer().length() - 1) == ':') { this.escapeLastChar = true; } print("_"); } public void printInlineMacro(String confluenceSyntaxText) { // If the lookahead buffer is not empty and the last character is "{" then // we need to escape it since otherwise we would get "{{{" which could be confused for a verbatim block. if (getBuffer().length() > 0 && getBuffer().charAt(getBuffer().length() - 1) == '{') { this.escapeLastChar = true; } print(confluenceSyntaxText); } public void printVerbatimContent(String verbatimContent) { StringBuffer result = new StringBuffer(); Stack<StringBuffer> subVerbatimStack = new Stack<StringBuffer>(); boolean printEndVerbatim = false; Matcher matcher = VERBATIM_PATTERN.matcher(verbatimContent); int currentIndex = 0; for (; matcher.find(); currentIndex = matcher.end()) { String before = verbatimContent.substring(currentIndex, matcher.start()); if (printEndVerbatim) { if (before.startsWith("}")) { result.append("~}~}~}"); } else { result.append("~}}}"); } } if (subVerbatimStack.size() == 0) { result.append(before); } else { subVerbatimStack.peek().append(before); } if (matcher.group(1) != null) { subVerbatimStack.push(new StringBuffer()); } else { if (subVerbatimStack.size() == 0) { printEndVerbatim = true; } else { StringBuffer subVerbatim = subVerbatimStack.pop(); if (subVerbatimStack.size() == 0) { result.append("{{{"); result.append(subVerbatim); result.append("}}}"); } else { subVerbatimStack.peek().append("{{{"); subVerbatimStack.peek().append(subVerbatim); subVerbatimStack.peek().append("}}}"); } } } } if (currentIndex == 0) { print(verbatimContent); return; } String end = verbatimContent.substring(currentIndex); if (printEndVerbatim) { if (end.length() == 0 || end.charAt(0) == '}') { result.append("~}~}~}"); } else { result.append("~}}}"); } } if (subVerbatimStack.size() > 0) { // Append remaining string subVerbatimStack.peek().append(end); // Escape not closed verbatim blocks while (subVerbatimStack.size() > 0) { StringBuffer subVerbatim = subVerbatimStack.pop(); if (subVerbatimStack.size() == 0) { if (subVerbatim.length() > 0 && subVerbatim.charAt(0) == '{') { result.append("~{~{~{"); } else { result.append("~{{{"); } result.append(subVerbatim); } else { if (subVerbatim.length() > 0 && subVerbatim.charAt(0) == '{') { subVerbatimStack.peek().append("~{~{~{"); } else { subVerbatimStack.peek().append("~{{{"); } subVerbatimStack.peek().append(subVerbatim); } } } else { // Append remaining string result.append(end); } print(result.toString()); } }