/* * Copyright 2011 OverZealous Creations, LLC * * Licensed 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 com.overzealous.remark; import java.util.HashSet; import java.util.Set; /** * This class is used to configure the Remark engine. * * Standard profiles have been created for a variety of Markdown processors, including: * <ul> * <li>{@link #markdown() Standard Markdown}</li> * <li>{@link #markdownExtra() PHP Markdown Extra}</li> * <li>{@link #multiMarkdown() MultiMarkdown}</li> * <li>{@link #pegdownBase() pegdown basic}</li> * <li>{@link #pegdownAllExtensions() pegdown with all extensions}</li> * <li>{@link #github() Github Flavored Markdown}</li> * </ul> * * @author Phil DeJarnett */ public class Options implements Cloneable { /** * Provides settings to control how Tables are converted. */ public enum Tables { /** * Remove all tables and their contents */ REMOVE(true,false,false,false,false), /** * Leave all tables and their contents as raw HTML (the default, as * this is the recommended syntax from Markdown) */ LEAVE_AS_HTML(false,true,false,false,false), /** * Convert tables to clean code block (compatible with the original Markdown) */ CONVERT_TO_CODE_BLOCK(false,false,true,true,true), /** * Convert tables to the syntax used by PHP Markdown Extra. * @see <a href="http://michelf.com/projects/php-markdown/extra/#table">PHP Markdown Extra Tables</a> */ MARKDOWN_EXTRA(false,false,true,false,false), /** * Convert tables to the syntax used by MultiMarkdown. * @see <a href="http://fletcher.github.com/peg-multimarkdown/#tables">MultiMarkdown Tables</a> */ MULTI_MARKDOWN(false,false,true,false,true); // Private fields private final boolean removed; private final boolean leftAsHtml; private final boolean convertedToText; private final boolean renderedAsCode; private final boolean colspanEnabled; private Tables(boolean removed, boolean leftAsHtml, boolean convertedToText, boolean renderedAsCode, boolean colspanEnabled) { this.removed = removed; this.leftAsHtml = leftAsHtml; this.convertedToText = convertedToText; this.renderedAsCode = renderedAsCode; this.colspanEnabled = colspanEnabled; } /** * True if the table is to be fully removed. * @return true or false */ public boolean isRemoved() { return removed; } /** * True if the table is to be left as raw HTML. * @return true or false */ public boolean isLeftAsHtml() { return leftAsHtml; } /** * True if the table is to be converted to plain text. * This is true if the result is a Markdown table or a code block. * @return true or false */ public boolean isConvertedToText() { return convertedToText; } /** * True if the table should be rendered as a code block * @return true or false */ public boolean isRenderedAsCode() { return renderedAsCode; } /** * True if the table is rendered as a MultiMarkdown table with column spanning. * @return true or false */ public boolean isColspanEnabled() { return colspanEnabled; } } /** * Provides settings to configure if fenced code blocks are used. */ public enum FencedCodeBlocks { /** * Completely disables fenced code blocks. */ DISABLED(false, ' '), /** * Enables fenced code blocks, using multiple {@code '~'} as the separator characters. */ ENABLED_TILDE(true, '~'), /** * Enables fenced code blocks, using multiple {@code '`'} as the separator characters. */ ENABLED_BACKTICK(true, '`'); // private fields private final boolean enabled; private final char separatorCharacter; private FencedCodeBlocks(boolean enabled, char separatorCharacter) { this.enabled = enabled; this.separatorCharacter = separatorCharacter; } /** * True if fenced code blocks are enabled * @return true or false */ public boolean isEnabled() { return enabled; } /** * Returns the separator character to use. * @return the separator character */ public char getSeparatorCharacter() { return separatorCharacter; } } /** * Provides options for how to handle in-word emphasis. */ public enum InWordEmphasis { /** * Uses the default mode, which allows in-word emphasis. Because Remark only uses * asterisks for spacing ({@code '*'}), this mode works with parsers that disable * in-word underscores ({@code '_'}) but not in-word asterisks. */ NORMAL(true, false), /** * Adds spaces around the in-word emphasis characters. This will actually render different output. * * For example, {@code My<em>Example</em>Word} becomes {@code My *Example* Word}. This will actually * render as {@code My <em>Example</em> Word}. */ ADD_SPACES(true, true), /** * Removes in-word emphasis altogether. Designed for parsers that do not allow in-word asterisks * or in-word underscores. * * This means that {@code My<em>Example</em>Word} becomes {@code MyExampleWord}. * */ REMOVE_EMPHASIS(false, false); private final boolean emphasisPreserved; private final boolean additionalSpacingNeeded; InWordEmphasis(boolean emphasisPreserved, boolean additionalSpacingNeeded) { this.emphasisPreserved = emphasisPreserved; this.additionalSpacingNeeded = additionalSpacingNeeded; } /** * Returns whether or not to preserve emphasis at all. * @return true if emphasis should be preserved, false to remove it altogether. */ public boolean isEmphasisPreserved() { return emphasisPreserved; } /** * Returns true when Remark should add spaces around the emphasis characters. * @return true if spaces should be added. */ public boolean isAdditionalSpacingNeeded() { return additionalSpacingNeeded; } } /** * Creates and returns a new Options set with the default options * compatible with the original Markdown. * * @return Options for original Markdown compatibility */ public static Options markdown() { return new Options(); } /** * Creates and returns a new Options set with the default options * compatible with PHP Markdown Extra features. * * <p>Enables:</p> * <ul> * <li>headerIDs</li> * <li>Markdown Extra fencedCodeBlocks</li> * <li>Markdown Extra tables</li> * <li>definitionLists</li> * <li>abbreviations</li> * </ul> * * @return Options for PHP Markdown Extra compatibility */ public static Options markdownExtra() { Options opts = new Options(); opts.headerIds = true; opts.fencedCodeBlocks = FencedCodeBlocks.ENABLED_TILDE; opts.tables = Tables.MARKDOWN_EXTRA; opts.definitionLists = true; opts.abbreviations = true; return opts; } /** * Creates and returns a new Options set with the default options * compatible with MultiMarkdown features. * * <p>Enables:</p> * <ul> * <li>MultiMarkdown tables</li> * <li>definitionLists</li> * </ul> * * @return Options for MultiMarkdown compatibility */ public static Options multiMarkdown() { Options opts = new Options(); opts.tables = Tables.MULTI_MARKDOWN; opts.definitionLists = true; return opts; } /** * Creates and returns a new Options set with the default options * compatible with the base pegdown configuration. * * <p>Please note: if you are using pegdown version 1.0.2 or older, you'll need to * manually enable {@link #fixPegdownStrongEmphasisInLinks}.</p> * * <p>Enables:</p> * <ul> * <li>hardwraps</li> * </ul> * * @return Options for pegdown compatibility */ @SuppressWarnings({"WeakerAccess"}) public static Options pegdownBase() { Options opts = new Options(); opts.inWordEmphasis = InWordEmphasis.REMOVE_EMPHASIS; return opts; } /** * Creates and returns a new Options set with the default options * compatible with pegdown configured with all extensions. * * <p>Please note: if you are using pegdown version 1.0.2 or older, you'll need to * manually enable {@link #fixPegdownStrongEmphasisInLinks}.</p> * * <p>Enables:</p> * <ul> * <li>hardwraps</li> * <li>Markdown Extra fencedCodeBlocks</li> * <li>MultiMarkdown tables</li> * <li>definitionLists</li> * <li>abbreviations</li> * <li>autoLinks</li> * <li>reverses all Smart options</li> * </ul> * * @return Options for pegdown compatibility */ public static Options pegdownAllExtensions() { Options opts = pegdownBase(); opts.hardwraps = true; opts.fencedCodeBlocks = FencedCodeBlocks.ENABLED_TILDE; opts.reverseHtmlSmartPunctuation = true; opts.reverseHtmlSmartQuotes = true; opts.reverseUnicodeSmartPunctuation = true; opts.reverseUnicodeSmartQuotes = true; opts.autoLinks = true; opts.tables = Tables.MULTI_MARKDOWN; opts.definitionLists = true; opts.abbreviations = true; return opts; } /** * Creates and returns a new Options set with the default options * compatible with github-flavored Markdown. * * <p>Enables:</p> * <ul> * <li>hardwraps</li> * <li>Github fencedCodeBlocks</li> * <li>tables converted to code block</li> * <li>autoLinks</li> * </ul> * * @return Options for github compatibility */ public static Options github() { Options opts = new Options(); opts.hardwraps = true; opts.fencedCodeBlocks = FencedCodeBlocks.ENABLED_BACKTICK; opts.autoLinks = true; opts.tables = Tables.CONVERT_TO_CODE_BLOCK; return opts; } /** * If true, {@code <br/>}s are replaced with a simple linebreak. * <p>If false, {@code <br/>}s are replaced with a two spaces followed by a linebreak (default).</p> */ public boolean hardwraps = false; /** * Configures how in-word emphasis is handled. */ public InWordEmphasis inWordEmphasis = InWordEmphasis.NORMAL; /** * If true, relative links are preserved. <strong>You must still provide a baseURI!</strong> * <p>Otherwise, relative links are resolved against the provided baseURI (the default).</p> */ public boolean preserveRelativeLinks = false; /** * If true, place the URLs for links inline. * <p>Otherwise, generate link IDs and place at the end (the default).</p> */ public boolean inlineLinks = false; /** * If true, link IDs are simply incremented as they are found. * <p>Otherwise, Remark attempts to generate unique link IDs based on the link description.</p> */ public boolean simpleLinkIds = false; /** * Configures how tables are handled. */ public Tables tables = Tables.LEAVE_AS_HTML; /** * If true, replace all smart quote HTML entities (e.g: * {@code “} with simplified characters (e.g: {@code "}). */ public boolean reverseHtmlSmartQuotes = false; /** * If true, replace all smart quote unicode characters (e.g: * “) with simplified characters (e.g: {@code "}). */ public boolean reverseUnicodeSmartQuotes = false; /** * If true, replace all smart punctuation HTML entities (e.g: * {@code &emdash;}) with simplified characters (e.g: {@code ---}). */ public boolean reverseHtmlSmartPunctuation = false; /** * If true, replace all smart punctuation unicode characters (e.g: * —) with simplified characters (e.g: {@code ---}). */ public boolean reverseUnicodeSmartPunctuation = false; /** * If true, enable remarking definitions lists. When enabled, definition * lists ({@code <dl>}, {@code <dt>}, and {@code <dd>}) * are converted into <a href="http://michelf.com/projects/php-markdown/extra/#def-list">PHP Markdown Extra style definition lists</a>. */ public boolean definitionLists = false; /** * If true, enable remarking abbreviations. When enabled, {@code <abbr>} tags are converted * into <a href="http://michelf.com/projects/php-markdown/extra/#abbr">PHP Markdown Extra style abbreviations</a>. */ public boolean abbreviations = false; /** * If true, enable autoLinks. This only affects links whose {@code href} attribute is the same * as the node's inner text content. In this case, the URL is simply written directly to the * output. * * Example: * <blockquote>{@code <a href="http://www.example.com">http://www.example.com</a>}</blockquote> * becomes * <blockquote>{@code http://www.example.com}</blockquote> */ public boolean autoLinks = false; /** * If true, enable remarking header IDs. When enabled, the ID of header tags will be converted * into <a href="http://michelf.com/projects/php-markdown/extra/#header-id">PHP Markdown Extra style header IDs</a>. */ public boolean headerIds = false; /** * Configures how to handle code blocks. By default, code blocks are only configured * using the indented format supported by Markdown. This allows fenced code blocks when necessary. */ @SuppressWarnings({"WeakerAccess"}) public FencedCodeBlocks fencedCodeBlocks = FencedCodeBlocks.DISABLED; /** * Number of times to repeat the fencedCodeBlock character. (Defaults to 10.) */ @SuppressWarnings({"CanBeFinal"}) public int fencedCodeBlocksWidth = 10; /** * Allows the addition of extra HTML tags that can be left in the output. * Please note that this does not override default handling (for example, {@code <em>} tags). */ @SuppressWarnings({"WeakerAccess"}) public Set<IgnoredHtmlElement> ignoredHtmlElements = new HashSet<IgnoredHtmlElement>(); /** * This is a very specific fix for a very specific bug. As of version 1.0.2, pegdown has a serious bug that * really slows down when processing many links with <strong>bold</strong> and <em>italics</em> in the label for * an anchor. * * This option causes Remark to replace items like {@code [***my important link***][link-id]} with just * bold text, like {@code [**my important link**][link-id]}. * * <strong>Note: this was fixed in release 1.1.0!</strong> * * @see <a href="https://github.com/sirthias/pegdown/issues/34">Pegdown Issue #34</a> */ @SuppressWarnings({"CanBeFinal"}) public boolean fixPegdownStrongEmphasisInLinks = false; /** * Configures a default set of options. * The default set is configured to be most compatible with the original Markdown syntax. */ public Options() { } /** * Always returns a non-null setting for FencedCodeBlocks * @return The current FencedCodeBlocks or default if not set. */ public FencedCodeBlocks getFencedCodeBlocks() { if(fencedCodeBlocks == null) { fencedCodeBlocks = FencedCodeBlocks.DISABLED; } return fencedCodeBlocks; } /** * Always returns a non-null setting for IgnoredHtmlElements * @return The current set of IgnoredHtmlElements or an empty set if null. */ public Set<IgnoredHtmlElement> getIgnoredHtmlElements() { if(ignoredHtmlElements == null) { ignoredHtmlElements = new HashSet<IgnoredHtmlElement>(); } return ignoredHtmlElements; } /** * Always returns a non-null setting for Tables * @return the current Tables or default if not set. */ public Tables getTables() { if(tables == null) { tables = Tables.LEAVE_AS_HTML; } return tables; } /** * Always returns a non-null setting for InWordEmphasis * @return the current InWordEmphasis or default if not set. */ public InWordEmphasis getInWordEmphasis() { if(inWordEmphasis == null) { inWordEmphasis = InWordEmphasis.NORMAL; } return inWordEmphasis; } /** * Utility method to set reversing of both unicode and html * smart quotes. * * @param reverse true if they should be reversed */ public void setReverseSmartQuotes(boolean reverse) { this.reverseHtmlSmartQuotes = reverse; this.reverseUnicodeSmartQuotes = reverse; } /** * Utility method to set reversing of both unicode and html * smart punctuation. * * @param reverse true if they should be reversed */ public void setReverseSmartPunctuation(boolean reverse) { this.reverseHtmlSmartPunctuation = reverse; this.reverseUnicodeSmartPunctuation = reverse; } /** * Utility method to set reversing of both unicode and html * smart quotes and punctuation. * * @param reverse true if they should be reversed */ @SuppressWarnings({"UnusedDeclaration"}) public void setReverseAllSmarts(boolean reverse) { setReverseSmartQuotes(reverse); setReverseSmartPunctuation(reverse); } public Options getCopy() { Options copy; try { copy = (Options)this.clone(); } catch(CloneNotSupportedException e) { throw new RuntimeException("Should never happen"); } return copy; } }