/* * Copyright (c) JForum Team. All rights reserved. * * The software in this package is published under the terms of the LGPL * license a copy of which has been included with this distribution in the * license.txt file. * * The JForum Project * http://www.jforum.net */ package net.jforum.formatters; import java.util.LinkedHashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import br.com.caelum.vraptor.ioc.ApplicationScoped; import br.com.caelum.vraptor.ioc.Component; /** * Applies formatting for all contents of bb_config.xml. * Before using this class, all bb codes you want to be * processed should be added through the addBb() method. * @author Rafael Steil */ @Component @ApplicationScoped public class BBConfigFormatter implements Formatter { private Map<String, BBCode> bbTags = new LinkedHashMap<String, BBCode>(); private Map<String, BBCode> alwaysProcessTags = new LinkedHashMap<String, BBCode>(); public BBConfigFormatter() { BBCodeConfigParser parser = new BBCodeConfigParser(this); parser.parse(); } public void addBb(BBCode code) { if (code.alwaysProcess()) { this.alwaysProcessTags.put(code.getTagName(), code); } else { this.bbTags.put(code.getTagName(), code); } } /** * @see net.jforum.formatters.Formatter#format(java.lang.String, net.jforum.formatters.PostOptions) */ @Override public String format(String text, PostOptions postOptions) { boolean hasTags = this.hasTags(text); if (postOptions.isBbCodeEnabled() && hasTags) { text = this.processBB(text, postOptions); } text = this.formatAlwaysProcessBBCodes(text); return text; } /** * Applies bb code formatting to the text * * @param text the contents to be formatted * @param options post options * @return the formatted text */ private String processBB(String text, PostOptions options) { String startCodeFragment = "[code"; String endCodeFragment = "[/code]"; int codeIndex = text.indexOf(startCodeFragment); int codeEndIndex = codeIndex > -1 ? text.indexOf(endCodeFragment) : -1; if (codeIndex == -1 || codeEndIndex == -1 || codeEndIndex < codeIndex) { text = this.processBBExceptCodeTag(text, options); } else { int nextStartPos = 0; StringBuilder result = new StringBuilder(text.length()); // Applies formatting in steps, as all contents inside a [code] tag // should be considered as plain text, thus not being elegible for processing while (codeIndex > -1 && codeEndIndex > -1 && codeEndIndex > codeIndex) { codeEndIndex += endCodeFragment.length(); // Format only the text between [code] and [/code] String codeResult = this.processCodeTag(text.substring(codeIndex, codeEndIndex)); // Format the text before [code] String nonCodeResult = this.processBBExceptCodeTag(text.substring(nextStartPos, codeIndex), options); result.append(nonCodeResult).append(codeResult); nextStartPos = codeEndIndex; codeIndex = text.indexOf(startCodeFragment, codeEndIndex); codeEndIndex = codeIndex > -1 ? text.indexOf(endCodeFragment, codeIndex) : -1; } if (nextStartPos > -1) { String nonCodeResult = processBBExceptCodeTag(text.substring(nextStartPos), options); result.append(nonCodeResult); } text = result.toString(); } return text; } /** * Formats only the [code] tag * * @param text the text to format * @return the formatted text */ private String processCodeTag(String text) { for (BBCode bb : this.bbTags.values()) { // There is "code" and "code-highlight" if (bb.getTagName().startsWith("code")) { Matcher matcher = Pattern.compile(bb.getRegex()).matcher(text); StringBuilder sb = new StringBuilder(text); while (matcher.find()) { String lang = null; String contents = null; if ("code".equals(bb.getTagName())) { contents = matcher.group(1); } else { lang = matcher.group(1); contents = matcher.group(2); } contents = StringUtils.replace(contents, "<br/> ", "\n"); // XML-like tags contents = StringUtils.replace(contents, "<", "<"); contents = StringUtils.replace(contents, ">", ">"); // Note: there is no replacing for spaces and tabs as // we are relying on the Javascript SyntaxHighlighter library // to do it for us StringBuilder replace = new StringBuilder(bb.getReplace()); int index = replace.indexOf("$1"); if ("code".equals(bb.getTagName())) { if (index > -1) { replace.replace(index, index + 2, contents.toString()); } index = sb.indexOf("[code]"); } else { if (index > -1) { replace.replace(index, index + 2, lang.toString()); } index = replace.indexOf("$2"); if (index > -1) { replace.replace(index, index + 2, contents.toString()); } index = sb.indexOf("[code="); } int lastIndex = sb.indexOf("[/code]", index) + "[/code]".length(); if (lastIndex > index) { sb.replace(index, lastIndex, replace.toString()); } } text = sb.toString(); } } return text; } /** * Process all bb codes, except [code] * * @param text the text to format * @param options post options * @return the formatted text */ private String processBBExceptCodeTag(String text, PostOptions options) { for (BBCode bb : this.bbTags.values()) { if (!bb.getTagName().startsWith("code")) { text = text.replaceAll(bb.getRegex(), bb.getReplace()); } } return text; } /** * Process the bb tags that always should be applied * * @param text the text to format * @return the formatted text */ private String formatAlwaysProcessBBCodes(String text) { for (BBCode bb : this.alwaysProcessTags.values()) { text = text.replaceAll(bb.getRegex(), bb.getReplace()); } return text; } /** * Checks if there is any bb tag in the text * * @param text the text to search for tags * @return true if there is at least one tag in the text */ private boolean hasTags(String text) { return text.indexOf('[') > -1 && text.indexOf(']') > -1; } }