Re: A nullability question
Daniel Dekany <[email protected]>
| Newsgroups | gmane.comp.web.freemarker.user |
|---|---|
| Message-ID | <[email protected]> |
Monday, May 30, 2011, 12:19:54 PM, Eirik Lygre wrote:
> Hi,
>
> For one of our FreeMarker scenarios, we use a model object with a signature
> like this:
>
> class MyNumberModel implements TemplateHashModelEx, TemplateNumberModel {
> ...
> public Number getAsNumber() {
> return functionThatReturnsActualNumber();
> }
> }
>
> In general, the HashModel returns lots of metadata (like datatype, length,
> name, origin, etc), while the NumberModel returns the actual value. My
> question is how to best handle null values, to that "${field!0}" works properly.
>
> I was hoping that having getAsNumber() return "null" would be the proper
> thing to do: Then, we can reuse the model for all numeric types, regardless
> of their actual value, and still access the metadata even when the value is
> null. This doesn't seem to be the case, though: Rather, if
> functionThatReturnsActualNumber() returns null, we get this stack trace:
>
> freemarker.template.TemplateException: value!0 evaluated to null number.
> at
> freemarker.core.EvaluationUtil.getNumber(EvaluationUtil.java:117)
> ...
>
> The alternative way is to actually have two different models, and instantiate
> them as required. This is shown below, but it complicates our code quite a
> bit, since we can no longer just instantiate the model object based on our
> underlying data type, but also must care for the underlying *value*:
>
> class MyNullNumberModel implements TemplateHashModelEx {
> ...
> }
>
>
> class MyNotNullNumberModel extends MyNullNumberModel
> implements TemplateNumberModel {
> ...
> public Number getAsNumber() {
> return functionThatReturnsActualNumber();
> }
> }
>
> So, the questions are:
>
> 1) Currently, will I have to do the two-different-model-classes approach,
> or am I missing something?
> 2) In the future, will this change & become simpler?
>
> Eirik
Hmm... I see the problem, but I'm afraid FreeMarker has no operator
for thus. And I think there's a conceptual misunderstanding regarding
what the ! operator meant to do. The ! operator doesn't check if the
left-hand operand has a *scalar* value (let alone if it has a
numerical value), it simply checks if it has any kind of value at all
(but null). It's typically used to checking if we have a foo variable,
or it's missing from the data-model (in which case
TemplateHashModel.get(key) returns null). So what you want here would
be something like foo?has_numberical_value(0), but there's no such
thing in FreeMarker (nor you would want to type it if it had :) ).
So, the second solution wouldn't work either. If in foo!0 the foo
variable is a MyNullNumberModel, since the hash exists, it will just
evaluate to foo, not to 0.
So I'm afraid you will have to have a sub-variable called, for
example, "value". This would be either a TemplateNumberModel or null.
Then you can write this:
${foo.value?0}
while you will also have
${foo.datatype}
${foo.origin}
and so on. On the same time, you can also have ${foo}, which prints a
number (because when it comes to converting to sting, FM will prefer
the numerical value over the hash value), but ! will not work with
that since foo exists even if it's not a number but just a hash.
Do not return null with getAsNumber(). I don't think FM was designed
wit that case in mind, so it pretty much a call for a NPE, even if it
happens to be treated differently as it turns out. Some may point out
that a hack like ${(foo + 0)!0} works (or does it?), but I wouldn't
build on anything like that. It's pretty much an implementation
detail, if not a bug. So it can change.
BTW, if you really must spare the keystrokes when doing things like
${foo.value?0}, then foo could also implement TemplateMethodModelEx,
and then you could write ${foo(0)}. Cryptic, but doable and fast. Or
another solution which is a bit longer is ${foo.or(0)}.
--
Best regards,
Daniel Dekany
------------------------------------------------------------------------------
vRanger cuts backup time in half-while increasing security.
With the market-leading solution for virtual backup and recovery,
you get blazing-fast, flexible, and affordable data protection.
Download your free trial now.
http://p.sf.net/sfu/quest-d2dcopy1