Skip to content

Commit

Permalink
Improve link 'rel=nofollow' enabling logic, closes sirthias#29
Browse files Browse the repository at this point in the history
  • Loading branch information
sirthias committed Aug 9, 2011
1 parent e629cd3 commit d9e198d
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 36 deletions.
6 changes: 0 additions & 6 deletions src/main/java/org/pegdown/Extensions.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,4 @@ public interface Extensions {
* Suppresses HTML blocks as well as inline HTML tags. Both will be accepted in the input but not be contained in the output.
*/
static final int SUPPRESS_ALL_HTML = 0x00030000;

/**
* Suppresses HTML blocks as well as inline HTML tags. Both will be accepted in the input but not be contained in the output.
*/
static final int NO_FOLLOW_LINKS = 0x00040000;

}
41 changes: 41 additions & 0 deletions src/main/java/org/pegdown/NoFollow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.pegdown;

import org.pegdown.ast.AutoLinkNode;
import org.pegdown.ast.ExpLinkNode;
import org.pegdown.ast.RefLinkNode;

public interface NoFollow {

/**
* Determines whether the given link node should receive a "rel=nofollow" attribute when generated as HTML.
* @param node
* @return true if the given link node should receive a "rel=nofollow" attribute
*/
boolean noFollow(AutoLinkNode node);

/**
* Determines whether the given link node should receive a "rel=nofollow" attribute when generated as HTML.
* @param node
* @return true if the given link node should receive a "rel=nofollow" attribute
*/
boolean noFollow(ExpLinkNode node);

/**
* Determines whether the given link node should receive a "rel=nofollow" attribute when generated as HTML.
* @param node
* @return true if the given link node should receive a "rel=nofollow" attribute
*/
boolean noFollow(RefLinkNode node);

static NoFollow NEVER = new NoFollow() {
public boolean noFollow(AutoLinkNode node) { return false; }
public boolean noFollow(ExpLinkNode node) { return false; }
public boolean noFollow(RefLinkNode node) { return false; }
};

static NoFollow ALWAYS = new NoFollow() {
public boolean noFollow(AutoLinkNode node) { return true; }
public boolean noFollow(ExpLinkNode node) { return true; }
public boolean noFollow(RefLinkNode node) { return true; }
};
}
6 changes: 3 additions & 3 deletions src/main/java/org/pegdown/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ public Rule NonAutoLink() {
public Rule ExplicitLink() {
Var<ExpLinkNode> node = new Var<ExpLinkNode>();
return Sequence(
push(node.setAndGet(new ExpLinkNode(popAsNode(), ext(NO_FOLLOW_LINKS)))),
push(node.setAndGet(new ExpLinkNode(popAsNode()))),
Spn1(), '(', Sp(),
Source(node),
Spn1(), Optional(Title(node)),
Expand All @@ -708,7 +708,7 @@ public Rule ExplicitLink() {
public Rule ReferenceLink() {
Var<RefLinkNode> node = new Var<RefLinkNode>();
return Sequence(
push(node.setAndGet(new RefLinkNode(popAsNode(), ext(NO_FOLLOW_LINKS)))),
push(node.setAndGet(new RefLinkNode(popAsNode()))),
FirstOf(
// regular reference link
Sequence(Spn1(), node.get().setSeparatorSpace(match()),
Expand Down Expand Up @@ -765,7 +765,7 @@ public Rule AutoLink() {
public Rule AutoLinkUrl() {
return Sequence(
Sequence(OneOrMore(Letter()), "://", AutoLinkEnd()),
push(new AutoLinkNode(match(), ext(NO_FOLLOW_LINKS)))
push(new AutoLinkNode(match()))
);
}

Expand Down
24 changes: 23 additions & 1 deletion src/main/java/org/pegdown/PegDownProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,37 @@ public String markdownToHtml(String markdownSource) {
return markdownToHtml(markdownSource.toCharArray());
}

/**
* Converts the given markdown source to HTML.
*
* @param markdownSource the markdown source to convert
* @param noFollow the NoFollow to use
* @return the HTML
*/
public String markdownToHtml(String markdownSource, NoFollow noFollow) {
return markdownToHtml(markdownSource.toCharArray(), noFollow);
}

/**
* Converts the given markdown source to HTML.
*
* @param markdownSource the markdown source to convert
* @return the HTML
*/
public String markdownToHtml(char[] markdownSource) {
return markdownToHtml(markdownSource, NoFollow.NEVER);
}

/**
* Converts the given markdown source to HTML.
*
* @param markdownSource the markdown source to convert
* @param noFollow the NoFollow to use
* @return the HTML
*/
public String markdownToHtml(char[] markdownSource, NoFollow noFollow) {
RootNode astRoot = parseMarkdown(markdownSource);
return new ToHtmlSerializer().toHtml(astRoot);
return new ToHtmlSerializer(noFollow).toHtml(astRoot);
}

/**
Expand Down
14 changes: 10 additions & 4 deletions src/main/java/org/pegdown/ToHtmlSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,20 @@
import static org.parboiled.common.Preconditions.checkArgNotNull;

public class ToHtmlSerializer implements Visitor, Printer.Encoder {

protected Printer printer = new Printer();
protected final Map<String, ReferenceNode> references = new HashMap<String, ReferenceNode>();
protected final Map<String, String> abbreviations = new HashMap<String, String>();
protected final NoFollow noFollow;

protected TableNode currentTableNode;
protected int currentTableColumn;
protected boolean inTableHeader;
protected Random random = new Random(0x2626); // for email obfuscation
protected Random random = new Random(0x2626); // for email obfuscation

public ToHtmlSerializer(NoFollow noFollow) {
this.noFollow = noFollow;
}

public String toHtml(RootNode astRoot) {
checkArgNotNull(astRoot, "astRoot");
Expand Down Expand Up @@ -69,7 +75,7 @@ public void visit(AbbreviationNode node) {
public void visit(AutoLinkNode node) {
printer.print("<a href=\"")
.printEncoded(node.getText(), this)
.print(node.isNofollow() ? "\" rel=\"nofollow\">" : "\">")
.print(noFollow.noFollow(node) ? "\" rel=\"nofollow\">" : "\">")
.printEncoded(node.getText(), this)
.print("</a>");
}
Expand Down Expand Up @@ -112,7 +118,7 @@ public void visit(ExpLinkNode node) {
if (node.getTitle() != null) {
printer.print(" title=\"").printEncoded(node.getTitle(), this).print('"');
}
if (node.isNofollow()) printer.print(" rel=\"nofollow\"");
if (noFollow.noFollow(node)) printer.print(" rel=\"nofollow\"");
printer.print('>');
visitChildren(node);
printer.print("</a>");
Expand Down Expand Up @@ -207,7 +213,7 @@ public void run() {
if (refNode.getTitle() != null) {
printer.print(" title=\"").printEncoded(refNode.getTitle(), this).print('"');
}
if (node.isNofollow()) printer.print(" rel=\"nofollow\"");
if (noFollow.noFollow(node)) printer.print(" rel=\"nofollow\"");
printer.print('>');
visitChildren(node);
printer.print("</a>");
Expand Down
8 changes: 1 addition & 7 deletions src/main/java/org/pegdown/ast/AutoLinkNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,9 @@
package org.pegdown.ast;

public class AutoLinkNode extends TextNode {
private boolean nofollow;

public AutoLinkNode(String text, boolean nofollow) {
public AutoLinkNode(String text) {
super(text);
this.nofollow = nofollow;
}

public boolean isNofollow() {
return nofollow;
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/pegdown/ast/ExpLinkNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class ExpLinkNode extends LinkNode {
private String url;
private String title;

public ExpLinkNode(Node child, boolean nofollow) {
super(child, nofollow);
public ExpLinkNode(Node child) {
super(child);
}

public String getUrl() {
Expand Down
8 changes: 1 addition & 7 deletions src/main/java/org/pegdown/ast/LinkNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@

public abstract class LinkNode extends SuperNode {
private boolean image;
private boolean nofollow;

public LinkNode(Node child, boolean nofollow) {
public LinkNode(Node child) {
super(child);
this.nofollow = nofollow;
}

public boolean getImage() {
Expand All @@ -34,8 +32,4 @@ public boolean getImage() {
public boolean makeImage() {
return image = true;
}

public boolean isNofollow() {
return nofollow;
}
}
4 changes: 2 additions & 2 deletions src/main/java/org/pegdown/ast/RefLinkNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class RefLinkNode extends LinkNode {
private String separatorSpace;
private SuperNode referenceKey;

public RefLinkNode(Node child, boolean nofollow) {
super(child, nofollow);
public RefLinkNode(Node child) {
super(child);
}

public String getSeparatorSpace() {
Expand Down
12 changes: 10 additions & 2 deletions src/test/java/org/pegdown/AbstractTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,26 @@ public void setup() {
protected abstract PegDownProcessor getProcessor();

protected void test(String testName) {
test(testName, NoFollow.NEVER);
}

protected void test(String testName, NoFollow noFollow) {
String expectedUntidy = FileUtils.readAllTextFromResource(testName + ".html");
assertNotNull(expectedUntidy);

test(testName, tidy(expectedUntidy));
test(testName, tidy(expectedUntidy), noFollow);
}

protected void test(String testName, String expectedOutput) {
test(testName, expectedOutput, NoFollow.NEVER);
}

protected void test(String testName, String expectedOutput, NoFollow noFollow) {
char[] markdown = FileUtils.readAllCharsFromResource(testName + ".md");
Preconditions.checkState(markdown != null, "Test not found");

RootNode astRoot = getProcessor().parseMarkdown(markdown);
String actualHtml = new ToHtmlSerializer().toHtml(astRoot);
String actualHtml = new ToHtmlSerializer(noFollow).toHtml(astRoot);

// debugging I: check the parse tree
//assertEquals(printNodeTree(getProcessor().parser.parseToParsingResult(markdown)), "<parse tree>");
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/pegdown/CustomPegDownTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ public void testHTMLSuppression() {

@Test(dependsOnMethods = "customPegDownTests2")
public void testNoFollowLinks() {
processor = new PegDownProcessor((ALL + NO_FOLLOW_LINKS) & ~HARDWRAPS);
test("pegdown/No Follow Links");
processor = new PegDownProcessor(ALL & ~HARDWRAPS);
test("pegdown/No Follow Links", NoFollow.ALWAYS);
}

}

0 comments on commit d9e198d

Please sign in to comment.