Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/main/java/org/pegdown/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.parboiled.support.Var;
import org.pegdown.ast.*;
import org.pegdown.ast.SimpleNode.Type;
import org.pegdown.plugins.PegdownPlugins;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -63,14 +64,20 @@ public ParseRunner<Node> get(Rule rule) {
protected final int options;
protected final long maxParsingTimeInMillis;
protected final ParseRunnerProvider parseRunnerProvider;
protected final PegdownPlugins plugins;
final List<AbbreviationNode> abbreviations = new ArrayList<AbbreviationNode>();
final List<ReferenceNode> references = new ArrayList<ReferenceNode>();
long parsingStartTimeStamp = 0L;

public Parser(Integer options, Long maxParsingTimeInMillis, ParseRunnerProvider parseRunnerProvider) {
public Parser(Integer options, Long maxParsingTimeInMillis, ParseRunnerProvider parseRunnerProvider, PegdownPlugins plugins) {
this.options = options;
this.maxParsingTimeInMillis = maxParsingTimeInMillis;
this.parseRunnerProvider = parseRunnerProvider;
this.plugins = plugins;
}

public Parser(Integer options, Long maxParsingTimeInMillis, ParseRunnerProvider parseRunnerProvider) {
this(options, maxParsingTimeInMillis, parseRunnerProvider, PegdownPlugins.NONE);
}

public RootNode parse(char[] source) {
Expand Down Expand Up @@ -98,6 +105,7 @@ public Rule Block() {
return Sequence(
ZeroOrMore(BlankLine()),
FirstOf(new ArrayBuilder<Rule>()
.add(plugins.getBlockPluginRules())
.add(BlockQuote(), Verbatim())
.addNonNulls(ext(ABBREVIATIONS) ? Abbreviation() : null)
.add(Reference(), HorizontalRule(), Heading(), OrderedList(), BulletList(), HtmlBlock())
Expand Down Expand Up @@ -569,6 +577,7 @@ public Rule NonAutoLinkInline() {

public Rule NonLinkInline() {
return FirstOf(new ArrayBuilder<Rule>()
.add(plugins.getInlinePluginRules())
.add(Str(), Endline(), UlOrStarLine(), Space(), StrongOrEmph(), Image(), Code(), InlineHtml(),
Entity(), EscapedChar())
.addNonNulls(ext(QUOTES) ? new Rule[]{SingleQuoted(), DoubleQuoted(), DoubleAngleQuoted()} : null)
Expand Down
19 changes: 14 additions & 5 deletions src/main/java/org/pegdown/ToHtmlSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@

import org.parboiled.common.StringUtils;
import org.pegdown.ast.*;
import org.pegdown.plugins.ToHtmlSerializerPlugin;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.*;

import static org.parboiled.common.Preconditions.checkArgNotNull;

Expand All @@ -34,13 +32,19 @@ public class ToHtmlSerializer implements Visitor {
protected final Map<String, ReferenceNode> references = new HashMap<String, ReferenceNode>();
protected final Map<String, String> abbreviations = new HashMap<String, String>();
protected final LinkRenderer linkRenderer;
protected final List<ToHtmlSerializerPlugin> plugins;

protected TableNode currentTableNode;
protected int currentTableColumn;
protected boolean inTableHeader;

public ToHtmlSerializer(LinkRenderer linkRenderer) {
this(linkRenderer, Collections.<ToHtmlSerializerPlugin>emptyList());
}

public ToHtmlSerializer(LinkRenderer linkRenderer, List<ToHtmlSerializerPlugin> plugins) {
this.linkRenderer = linkRenderer;
this.plugins = plugins;
}

public String toHtml(RootNode astRoot) {
Expand Down Expand Up @@ -323,8 +327,13 @@ public void visit(SuperNode node) {
}

public void visit(Node node) {
for (ToHtmlSerializerPlugin plugin : plugins) {
if (plugin.visit(node, this, printer)) {
return;
}
}
// override this method for processing custom Node implementations
throw new RuntimeException("Not implemented");
throw new RuntimeException("Don't know how to handle node " + node);
}

// helpers
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/org/pegdown/plugins/BlockPluginParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2011 Mathias Doenitz
*
* Based on peg-markdown (C) 2008-2010 John MacFarlane
*
* 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 org.pegdown.plugins;

import org.parboiled.Rule;

/**
* A parser that provides block parser rules for pegdown. A pegdown plugin should implement this, along with
* {@link org.parboiled.BaseParser} or {@link org.pegdown.Parser} if it wants to use the pegdown utilities.
*
* This interface is intended for use with {@link PegdownPlugins.Builder#withPlugin(Class, Object...)}, in order for
* Java plugins to easily be registered with a parser.
*/
public interface BlockPluginParser {
Rule[] blockPluginRules();
}
32 changes: 32 additions & 0 deletions src/main/java/org/pegdown/plugins/InlinePluginParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2011 Mathias Doenitz
*
* Based on peg-markdown (C) 2008-2010 John MacFarlane
*
* 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 org.pegdown.plugins;

import org.parboiled.Rule;

/**
* A parser that provides inline parser rules for pegdown. A pegdown plugin should implement this, along with
* {@link org.parboiled.BaseParser} or {@link org.pegdown.Parser} if it wants to use the pegdown utilities.
*
* This interface is intended for use with {@link PegdownPlugins.Builder#withPlugin(Class, Object...)}, in order for
* Java plugins to easily be registered with a parser.
*/
public interface InlinePluginParser {
Rule[] inlinePluginRules();
}
113 changes: 113 additions & 0 deletions src/main/java/org/pegdown/plugins/PegdownPlugins.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (C) 2010-2011 Mathias Doenitz
*
* Based on peg-markdown (C) 2008-2010 John MacFarlane
*
* 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 org.pegdown.plugins;

import org.parboiled.BaseParser;
import org.parboiled.Parboiled;
import org.parboiled.Rule;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Encapsulates the plugins provided to pegdown.
*
* Construct this using @{link PegdownPlugins#builder}, and then passing in either the Java plugin classes, or
* precompiled rules (for greater control, or if using Scala rules).
*/
public class PegdownPlugins {

private final Rule[] inlinePluginRules;
private final Rule[] blockPluginRules;

private PegdownPlugins(Rule[] inlinePluginRules, Rule[] blockPluginRules) {
this.inlinePluginRules = inlinePluginRules;
this.blockPluginRules = blockPluginRules;
}

public Rule[] getInlinePluginRules() {
return inlinePluginRules;
}

public Rule[] getBlockPluginRules() {
return blockPluginRules;
}

public static Builder builder() {
return new Builder();
}

/**
* Create a builder that is a copy of the existing plugins
*/
public static Builder builder(PegdownPlugins like) {
return builder().withInlinePluginRules(like.getInlinePluginRules()).withBlockPluginRules(like.getBlockPluginRules());
}

/**
* Convenience reference to no plugins.
*/
public static PegdownPlugins NONE = builder().build();

public static class Builder {
private final List<Rule> inlinePluginRules = new ArrayList<Rule>();
private final List<Rule> blockPluginRules = new ArrayList<Rule>();

public Builder() {
}

public Builder withInlinePluginRules(Rule... inlinePlugins) {
this.inlinePluginRules.addAll(Arrays.asList(inlinePlugins));
return this;
}

public Builder withBlockPluginRules(Rule... blockPlugins) {
this.blockPluginRules.addAll(Arrays.asList(blockPlugins));
return this;
}

/**
* Add a plugin parser. This should either implement {@link InlinePluginParser} or {@link BlockPluginParser},
* or both. The parser will be enhanced by parboiled before its rules are extracted and registered here.
*
* @param pluginParser the plugin parser class.
* @param arguments the arguments to pass to the constructor of that class.
*/
public Builder withPlugin(Class<? extends BaseParser<Object>> pluginParser, Object... arguments) {
// First, check that the parser implements one of the parser interfaces
if (!(InlinePluginParser.class.isAssignableFrom(pluginParser) ||
BlockPluginParser.class.isAssignableFrom(pluginParser))) {
throw new IllegalArgumentException("Parser plugin must implement a parser plugin interface to be useful");
}
BaseParser<Object> parser = Parboiled.createParser(pluginParser, arguments);
if (parser instanceof InlinePluginParser) {
withInlinePluginRules(((InlinePluginParser) parser).inlinePluginRules());
}
if (parser instanceof BlockPluginParser) {
withBlockPluginRules(((BlockPluginParser) parser).blockPluginRules());
}
return this;
}

public PegdownPlugins build() {
return new PegdownPlugins(inlinePluginRules.toArray(new Rule[0]), blockPluginRules.toArray(new Rule[0]));
}
}
}
39 changes: 39 additions & 0 deletions src/main/java/org/pegdown/plugins/ToHtmlSerializerPlugin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2010-2011 Mathias Doenitz
*
* Based on peg-markdown (C) 2008-2010 John MacFarlane
*
* 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 org.pegdown.plugins;

import org.pegdown.Printer;
import org.pegdown.ast.Node;
import org.pegdown.ast.Visitor;

/**
* A plugin for the {@link org.pegdown.ToHtmlSerializer}
*/
public interface ToHtmlSerializerPlugin {

/**
* Visit the given node
*
* @param node The node to visit
* @param visitor The visitor, for delegating back to handling children, etc
* @param printer The printer to print output to
* @return true if this plugin knew how to serialize the node, false otherwise
*/
boolean visit(Node node, Visitor visitor, Printer printer);
}
16 changes: 16 additions & 0 deletions src/test/java/org/pegdown/BlockPluginNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.pegdown;

import org.pegdown.ast.Node;
import org.pegdown.ast.TextNode;
import org.pegdown.ast.Visitor;

public class BlockPluginNode extends TextNode {
public BlockPluginNode(String text) {
super(text);
}

@Override
public void accept(Visitor visitor) {
visitor.visit((Node) this);
}
}
16 changes: 16 additions & 0 deletions src/test/java/org/pegdown/InlinePluginNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.pegdown;

import org.pegdown.ast.Node;
import org.pegdown.ast.TextNode;
import org.pegdown.ast.Visitor;

public class InlinePluginNode extends TextNode {
public InlinePluginNode(String text) {
super(text);
}

@Override
public void accept(Visitor visitor) {
visitor.visit((Node) this);
}
}
49 changes: 49 additions & 0 deletions src/test/java/org/pegdown/PluginParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.pegdown;

import org.parboiled.BaseParser;
import org.parboiled.Rule;
import org.parboiled.support.StringBuilderVar;
import org.pegdown.plugins.BlockPluginParser;
import org.pegdown.plugins.InlinePluginParser;

public class PluginParser extends Parser implements InlinePluginParser, BlockPluginParser {

public PluginParser() {
super(ALL, 1000l, DefaultParseRunnerProvider);
}

@Override
public Rule[] blockPluginRules() {
return new Rule[] {BlockPlugin()};
}

@Override
public Rule[] inlinePluginRules() {
return new Rule[] {InlinePlugin()};
}

public Rule InlinePlugin() {
StringBuilderVar text = new StringBuilderVar();
return NodeSequence(
Ch('%'),
OneOrMore(TestNot(Ch('%')), BaseParser.ANY, text.append(matchedChar())),
push(new InlinePluginNode(text.getString())),
Ch('%')
);
}

public Rule BlockPlugin() {
StringBuilderVar text = new StringBuilderVar();
return NodeSequence(
BlockPluginMarker(),
OneOrMore(TestNot(Newline(), BlockPluginMarker()), BaseParser.ANY, text.append(matchedChar())),
Newline(),
push(new BlockPluginNode(text.appended('\n').getString())),
BlockPluginMarker()
);
}

public Rule BlockPluginMarker() {
return Sequence(NOrMore('%', 3), Newline());
}
}
Loading