Some questions about writing my own processor

classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

Some questions about writing my own processor

niels
Hi I'm struggling with finding a smart solution for Twitter Bootstrap fields. I described it in detail at Stackoverflow (formatting is easier there). http://stackoverflow.com/questions/16394548/how-to-handle-twitter-bootstrap-fields-with-thymeleaf-spring-and-less-code.

The main question is how much time needs a beginner to write his first processor? Can a processor inserts th:field attributes that will be interpreted later?

Would be glad to get some feedback.
Niels
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

Zemi
Administrator
> The main question is how much time needs a beginner to write his first processor?

According to the docs, exactly 5 minutes :-)
   http://www.thymeleaf.org/sayhelloextendingthymeleaf5minutes.html

If you have another 5 minutes you can do even more :-)
   http://www.thymeleaf.org/sayhelloagainextendingthymeleafevenmore5minutes.html

> Can a processor inserts th:field attributes that will be interpreted later?

Yes, it can, provided the processor precedence is higher than the th:field processor precedence.

Feel free to ask any problems you encounter.
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
5 minutes  for reading. First problem is to find the right super-class without javadoc. I choose AbstractChildrenModifierAttrProcessor, hopefully the right choice.
Niels
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

danielfernandez
Administrator
Yup, javadoc for many of those classes is still in the TO-DO.

Anyway, we accept contributions ;-)

Regards,
Daniel.
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

Zemi
Administrator
In reply to this post by niels
 > I choose AbstractChildrenModifierAttrProcessor, hopefully the right choice.

I always extend AbstractAttrProcessor which is the base class for attribute processors.

Extending that base class you can do everything. On the other hand, extending one of the subclasses you will have to write less code (which is better), but you will not be able to do everything.
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
Ok I do the following
BootstrapDialect with

   @Override
    public String getPrefix() {
        return "bs";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isLenient() {
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<IProcessor> getProcessors() {
        final Set<IProcessor> processors = new HashSet<>();
        processors.add(new BootstrapFieldAttrProcessor());
        return processors;
    }

And in BootstrapFieldAttrProcessor

    public static final String ATTRIBUTE_NAME = "field";

    public BootstrapFieldAttrProcessor() {
        super("ATTRIBUTE_NAME");
    }

In SpringConfig:

SpringTemplateEngine ste = new SpringTemplateEngine();
        ste.setTemplateResolver(templateResolver());
        ste.addDialect(new LayoutDialect());
        ste.addDialect(new BootstrapDialect());

and in the template
<textarea rows="4" bs:field="content"  th:disabled="${disabled}"></textarea>

How ever non of my methods in the processor seems to reached :-( So I get a
Error processing template: dialect prefix "bs" is set as non-lenient but attribute "bs:field" has not been removed during process (post/postform:67)

I only can see that the Constructor of my Processor is called.

Any idea?

Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

Zemi
Administrator
Your processor did not remove the bs:field attribute.

Could you post the Processor code?
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
Of course it doesn't remove it - the code is never called.

How ever here is the code.

public class BootstrapFieldAttrProcessor extends
        AbstractChildrenModifierAttrProcessor {

    /**
     * The Logger for the controller.
     */
    private static final Logger LOG = LoggerFactory
            .getLogger(BootstrapFieldAttrProcessor.class);

    public static final String ATTRIBUTE_NAME = "field";

    public BootstrapFieldAttrProcessor() {
        super("ATTRIBUTE_NAME");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected List<Node> getModifiedChildren(Arguments arguments,
            Element element, String attributeName) {
        System.out.println(attributeName);
        final List<Node> modifiedElements = new ArrayList<>();
        final String fieldName = element.getAttributeValue(ATTRIBUTE_NAME);
        element.setAttribute(
                "th:" + AbstractSpringFieldAttrProcessor.ATTR_NAME, fieldName);
        element.removeAttribute(ATTRIBUTE_NAME);
        modifiedElements.add(element);
        return modifiedElements;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean getReplaceHostElement(Arguments arguments,
            Element element, String attributeName) {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getPrecedence() {
        return 10;
    }

}
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
I have got it  super("ATTRIBUTE_NAME");  is wrong with super(ATTRIBUTE_NAME);  I get an error which has to do with my processor. I must analyze it.
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
Any prettyprinter for a node so that I can see what I have constructed? This would make it easier to analyze errors.
At the moment
"I get Error processing template: dialect prefix "th" is set as non-lenient but attribute "th:disabled" has not been removed during process (post/postform:67)"
this is normal because I only remove the tag I work on. So it looks like the element must recompute later.

Here my code:
    private Node createBootstrapField(String fieldName, Element input) {
        Validate.notNull(input, "You must define an input-element.");
        Validate.notNull(fieldName, "You must define a fieldName.");
        // Create the new elements tags
        Element controlgroup = new Element("div");
        controlgroup.setAttribute("class", "control-label");
        controlgroup.setAttribute("th:for", "'" + fieldName + "'");
        controlgroup.setAttribute("th:classappend", "${#fields.hasErrors('"
                + fieldName + "')}? 'error'");

        Element label = new Element("label");
        label.setAttribute("class", "control-group");
        label.setAttribute("th:text", "#{model.__*{class.simpleName}__."
                + fieldName + "}+':'");
        label.addChild(new Text(fieldName));

        Element controls = new Element("div");
        controls.setAttribute("class", "controls");

        controlgroup.addChild(label);
        controlgroup.addChild(controls);
        controls.addChild(input);
        Element help = new Element("span");
        help.setAttribute("class", "help-inline");
        help.setAttribute("th:if", "${#fields.hasErrors('" + fieldName + "')}");
        help.setAttribute("th:errors", "*{" + fieldName + "}");
        input.setRecomputeProcessorsImmediately(true);
        return controlgroup;
    }

and
final Node newNode = createBootstrapField(fieldName, element);
        parent.insertAfter(element, newNode);
        parent.removeChild(element);
        newNode.setRecomputeProcessorsImmediately(true);

It looks like the newNode isn't computed by thyemleaf :-(
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

danielfernandez
Administrator
The error says there is a "th:disabled" attribute somewhere that somehow finishes template execution without being removed. As the SpringStandard dialect is "non-lenient", this is considered an error (non-lenient dialects must leave no trace of their execution).

But I don't see any "th:disabled" in your code, so the problem might be anywhere else...

As for that "recomputeProcessorsImmediately" you execute on "newNode", it has no effect because your newNode is new, so at that moment there have been no precomputations on it. That "recomputeProcessorsImmediately" flag is used to indicate thymeleaf that previous precomputations (which decide what processors should be executed for each DOM node) should be ignored. So you are probably better off setting that "recomputeProcessorsImmediately" on "parent".

Finally, as for methods for turning DOM into String, there are some at the "DOMUtils" class, like "getHtml5For(Node)".

Regards,
Daniel.

Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

danielfernandez
Administrator
Ah, and by the way, that "help" Element you are creating there doesn't look to be added anywhere in your DOM...
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
Thanks for the information. The th:disabled comes from
<textarea rows="4" bs:field="content"  th:disabled="${disabled}"></textarea>

If I removed it I get
 Error processing template: dialect prefix "th" is set as non-lenient but attribute "th:field" has not been removed during process (post/postform:67)
So I guess it's a problem that my Node isn't recompute.

parent.setRecomputeProcessorsImmediately(true); doesn't solve the problem.

If I add System.out.println(DOMUtils.getHtml5For(newNode)); throws a different exception coming from DOMUtils.
Error processing template: dialect prefix "th" is set as non-lenient but attribute "th:for" has not been removed during process (output:67)
So it's not very useful for debugging .

Any further ideas how I can find out why the recompute or first compute doesn't work? I set the PRECEDENCE to 10 so I thought it would computed first and then the normal th: attributes.
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

Zemi
Administrator
I would try the opposite, a higher precedence such as 10000.

Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
@Zemi: This doesn't help. How ever I got it. The HTML is wrong, but it seems to work.
Reply | Threaded
Open this post in threaded view
|

Re: Some questions about writing my own processor

niels
Are there any helpers for unit-tests? I thought about something like node.toHtmlWithNoFurtherComputing() and a possibility to compare with a String. Or the other way around, parsing a String creates a DOM and then compare the DOM.