Custom directive for XML model

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

Custom directive for XML model

Viktor Levine
In the test below the test for map model works fine, but for XML model
breaks. The custom directive is to wire in Spring's SPEL into FM templates,
but I'm certain that the same would fail on any other directive. The reason
IMHO is this construct in DocumentModel
if (StringUtil.isXMLID(key)) {
            ElementModel em = (ElementModel) NodeModel.wrap(((Document)
node).getDocumentElement());
            if (em.matchesName(key, Environment.getCurrentEnvironment())) {
                return em;
            } else {
//HERE
                return new NodeListModel(this);
            }
}
which should return null where it is commented HERE. Then the caller
(Environment.getGlobalVariable(String)) would correctly pull the directive
from the shared variables.

The template  is :
[#ftl]
We say [@spel]@greetingBean.printGreeting(name)[/@spel] from Freemarker in
[@spel expression="T(java.util.Locale).getDefault()"/]


The test (pardon me for the length):

public class SpelFreemarkerTemplateDirectiveTest {
    private static final String XML = "<name>Felix</name>";
    @Mock
    private BeanFactory beanFactory;
    private GreetingBean greetingBean = new GreetingBean("Hello");

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testFreemarkerSpel()
        throws IOException, TemplateException {
        Map<String, String> model = new HashMap<String, String>();

        model.put("name", "Felix");

        Configuration cfg = new Configuration();
        SpelFreemarkerTemplateDirective directive = new
SpelFreemarkerTemplateDirective();

        when(beanFactory.getBean("greetingBean")).thenReturn(greetingBean);
        directive.setBeanFactory(beanFactory);
        cfg.setSharedVariable("spel", directive);

        Template tpl = cfg.getTemplate("greeting-template.ftl");

        StringWriter writer = new StringWriter();

        tpl.process(model, writer);

        System.out.println(writer.toString());

        String expected = "We say " +
greetingBean.printGreeting(model.get("name")) +
            " from Freemarker in " + Locale.getDefault();

        assertEquals(expected, writer.toString());
    }

    /**
     * JAVADOC Method Level Comments
     *
     * @throws Exception JAVADOC.
     */
    @Test
    public void testXmlSpel()
        throws Exception {
        NodeModel model = NodeModel.parse(new InputSource(new
StringReader(XML)));
        Configuration cfg = new Configuration();

        SpelFreemarkerTemplateDirective directive = new
SpelFreemarkerTemplateDirective();

        when(beanFactory.getBean("greetingBean")).thenReturn(greetingBean);
        directive.setBeanFactory(beanFactory);
        cfg.setSharedVariable("spel", directive);

        Template tpl = cfg.getTemplate("greeting-template.ftl");

        StringWriter writer = new StringWriter();

        tpl.process(model, writer);

        System.out.println(writer.toString());

        String expected = "We say " + greetingBean.printGreeting("Felix") +
" from Freemarker in " +
            Locale.getDefault();

        assertEquals(expected, writer.toString());
    }

    public static class GreetingBean {
        private String greeting;

        public GreetingBean(String greeting) {
            this.greeting = greeting;
        }

        public String printGreeting(String name) {
            return greeting + " " + name;
        }
    }
}



------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. Consolidate legacy IT systems to a single system of record for IT
2. Standardize and globalize service processes across IT
3. Implement zero-touch automation to replace manual, redundant tasks
http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk
_______________________________________________
FreeMarker-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/freemarker-user
Reply | Threaded
Open this post in threaded view
|

Re: Custom directive for XML model

Viktor Levine
And the stack trace:

FreeMarker template error:
For "@" callee: Expected a(n) user-defined directive (macro, etc.), but this
evaluated to a sequence+hash (wrapper: f.e.dom.NodeListModel):
==> spel  [in template
"test/unit/com/algorithmics/pulp/spring/greeting-template.ftl" at line 2,
column 10]

The failing instruction (FTL stack trace):
----------
==> @spel  [in template
"test/unit/com/algorithmics/pulp/spring/greeting-template.ftl" at line 2,
column 8]
----------

Java stack trace (for programmers):
----------
freemarker.core.UnexpectedTypeException: [... Exception message was already
printed; see it above ...]
        at freemarker.core.UnifiedCall.accept(UnifiedCall.java:146)
        at freemarker.core.Environment.visit(Environment.java:265)
        at freemarker.core.MixedContent.accept(MixedContent.java:93)
        at freemarker.core.Environment.visit(Environment.java:265)
        at freemarker.core.Environment.process(Environment.java:243)
        at freemarker.template.Template.process(Template.java:277)
        at



------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. Consolidate legacy IT systems to a single system of record for IT
2. Standardize and globalize service processes across IT
3. Implement zero-touch automation to replace manual, redundant tasks
http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk
_______________________________________________
FreeMarker-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/freemarker-user
Reply | Threaded
Open this post in threaded view
|

Re: Custom directive for XML model

Daniel Dekany
In reply to this post by Viktor Levine
There problem here is that with XML models the `.` or `[]` operator
will always return a set of nodes (NodeListModel), even if there's no
match. This is crucial in the approach of the XML wapper, as further
`.` or `[]` operators are possibly applied on the result, like in
`foo.maybeMissing.bar`, also because `<#list foo.optinals as i>` has
to work even if there's 0 `optionals` in foo. So an XML model isn't
meant to be *directly* used as the data-model root. Even if it had a
setting that causes it to return null there, to fall back to the
shared variables, first it had to check that em.matchesName returns
false, which is probably not very fast (compared to, say, a
HashMap.get).

So maybe you should just create a HashMap, and put the top-element of
the XML into it with its own name, and then use said HashMap as the
root. Or you can encapsulate the same functionality into your own
TemplateHashModel implementation that wraps a W3C DOM Document. Is
that viable?

--
Thanks,
 Daniel Dekany

Thursday, September 12, 2013, 9:31:30 AM, Viktor Levine wrote:

> In the test below the test for map model works fine, but for XML model
> breaks. The custom directive is to wire in Spring's SPEL into FM templates,
> but I'm certain that the same would fail on any other directive. The reason
> IMHO is this construct in DocumentModel
> if (StringUtil.isXMLID(key)) {
>             ElementModel em = (ElementModel) NodeModel.wrap(((Document)
> node).getDocumentElement());
>             if (em.matchesName(key,
> Environment.getCurrentEnvironment())) {
>                 return em;
>             } else {
> //HERE
>                 return new NodeListModel(this);
>             }
> }
> which should return null where it is commented HERE. Then the caller
> (Environment.getGlobalVariable(String)) would correctly pull the directive
> from the shared variables.
>
> The template  is :
> [#ftl]
> We say [@spel]@greetingBean.printGreeting(name)[/@spel] from Freemarker in
> [@spel expression="T(java.util.Locale).getDefault()"/]
>
>
> The test (pardon me for the length):
>
> public class SpelFreemarkerTemplateDirectiveTest {
>     private static final String XML = "<name>Felix</name>";
>     @Mock
>     private BeanFactory beanFactory;
>     private GreetingBean greetingBean = new GreetingBean("Hello");
>
>     @Before
>     public void setup() {
>         MockitoAnnotations.initMocks(this);
>     }
>
>     @Test
>     public void testFreemarkerSpel()
>         throws IOException, TemplateException {
>         Map<String, String> model = new HashMap<String, String>();
>
>         model.put("name", "Felix");
>
>         Configuration cfg = new Configuration();
>         SpelFreemarkerTemplateDirective directive = new
> SpelFreemarkerTemplateDirective();
>
>        
> when(beanFactory.getBean("greetingBean")).thenReturn(greetingBean);
>         directive.setBeanFactory(beanFactory);
>         cfg.setSharedVariable("spel", directive);
>
>         Template tpl = cfg.getTemplate("greeting-template.ftl");
>
>         StringWriter writer = new StringWriter();
>
>         tpl.process(model, writer);
>
>         System.out.println(writer.toString());
>
>         String expected = "We say " +
> greetingBean.printGreeting(model.get("name")) +
>             " from Freemarker in " + Locale.getDefault();
>
>         assertEquals(expected, writer.toString());
>     }
>
>     /**
>      * JAVADOC Method Level Comments
>      *
>      * @throws Exception JAVADOC.
>      */
>     @Test
>     public void testXmlSpel()
>         throws Exception {
>         NodeModel model = NodeModel.parse(new InputSource(new
> StringReader(XML)));
>         Configuration cfg = new Configuration();
>
>         SpelFreemarkerTemplateDirective directive = new
> SpelFreemarkerTemplateDirective();
>
>        
> when(beanFactory.getBean("greetingBean")).thenReturn(greetingBean);
>         directive.setBeanFactory(beanFactory);
>         cfg.setSharedVariable("spel", directive);
>
>         Template tpl = cfg.getTemplate("greeting-template.ftl");
>
>         StringWriter writer = new StringWriter();
>
>         tpl.process(model, writer);
>
>         System.out.println(writer.toString());
>
>         String expected = "We say " +
> greetingBean.printGreeting("Felix") +
> " from Freemarker in " +
>             Locale.getDefault();
>
>         assertEquals(expected, writer.toString());
>     }
>
>     public static class GreetingBean {
>         private String greeting;
>
>         public GreetingBean(String greeting) {
>             this.greeting = greeting;
>         }
>
>         public String printGreeting(String name) {
>             return greeting + " " + name;
>         }
>     }
> }


------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. Consolidate legacy IT systems to a single system of record for IT
2. Standardize and globalize service processes across IT
3. Implement zero-touch automation to replace manual, redundant tasks
http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk
_______________________________________________
FreeMarker-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/freemarker-user
Reply | Threaded
Open this post in threaded view
|

Re: Custom directive for XML model

Viktor Levine
Daniel,

I can see your reasons. However, it renders the whole concept of custom
directives unusable in XML models out of the box. Perhaps documentation
should reflect this. We would have to avoid using custom directives in our
development. Until this functionality would be independent of the model.

Brgrds




------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. Consolidate legacy IT systems to a single system of record for IT
2. Standardize and globalize service processes across IT
3. Implement zero-touch automation to replace manual, redundant tasks
http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk
_______________________________________________
FreeMarker-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/freemarker-user
Reply | Threaded
Open this post in threaded view
|

Re: Custom directive for XML model

Daniel Dekany
Thursday, September 12, 2013, 2:50:35 PM, Viktor Levine wrote:

> Daniel,
>
> I can see your reasons. However, it renders the whole concept of custom
> directives unusable in XML models out of the box.

XML is normally not used as the data-model *root*. It was never meant
to be used like that.

> Perhaps documentation should reflect this. We would have to avoid
> using custom directives in our development.

You can just expose the top-level XML element in the data-model with a
variable, as I have suggested.

> Until this functionality would be independent of the model.

--
Thanks,
 Daniel Dekany


------------------------------------------------------------------------------
How ServiceNow helps IT people transform IT departments:
1. Consolidate legacy IT systems to a single system of record for IT
2. Standardize and globalize service processes across IT
3. Implement zero-touch automation to replace manual, redundant tasks
http://pubads.g.doubleclick.net/gampad/clk?id=51271111&iu=/4140/ostg.clktrk
_______________________________________________
FreeMarker-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/freemarker-user