Quantcast

Pegdown visit method of custom node missed during serialization

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Pegdown visit method of custom node missed during serialization

jhinkey
I am working on an extension of Pegdown's ToHtmlSerializer class to support some custom markdown to HTML conversions that I'd like to do. But I've not been able to get any of my custom nodes "visited" despite implementing respective visit(node) methods for them in my serializer.

To demonstrate, I've created a few classes that attempt to override the conversion of "strong" text from Markdown to HTML. Note, I chose the strong node simply to demonstrate the issue I'm encountering.

Here are the classes:

    ParserExt - Extends Parser overriding the rule for Strong(). Its main passes in "**Foo**" to be converted to HTML by the Pegdown processor
    PegdownProcessorExt - Extends PegdownProcessor overriding markdownToHtml() in order to use a custom serializer
    ToHtmlSerializerExt - Extends ToHtmlSerializer with method visit(StrongNodeExt)
    StrongNodeExt - Identical to StrongNode.


Upon running the program, the custom rule was matched, a StrongNodeExt was pushed onto the value stack, and the accept() method of StrongNodeExt was invoked. However, instead of the serializer's visit(StrongNodeExt) method being found and invoked, method ToHtmlSerializer.visit(SuperNode) was invoked. I followed the debugger into StrongNodeExt.accept() and then directly into method ToHtmlSerializer.visit(SuperNode). It was as though, no visit() method could be found to match the StrongNodeExt node despite a visit method being implemented for it right in my custom ToHtmlSerializerExt class.

Note, the original Pegdown parser has no problem finding the visit(StrongNode) method of the serializer. Is invocation of visit() methods limited to only methods declared in the Visitor interface? Did I implement my custom serializer incorrectly? If anyone could shed light on how to override or extend ToHtmlSerializer and what I may be doing wrong I'd much appreciate it.

Here are my classes ...

Classes in package org.pegdown:

    - ParserExt
    - PegDownProcessorExt
    - ToHtmlSerializerExt


public class ParserExt extends Parser {

    public ParserExt(Integer options) {
        super(options);
    }

    public ParserExt(Integer options, ParseRunnerProvider parseRunnerProvider) {
        super(options, parseRunnerProvider);
    }

    public Rule Strong() {
        return NodeSequence(
                FirstOf(EmphOrStrong("**"), EmphOrStrong("__")),
                push(new StrongNodeExt(popAsNode().getChildren()))
        );
    }

    public static void main(String[] args) {
        ParserExt parser =
            Parboiled.createParser(ParserExt.class, Extensions.ALL);
        PegDownProcessorExt pegDownProcessor = new PegDownProcessorExt(parser);
        String processedOutput = pegDownProcessor.markdownToHtml(
                "**Foo**", new LinkRenderer());
        System.out.print(processedOutput);
    }
}

public class PegDownProcessorExt extends PegDownProcessor {

    public PegDownProcessorExt(Parser parser) {
        super(parser);
    }

    public String markdownToHtml(char[] markdownSource, LinkRenderer linkRenderer) {
        RootNode astRoot = parseMarkdown(markdownSource);
        return new ToHtmlSerializerExt(linkRenderer).toHtml(astRoot);
    }
}

public class ToHtmlSerializerExt extends ToHtmlSerializer {

    public ToHtmlSerializerExt(LinkRenderer linkRenderer) {
        super(linkRenderer);
    }

    public void visit(StrongNodeExt node) {
        printTag(node, "strong");
    }

    protected void printTag(SuperNode node, String tag) {
        printer.print('<').print(tag).print('>');
        visitChildren(node);
        printer.print('<').print('/').print(tag).print('>');
    }
}

Classes in package org.pegdown.ast:

public class StrongNodeExt extends SuperNode {

    public StrongNodeExt(List<Node> children) {
        super(children);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

Thanks for having a look!
Jim
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Pegdown visit method of custom node missed during serialization

mathias
Administrator
Jim,

sorry for taking so long in getting back to you.

> ... Note, the original Pegdown parser has no problem finding the visit(StrongNode) method of the serializer. Is invocation of visit() methods limited to only methods declared in the Visitor interface?

Well, you've hit the most serious drawback of the visitor pattern:
You can easily implement new visitors but you cannot easily add new "visitees".

Look at your StrongNodeExt implementation:

> public class StrongNodeExt extends SuperNode {
>
>     public StrongNodeExt(List<Node> children) {
>         super(children);
>     }
>
>     @Override
>     public void accept(Visitor visitor) {
>         visitor.visit(this);  // <- "visit line"
>     }
> }

When the compiler generates the code for the "visit line" it determines, at compile time (!), which method of the Visitor interface to call.
Since the Visitor interface does not define a dedicated method for `StrongNodeExt` it has to fall back to calling `void visit(SuperNode)` here.

In the "pure" and original form of the visitor pattern this code wouldn't even compile, since there'd be no "catch-all" methods like `void visit(SuperNode)`. The reason I have added these to the Visitor interface are exactly cases like yours:
They give you a way of working around this limitation of the visitor pattern by putting you code into `void visit(SuperNode)` and discriminate between different cases using `instanceOf`.

HTH and cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 16.05.2012, at 03:14, jhinkey [via parboiled users] wrote:

> I am working on an extension of Pegdown's ToHtmlSerializer class to support some custom markdown to HTML conversions that I'd like to do. But I've not been able to get any of my custom nodes "visited" despite implementing respective visit(node) methods for them in my serializer. To demonstrate, I've created a few classes that attempt to override the conversion of "strong" text from Markdown to HTML. Note, I chose the strong node simply to demonstrate the issue I'm encountering. Here are the classes: ParserExt - Extends Parser overriding the rule for Strong(). Its main passes in "**Foo**" to be converted to HTML by the Pegdown processor PegdownProcessorExt - Extends PegdownProcessor overriding markdownToHtml() in order to use a custom serializer ToHtmlSerializerExt - Extends ToHtmlSerializer with method visit(StrongNodeExt) StrongNodeExt - Identical to StrongNode. Upon running the program, the custom rule was matched, a StrongNodeExt was pushed onto the value stack, and the accept() method of StrongNodeExt was invoked. However, instead of the serializer's visit(StrongNodeExt) method being found and invoked, method ToHtmlSerializer.visit(SuperNode) was invoked. I followed the debugger into StrongNodeExt.accept() and then directly into method ToHtmlSerializer.visit(SuperNode). It was as though, no visit() method could be found to match the StrongNodeExt node despite a visit method being implemented for it right in my custom ToHtmlSerializerExt class. Note, the original Pegdown parser has no problem finding the visit(StrongNode) method of the serializer. Is invocation of visit() methods limited to only methods declared in the Visitor interface? Did I implement my custom serializer incorrectly? If anyone could shed light on how to override or extend ToHtmlSerializer and what I may be doing wrong I'd much appreciate it. Here are my classes ... Classes in package org.pegdown: - ParserExt - PegDownProcessorExt - ToHtmlSerializerExt
> public class ParserExt extends Parser {
>
>     public ParserExt(Integer options) {
>         super(options);
>     }
>
>     public ParserExt(Integer options, ParseRunnerProvider parseRunnerProvider) {
>         super(options, parseRunnerProvider);
>     }
>
>     public Rule Strong() {
>         return NodeSequence(
>                 FirstOf(EmphOrStrong("**"), EmphOrStrong("__")),
>                 push(new StrongNodeExt(popAsNode().getChildren()))
>         );
>     }
>
>     public static void main(String[] args) {
>         ParserExt parser =
>             Parboiled.createParser(ParserExt.class, Extensions.ALL);
>         PegDownProcessorExt pegDownProcessor = new PegDownProcessorExt(parser);
>         String processedOutput = pegDownProcessor.markdownToHtml(
>                 "**Foo**", new LinkRenderer());
>         System.out.print(processedOutput);
>     }
> }
>
> public class PegDownProcessorExt extends PegDownProcessor {
>
>     public PegDownProcessorExt(Parser parser) {
>         super(parser);
>     }
>
>     public String markdownToHtml(char[] markdownSource, LinkRenderer linkRenderer) {
>         RootNode astRoot = parseMarkdown(markdownSource);
>         return new ToHtmlSerializerExt(linkRenderer).toHtml(astRoot);
>     }
> }
>
> public class ToHtmlSerializerExt extends ToHtmlSerializer {
>
>     public ToHtmlSerializerExt(LinkRenderer linkRenderer) {
>         super(linkRenderer);
>     }
>
>     public void visit(StrongNodeExt node) {
>         printTag(node, "strong");
>     }
>
>     protected void printTag(SuperNode node, String tag) {
>         printer.print('<').print(tag).print('>');
>         visitChildren(node);
>         printer.print('<').print('/').print(tag).print('>');
>     }
> }
>
> Classes in package org.pegdown.ast:
> public class StrongNodeExt extends SuperNode {
>
>     public StrongNodeExt(List<Node> children) {
>         super(children);
>     }
>
>     @Override
>     public void accept(Visitor visitor) {
>         visitor.visit(this);
>     }
> }
>
> Thanks for having a look! Jim
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/Pegdown-visit-method-of-custom-node-missed-during-serialization-tp3993060.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.
> NAML

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Pegdown visit method of custom node missed during serialization

jhinkey
Mathias,

Ah, I should have picked up on that . I was seeing that visit(SuperNode) was a catch all, but didn't even think of putting logic in there to call my visit(StrongNodeExt). Having the visit(SuperNode) catch all is nice - thanks for providing that!

Regards,
Jim
Loading...