All Downloads are FREE. Search and download functionalities are using the official Maven repository.

panda.doc.markdown.Processor Maven / Gradle / Ivy

Go to download

Panda Core is the core module of Panda Framework, it contains commonly used utility classes similar to apache-commons.

There is a newer version: 1.8.0
Show newest version
package panda.doc.markdown;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import panda.io.stream.CharSequenceReader;

/**
 * Markdown processor class.
 * 

* Example usage: *

* *
 * String result = Processor.process("This is ***TXTMARK***");
 * 
 * 
*/ public class Processor { /** The reader. */ private final Reader reader; /** The emitter. */ private final Emitter emitter; /** The Configuration. */ final Configuration config; /** Extension flag. */ private boolean useExtensions = false; /** * Constructor. * * @param reader the input reader. * @param config the configuration */ protected Processor(final Reader reader, final Configuration config) { this.reader = reader; this.config = config; this.useExtensions = config.forceExtendedProfile; this.emitter = new Emitter(this.config); } /** * Transforms an input stream into HTML using the given Configuration. * * @param reader The Reader to process. * @param configuration The Configuration. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration */ public final static String process(final Reader reader, final Configuration configuration) throws IOException { final Processor p = new Processor(!(reader instanceof BufferedReader) ? new BufferedReader(reader) : reader, configuration); return p.process(); } /** * Transforms an input String into HTML using the given Configuration. * * @param input The String to process. * @param configuration The Configuration. * @return The processed String. * @see Configuration */ public final static String process(final CharSequence input, final Configuration configuration) { try { return process(new CharSequenceReader(input), configuration); } catch (IOException e) { // This _can never_ happen return null; } } /** * Transforms an input file into HTML using the given Configuration. * * @param file The File to process. * @param configuration The Contifuration. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration */ public final static String process(final File file, final Configuration configuration) throws IOException { final FileInputStream input = new FileInputStream(file); final String ret = process(input, configuration); input.close(); return ret; } /** * Transforms an input stream into HTML using the given Configuration. * * @param input The InputStream to process. * @param configuration The Configuration. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration */ public final static String process(final InputStream input, final Configuration configuration) throws IOException { final Processor p = new Processor(new BufferedReader(new InputStreamReader(input, configuration.encoding)), configuration); return p.process(); } /** * Transforms an input String into HTML using the default Configuration. * * @param input The String to process. * @return The processed String. * @see Configuration#DEFAULT */ public final static String process(final String input) { return process(input, Configuration.DEFAULT); } /** * Transforms an input String into HTML. * * @param input The String to process. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @see Configuration#DEFAULT */ public final static String process(final String input, final boolean safeMode) { return process(input, Configuration.builder().setSafeMode(safeMode).build()); } /** * Transforms an input String into HTML. * * @param input The String to process. * @param decorator The decorator to use. * @return The processed String. * @see Configuration#DEFAULT */ public final static String process(final String input, final Decorator decorator) { return process(input, Configuration.builder().setDecorator(decorator).build()); } /** * Transforms an input String into HTML. * * @param input The String to process. * @param decorator The decorator to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @see Configuration#DEFAULT */ public final static String process(final String input, final Decorator decorator, final boolean safeMode) { return process(input, Configuration.builder().setDecorator(decorator).setSafeMode(safeMode).build()); } /** * Transforms an input file into HTML using the default Configuration. * * @param file The File to process. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file) throws IOException { return process(file, Configuration.DEFAULT); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final boolean safeMode) throws IOException { return process(file, Configuration.builder().setSafeMode(safeMode).build()); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param decorator The decorator to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final Decorator decorator) throws IOException { return process(file, Configuration.builder().setDecorator(decorator).build()); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param decorator The decorator to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final Decorator decorator, final boolean safeMode) throws IOException { return process(file, Configuration.builder().setDecorator(decorator).setSafeMode(safeMode).build()); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param encoding The encoding to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final String encoding) throws IOException { return process(file, Configuration.builder().setEncoding(encoding).build()); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param encoding The encoding to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final String encoding, final boolean safeMode) throws IOException { return process(file, Configuration.builder().setEncoding(encoding).setSafeMode(safeMode).build()); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param encoding The encoding to use. * @param decorator The decorator to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final String encoding, final Decorator decorator) throws IOException { return process(file, Configuration.builder().setEncoding(encoding).setDecorator(decorator).build()); } /** * Transforms an input file into HTML. * * @param file The File to process. * @param encoding The encoding to use. * @param decorator The decorator to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final File file, final String encoding, final Decorator decorator, final boolean safeMode) throws IOException { return process(file, Configuration.builder().setEncoding(encoding).setSafeMode(safeMode) .setDecorator(decorator).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input) throws IOException { return process(input, Configuration.DEFAULT); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final boolean safeMode) throws IOException { return process(input, Configuration.builder().setSafeMode(safeMode).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param decorator The decorator to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final Decorator decorator) throws IOException { return process(input, Configuration.builder().setDecorator(decorator).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param decorator The decorator to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final Decorator decorator, final boolean safeMode) throws IOException { return process(input, Configuration.builder().setDecorator(decorator).setSafeMode(safeMode).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param encoding The encoding to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final String encoding) throws IOException { return process(input, Configuration.builder().setEncoding(encoding).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param encoding The encoding to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final String encoding, final boolean safeMode) throws IOException { return process(input, Configuration.builder().setEncoding(encoding).setSafeMode(safeMode).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param encoding The encoding to use. * @param decorator The decorator to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final String encoding, final Decorator decorator) throws IOException { return process(input, Configuration.builder().setEncoding(encoding).setDecorator(decorator).build()); } /** * Transforms an input stream into HTML. * * @param input The InputStream to process. * @param encoding The encoding to use. * @param decorator The decorator to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final InputStream input, final String encoding, final Decorator decorator, final boolean safeMode) throws IOException { return process(input, Configuration.builder().setEncoding(encoding).setDecorator(decorator).setSafeMode(safeMode).build()); } /** * Transforms an input stream into HTML using the default Configuration. * * @param reader The Reader to process. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final Reader reader) throws IOException { return process(reader, Configuration.DEFAULT); } /** * Transforms an input stream into HTML. * * @param reader The Reader to process. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final Reader reader, final boolean safeMode) throws IOException { return process(reader, Configuration.builder().setSafeMode(safeMode).build()); } /** * Transforms an input stream into HTML. * * @param reader The Reader to process. * @param decorator The decorator to use. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final Reader reader, final Decorator decorator) throws IOException { return process(reader, Configuration.builder().setDecorator(decorator).build()); } /** * Transforms an input stream into HTML. * * @param reader The Reader to process. * @param decorator The decorator to use. * @param safeMode Set to true to escape unsafe HTML tags. * @return The processed String. * @throws IOException if an IO error occurs * @see Configuration#DEFAULT */ public final static String process(final Reader reader, final Decorator decorator, final boolean safeMode) throws IOException { return process(reader, Configuration.builder().setDecorator(decorator).setSafeMode(safeMode).build()); } /** * Reads all lines from our reader. *

* Takes care of markdown link references. *

* * @return A Block containing all lines. * @throws IOException If an IO error occurred. */ private Block readLines() throws IOException { final Block block = new Block(); final StringBuilder sb = new StringBuilder(80); int c = this.reader.read(); LinkRef lastLinkRef = null; while (c != -1) { sb.setLength(0); int pos = 0; boolean eol = false; while (!eol) { switch (c) { case -1: eol = true; break; case '\n': c = this.reader.read(); if (c == '\r') c = this.reader.read(); eol = true; break; case '\r': c = this.reader.read(); if (c == '\n') c = this.reader.read(); eol = true; break; case '\t': { final int np = pos + (4 - (pos & 3)); while (pos < np) { sb.append(' '); pos++; } c = this.reader.read(); break; } default: pos++; sb.append((char)c); c = this.reader.read(); break; } } final Line line = new Line(); line.value = sb.toString(); line.init(); // Check for link definitions boolean isLinkRef = false; String id = null, link = null, comment = null; if (!line.isEmpty && line.leading < 4 && line.value.charAt(line.leading) == '[') { line.pos = line.leading + 1; // Read ID up to ']' id = line.readUntil(']'); // Is ID valid and are there any more characters? if (id != null && line.pos + 2 < line.value.length()) { // Check for ':' ([...]:...) if (line.value.charAt(line.pos + 1) == ':') { line.pos += 2; line.skipSpaces(); // Check for link syntax if (line.pos < line.value.length() && line.value.charAt(line.pos) == '<') { line.pos++; link = line.readUntil('>'); line.pos++; } else { link = line.readUntil(' ', '\n'); } // Is link valid? if (link != null) { // Any non-whitespace characters following? if (line.skipSpaces()) { final char ch = line.value.charAt(line.pos); // Read comment if (ch == '\"' || ch == '\'' || ch == '(') { line.pos++; comment = line.readUntil(ch == '(' ? ')' : ch); // Valid linkRef only if comment is valid if (comment != null) isLinkRef = true; } } else { isLinkRef = true; } } } } } // To make compiler happy: add != null checks if (isLinkRef && id != null && link != null) { if (id.toLowerCase().equals("$profile$")) { this.emitter.useExtensions = this.useExtensions = link.toLowerCase().equals("extended"); lastLinkRef = null; } else { // Store linkRef and skip line final LinkRef lr = new LinkRef(link, comment, comment != null && (link.length() == 1 && link.charAt(0) == '*')); this.emitter.addLinkRef(id, lr); if (comment == null) lastLinkRef = lr; } } else { comment = null; // Check for multi-line linkRef if (!line.isEmpty && lastLinkRef != null) { line.pos = line.leading; final char ch = line.value.charAt(line.pos); if (ch == '\"' || ch == '\'' || ch == '(') { line.pos++; comment = line.readUntil(ch == '(' ? ')' : ch); } if (comment != null) lastLinkRef.title = comment; lastLinkRef = null; } // No multi-line linkRef, store line if (comment == null) { line.pos = 0; block.appendLine(line); } } } return block; } /** * Initializes a list block by separating it into list item blocks. * * @param root The Block to process. */ private void initListBlock(final Block root) { Line line = root.lines; line = line.next; while (line != null) { final LineType t = line.getLineType(this.useExtensions); if ((t == LineType.OLIST || t == LineType.ULIST) || (!line.isEmpty && (line.prevEmpty && line.leading == 0 && !(t == LineType.OLIST || t == LineType.ULIST)))) { root.split(line.previous).type = BlockType.LIST_ITEM; } line = line.next; } root.split(root.lineTail).type = BlockType.LIST_ITEM; } /** * Recursively process the given Block. * * @param root The Block to process. * @param listMode Flag indicating that we're in a list item block. */ private void recurse(final Block root, boolean listMode) { Block block, list; Line line = root.lines; if (listMode) { root.removeListIndent(this.useExtensions); if (this.useExtensions && root.lines != null && root.lines.getLineType(this.useExtensions) != LineType.CODE) { root.id = root.lines.stripID(); } } while (line != null && line.isEmpty) line = line.next; if (line == null) return; while (line != null) { final LineType type = line.getLineType(this.useExtensions); switch (type) { case OTHER: { final boolean wasEmpty = line.prevEmpty; while (line != null && !line.isEmpty) { final LineType t = line.getLineType(this.useExtensions); if ((listMode || this.useExtensions) && (t == LineType.OLIST || t == LineType.ULIST)) break; if (this.useExtensions && (t == LineType.CODE || t == LineType.FENCED_CODE || t == LineType.TABLE || t == LineType.TABLEB || t == LineType.PLUGIN)) break; if (t == LineType.HEADLINE || t == LineType.HEADLINE1 || t == LineType.HEADLINE2 || t == LineType.HR || t == LineType.BQUOTE || t == LineType.XML) break; line = line.next; } final BlockType bt; if (line != null && !line.isEmpty) { bt = (listMode && !wasEmpty) ? BlockType.NONE : BlockType.PARAGRAPH; root.split(line.previous).type = bt; root.removeLeadingEmptyLines(); } else { bt = (listMode && (line == null || !line.isEmpty) && !wasEmpty) ? BlockType.NONE : BlockType.PARAGRAPH; root.split(line == null ? root.lineTail : line).type = bt; root.removeLeadingEmptyLines(); } line = root.lines; break; } case CODE: while (line != null && (line.isEmpty || line.leading > 3)) { line = line.next; } block = root.split(line != null ? line.previous : root.lineTail); block.type = BlockType.CODE; block.removeSurroundingEmptyLines(); break; case XML: if (line.previous != null) { // FIXME ... this looks wrong root.split(line.previous); } root.split(line.xmlEndLine).type = BlockType.XML; root.removeLeadingEmptyLines(); line = root.lines; break; case BQUOTE: while (line != null) { if (!line.isEmpty && (line.prevEmpty && line.leading == 0 && line.getLineType(this.useExtensions) != LineType.BQUOTE)) break; line = line.next; } block = root.split(line != null ? line.previous : root.lineTail); block.type = BlockType.BLOCKQUOTE; block.removeSurroundingEmptyLines(); block.removeBlockQuotePrefix(); this.recurse(block, false); line = root.lines; break; case HR: if (line.previous != null) { // FIXME ... this looks wrong root.split(line.previous); } root.split(line).type = BlockType.RULER; root.removeLeadingEmptyLines(); line = root.lines; break; case TABLE: while (line != null && !line.isEmpty) { line = line.next; } block = root.split(line != null ? line.previous : root.lineTail); block.type = BlockType.TABLE; block.removeSurroundingEmptyLines(); line = root.lines; break; case FENCED_CODE: case TABLEB: case PLUGIN: line = line.next; while (line != null) { if (line.getLineType(this.useExtensions) == type) break; // TODO ... is this really necessary? Maybe add a special // flag? line = line.next; } if (line != null) line = line.next; block = root.split(line != null ? line.previous : root.lineTail); switch (type) { case TABLEB: block.type = BlockType.TABLEB; break; case PLUGIN: block.type = BlockType.PLUGIN; break; default: block.type = BlockType.FENCED_CODE; break; } block.meta = Utils.getMetaFromFence(block.lines.value); block.lines.setEmpty(); if (block.lineTail.getLineType(this.useExtensions) == type) block.lineTail.setEmpty(); block.removeSurroundingEmptyLines(); break; case HEADLINE: case HEADLINE1: case HEADLINE2: if (line.previous != null) { root.split(line.previous); } if (type != LineType.HEADLINE) { line.next.setEmpty(); } block = root.split(line); block.type = BlockType.HEADLINE; if (type != LineType.HEADLINE) block.hlDepth = type == LineType.HEADLINE1 ? 1 : 2; if (this.useExtensions) block.id = block.lines.stripID(); block.transfromHeadline(); root.removeLeadingEmptyLines(); line = root.lines; break; case OLIST: case ULIST: while (line != null) { final LineType t = line.getLineType(this.useExtensions); if (!line.isEmpty && (line.prevEmpty && line.leading == 0 && !(t == LineType.OLIST || t == LineType.ULIST))) break; line = line.next; } list = root.split(line != null ? line.previous : root.lineTail); list.type = type == LineType.OLIST ? BlockType.ORDERED_LIST : BlockType.UNORDERED_LIST; list.lines.prevEmpty = false; list.lineTail.nextEmpty = false; list.removeSurroundingEmptyLines(); list.lines.prevEmpty = list.lineTail.nextEmpty = false; initListBlock(list); block = list.blocks; while (block != null) { this.recurse(block, true); block = block.next; } list.expandListParagraphs(); break; default: line = line.next; break; } } } /** * Does all the processing. * * @return The processed String. * @throws IOException If an IO error occurred. */ private String process() throws IOException { final StringBuilder out = new StringBuilder(); final Block parent = this.readLines(); parent.removeSurroundingEmptyLines(); this.recurse(parent, false); Block block = parent.blocks; while (block != null) { this.emitter.emit(out, block); block = block.next; } return out.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy