You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@velocity.apache.org by Nathan Bubna <na...@esha.com> on 2001/03/20 21:45:47 UTC

macro & silent mode issue?

i'm trying to use this macro to return an empty value for variables that are
null, because I want the users to see clearly that a value is missing.  it
is meant to be a 'not-quite-silent' mode.

#macro( loudnull $a)
    #if ($a)
        $a
    #else
        --
    #end
#end

this works fine except when i call something like

#loudnull( $foo.getBar() )
[where $foo is not null, but $foo.getBar() is null]

in this case, if ($a) registers as true and i end up with "$foo.getBar()"
being displayed to the user.
silent mode works here as expected,
as does #loudnull( $foo ) [where $foo is null or not]

so far the best way around this i've found is,
#set( $this_really_sucks = $foo.getBar() )
#loudnull( $this_really_sucks )

i don't like that.  am i missing something here or is this a bug?

-Nathan Bubna





Re: macro & silent mode issue?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
Nathan Bubna wrote:
>[SNIP]
> and pass $foo.getBar() to it, then i get the opposite result from loudnull!!
> the results ought to be identical!  i tested various inputs into these and
> found these results
>[CHOP]

All fixed.  Give it a whirl.  I am disappointed I couldn't beat the
listserver...

geir

-- 
Geir Magnusson Jr.                               geirm@optonline.net
Developing for the web?  See http://jakarta.apache.org/velocity/

Re: macro & silent mode issue?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
Nathan Bubna wrote:
> 
> >Geir Magnusson Jr. wrote:
> [snip]
> > I tried to explain this in the first post - I had a choice when I
> > rewrote the how the VMs handle args.  The choice was :
> >
> > In the event of a reference not being in the context, I can either
> >
> >   a) Return the literal representation, just as what happens in regular
> > VTL, which was the pre-existing behavior.
> 
> yes, but since it is has not yet become final output, it should retain the
> functionality of a reference (respond appropriately to testing, quiet
> reference notation, etc.).

I know - it came from the fact that the first rev of VMs was literally a
'patch and parse', taking the args, patching them into the right spot in
the vm body, and rendering that.  So the reference would behave like a
reference :)

But it had other problems...

> 
> >
> >   b) Return null as the value, which I think would be better too.
> >
> > I made the choice that I did to preserve existing behavior, and noted
> > both in the code and in my personal lists to revisit that later. I guess
> > now is later :)
> 
> yeah, I didn't quite get it the first time you told me.  i'm a little slow
> sometimes.

No worries - you had to know the history, or I had to explain better.

> [snip]
> 
> > I am interested to know why you get NPE's.  You shouldn't get the NPE -
> > but you will get notices that an assignement was tried with a null.
> 
> the errors are as follows...
> Thu Mar 22 07:45:11 PST 2001  [error] ASTMethod.execute() : exception :
> java.lang.NullPointerException
> Thu Mar 22 07:45:11 PST 2001  [error] ASTMethod.execute() : exception :
> java.lang.NullPointerException
> Thu Mar 22 07:45:11 PST 2001   [warn]
> org.apache.velocity.runtime.exception.ReferenceException: reference :
> template = /screens/ViewPerson.vm [line 1,column 10] :
> $person.getEmail().toString() is not a valid reference.
> 
> It makes sense to me that these show up...  $person.getEmail() returns a
> null, and those are pretty hard to call toString() on.
> doing #set before calling the VM works fine, but it is enough extra
> keystrokes that it kind of defeats the purpose of having a simple little
> #loudnull macro.   :-(

Gaaaa.  That's not cool.  Will fix.
 
> >
> > Post the 1.0 release (1.5 weeks), I have a solution for that too that
> > doesn't require any changes to templates.
> >
> > Re the problem you noted, I agree with you 100%, and will take a look -
> > if I can't find a clever way out, we will bring it up to the Velocity
> > community to see if choice b) above is acceptable.
> 
> i'm all for it!
> 
> >
> > What that would mean is that #if() would work, but a reference in the VM
> > such as
> >
> > #macro( foo $a )
> >   $a
> > #end
> >
> > #foo( $iamnull )
> >
> > would render as '$a', not $iamnull.
> [snip]
> 
> this is a preferable drawback to me.  in fact, i think this would be
> expected.
> anyway, thanks for your attention!  it is appreciated.

-- 
Geir Magnusson Jr.                               geirm@optonline.net
Developing for the web?  See http://jakarta.apache.org/velocity/

Re: macro & silent mode issue?

Posted by Nathan Bubna <na...@esha.com>.
>Geir Magnusson Jr. wrote:
[snip]
> I tried to explain this in the first post - I had a choice when I
> rewrote the how the VMs handle args.  The choice was :
>
> In the event of a reference not being in the context, I can either
>
>   a) Return the literal representation, just as what happens in regular
> VTL, which was the pre-existing behavior.

yes, but since it is has not yet become final output, it should retain the
functionality of a reference (respond appropriately to testing, quiet
reference notation, etc.).

>
>   b) Return null as the value, which I think would be better too.
>
> I made the choice that I did to preserve existing behavior, and noted
> both in the code and in my personal lists to revisit that later. I guess
> now is later :)

yeah, I didn't quite get it the first time you told me.  i'm a little slow
sometimes.

[snip]

> I am interested to know why you get NPE's.  You shouldn't get the NPE -
> but you will get notices that an assignement was tried with a null.

the errors are as follows...
Thu Mar 22 07:45:11 PST 2001  [error] ASTMethod.execute() : exception :
java.lang.NullPointerException
Thu Mar 22 07:45:11 PST 2001  [error] ASTMethod.execute() : exception :
java.lang.NullPointerException
Thu Mar 22 07:45:11 PST 2001   [warn]
org.apache.velocity.runtime.exception.ReferenceException: reference :
template = /screens/ViewPerson.vm [line 1,column 10] :
$person.getEmail().toString() is not a valid reference.

It makes sense to me that these show up...  $person.getEmail() returns a
null, and those are pretty hard to call toString() on.
doing #set before calling the VM works fine, but it is enough extra
keystrokes that it kind of defeats the purpose of having a simple little
#loudnull macro.   :-(

>
> Post the 1.0 release (1.5 weeks), I have a solution for that too that
> doesn't require any changes to templates.
>
> Re the problem you noted, I agree with you 100%, and will take a look -
> if I can't find a clever way out, we will bring it up to the Velocity
> community to see if choice b) above is acceptable.

i'm all for it!

>
> What that would mean is that #if() would work, but a reference in the VM
> such as
>
> #macro( foo $a )
>   $a
> #end
>
> #foo( $iamnull )
>
> would render as '$a', not $iamnull.
[snip]

this is a preferable drawback to me.  in fact, i think this would be
expected.
anyway, thanks for your attention!  it is appreciated.


Re: macro & silent mode issue?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
Nathan Bubna wrote:
> [SNIP]
> > If you want the value to go in, do a
> >
> > #set($val = $ref.doIt() )
> > #foo( $val )
> 
> hmmm. :(
> ok, but i guess i still don't understand why
> 
>     #macro ( test $a)
>         #if ($a)
>             exists
>         #end
>     #end
> 
>     #test ($foo.getBar() )
> 
> isn't equivalent to
> 
>     #if ($foo.getBar()) exists #end
> 
> even if it is pass-by-reference, when it does get around to evaluating the
> reference, shouldn't it evaluate the same?  it ought to remain a reference,
> and not become a literal String representation of the reference. right?

I tried to explain this in the first post - I had a choice when I
rewrote the how the VMs handle args.  The choice was :

In the event of a reference not being in the context, I can either 
  
  a) Return the literal representation, just as what happens in regular
VTL, which was the pre-existing behavior.

  b) Return null as the value, which I think would be better too.

I made the choice that I did to preserve existing behavior, and noted
both in the code and in my personal lists to revisit that later. I guess
now is later :)

> basically, i've tried everything i can thing of to make this work w/o
> having to do #set and then pass it.  Calling toString() is slightly
> "better," but you end up with a lot of npe's in the log.  I'm stuck here,
> and will stick with this hack for now but not happily.

I am interested to know why you get NPE's.  You shouldn't get the NPE -
but you will get notices that an assignement was tried with a null.

Post the 1.0 release (1.5 weeks), I have a solution for that too that
doesn't require any changes to templates.
 
Re the problem you noted, I agree with you 100%, and will take a look -
if I can't find a clever way out, we will bring it up to the Velocity
community to see if choice b) above is acceptable.

What that would mean is that #if() would work, but a reference in the VM
such as 

#macro( foo $a )
  $a
#end

#foo( $iamnull )

would render as '$a', not $iamnull.

I am hoping for a clever solution.

> Thanks for fixing the bug with String testing,  and i'll try to avoid being
> a PITA about the VM arguments for a while. :)

No - it's a good point.  You are the first to complain, but it's valid.

geir

-- 
Geir Magnusson Jr.                               geirm@optonline.net
Developing for the web?  See http://jakarta.apache.org/velocity/

Re: macro & silent mode issue?

Posted by Nathan Bubna <na...@esha.com>.
> Note well that Velocimacros are 'pass by name' by design, so to speak,
> to allow you to pass a reference with a method and have that method
> called for each instance of the arg in the macro body, not just the
> value. i.e.
[snip]
> This is done to allow stateful things, such as a method that returns a
> series of colors for coloring rows in a table, for example, to be used
> in VMs just like they would if they were used in the VTL.  This feature
> isn't an 'accident' or 'side effect' of implementation.

duly noted and understood.

> If you want the value to go in, do a
>
> #set($val = $ref.doIt() )
> #foo( $val )

hmmm. :(
ok, but i guess i still don't understand why

    #macro ( test $a)
        #if ($a)
            exists
        #end
    #end

    #test ($foo.getBar() )

isn't equivalent to

    #if ($foo.getBar()) exists #end

even if it is pass-by-reference, when it does get around to evaluating the
reference, shouldn't it evaluate the same?  it ought to remain a reference,
and not become a literal String representation of the reference. right?
basically, i've tried everything i can thing of to make this work w/o
having to do #set and then pass it.  Calling toString() is slightly
"better," but you end up with a lot of npe's in the log.  I'm stuck here,
and will stick with this hack for now but not happily.

Thanks for fixing the bug with String testing,  and i'll try to avoid being
a PITA about the VM arguments for a while. :)

Nathan Bubna


Re: macro & silent mode issue?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
Nathan Bubna wrote:
> 
> > The best way I can describe it is a compromise.  I was looking over the
> > code, and I had to make a choice (I wrote the bit that does this.)
> >
> > I did a rewrite of the VM arg stuff a little while back, and the choice
> > was to either duplicate an existing behavior, namely that you get the
> > literal representation of an arg, or switch it so you would get what you
> > want, namely a null.
> >
> > You can see the choice I made :)
> 
> hmmm.  i'm not sure i understand why you would prefer the literal
> representation of the argument here.  i think parameters should be evaluated
> when they are passed into a "method," but i must admit to knowing nothing
> about the internal code of velocity.  is there some hidden benefit to doing
> things this way?

Like I said, it was a choice, a compromise, on the rewrite, to mimic the
existing behavior at that time.

Note well that Velocimacros are 'pass by name' by design, so to speak,
to allow you to pass a reference with a method and have that method
called for each instance of the arg in the macro body, not just the
value. i.e.

#macro( foo $a )
  $a $a $a
#end

#foo( $ref.doIt() )

would call doIt() 3 times, not 1.

This is done to allow stateful things, such as a method that returns a
series of colors for coloring rows in a table, for example, to be used
in VMs just like they would if they were used in the VTL.  This feature
isn't an 'accident' or 'side effect' of implementation.  

If you want the value to go in, do a 

#set($val = $ref.doIt() )
#foo( $val )

The 'pass by name' mechanism allows both you and the person that wants
to pass a method to be repeatedly called the desired functionality.
 
> i suppose then my primary complaint is that this is a very non-intuitive way
> of handling arguments.  when we switch from calling the variable
> $foo.getBar() to $a, i think this ought to be the equivalent of doing the
> #set ($a = $foo.getBar()) directive.

See above.
 
> i would greatly appreciate it if someone would make this change.

Not a chance :)  There is an excellent reason why we do this.  I agree
that it may not be expected, and will update the documentation this
evening.
 
> > So for now, you have to use that method that *really* sucks.
> 
> actually, i've found the easiest way around this problem to be calling
> toString() on all the vars i pass to the VM.  the code is now...
> 
> #loudnull ($foo.getBar().toString())

That works, I guess.  And if we change it such that $a evaluates to null
if $foo.getBar() == null, rather than returning the literal
'$foo.getBar()', it would still work, although I am sure you would want
to fix your VM.

And since it's a VM, there is only one place to do it. :)

> where
> 
> #macro (loudnull $a)
>     #if ($a != "")
>         $a
>     #else
>         --
>     #end
> #end
> 
> in the process of doing this though, i discovered something scary...
> 

Ok....

> if i add another macro in which i flip the conditional statement,
> 
> #macro (revloudnull $a)
>     #if ($a == "")
>         --
>     #else
>         $a
>     #end
> #end
> 
> and pass $foo.getBar() to it, then i get the opposite result from loudnull!!
> the results ought to be identical!  i tested various inputs into these and
> found these results

Yes, you showed a bug there :)

The reason, if you check your log, is that the LHS of the == is a
StringWriter, not a String, and therefore it won't perform the
evaluation.

This is a bug.  I am fixing it now.

Thanks!

 
> passed,      loudnull result,     revloudnull result,     $foo==null?,
> $foo.getX==null?,     actual type of arg,     desired result
> ----------------------------------------------------------------------------
> ----------------------------------------------
> 1. $foo.getBar(),    '--',     '$foo.getBar()',     false,     true,
> null, '--'
> 
> 2. $foo.getBlah(),   '--',     'blah',     false,     false,     Blah (which
> defines toString),     'blah'
> 
> 3. $foo.getBlah().toString(),      'blah',    'blah',     false,     false,
> String,     'blah'
> 
> 4. $test (where $test = $foo.getBar()),     '--',     '$a',    false,
> true,    null,    '--'
> 
> 5. $test (where $test = $foo.getBlah()),    '--',     'blah',     false,
> false,    Blah,    'blah'
> 
> 6. $test (where $test=$foo.getBlah().toString()),    'blah',    'blah',
> false,    false,    String,    'blah'
> 
> (if this is done directly in the template, instead of within a macro the
> results are the same except in #4 revloudnull results in '$test')
> 
> so, the two conditionals which SHOULD act identically when given the exact
> same argument, act differently depending on the object type being tested.
> results 4-6 show that this is not related to the issue with VM arguments
> either (except that #4 gave '$a' instead of '$test' from revloudnull).
> 
> this frightens me.

There is a simple explination. (the bug)  All will be ok.

> 
> > We can revisit this after the release if we want  (like next week) to
> > talk about reversing the behavior (the real solution is something I am
> > committed to pushing to conclusion - namely instead of the lottery
> > approach of
> >
> >   #if( $a )
> >    exists
> >   #else
> >    doesn't
> >   #end
> >
> > lottery ->  what if $a is a 'false' boolean? :)
> >
> > to something like
> >
> >   #if( $a == NULL )
> >
> > so it's clear what you are asking....
> 
> yes, i am aware of the $a being a 'false' boolean difficulty.
> Being able to do #if($a == NULL)  would be much better, but with all the
> weirdness i've run into here, i'm not sure that would solve my problems.

Simple bugfix might - the way the listserver is going, I might have it
done before you get this message.

geir

-- 
Geir Magnusson Jr.                               geirm@optonline.net
Developing for the web?  See http://jakarta.apache.org/velocity/

Re: macro & silent mode issue?

Posted by Nathan Bubna <na...@esha.com>.
> The best way I can describe it is a compromise.  I was looking over the
> code, and I had to make a choice (I wrote the bit that does this.)
>
> I did a rewrite of the VM arg stuff a little while back, and the choice
> was to either duplicate an existing behavior, namely that you get the
> literal representation of an arg, or switch it so you would get what you
> want, namely a null.
>
> You can see the choice I made :)

hmmm.  i'm not sure i understand why you would prefer the literal
representation of the argument here.  i think parameters should be evaluated
when they are passed into a "method," but i must admit to knowing nothing
about the internal code of velocity.  is there some hidden benefit to doing
things this way?

i suppose then my primary complaint is that this is a very non-intuitive way
of handling arguments.  when we switch from calling the variable
$foo.getBar() to $a, i think this ought to be the equivalent of doing the
#set ($a = $foo.getBar()) directive.

i would greatly appreciate it if someone would make this change.

> So for now, you have to use that method that *really* sucks.

actually, i've found the easiest way around this problem to be calling
toString() on all the vars i pass to the VM.  the code is now...

#loudnull ($foo.getBar().toString())

where

#macro (loudnull $a)
    #if ($a != "")
        $a
    #else
        --
    #end
#end

in the process of doing this though, i discovered something scary...

if i add another macro in which i flip the conditional statement,

#macro (revloudnull $a)
    #if ($a == "")
        --
    #else
        $a
    #end
#end

and pass $foo.getBar() to it, then i get the opposite result from loudnull!!
the results ought to be identical!  i tested various inputs into these and
found these results

passed,      loudnull result,     revloudnull result,     $foo==null?,
$foo.getX==null?,     actual type of arg,     desired result
----------------------------------------------------------------------------
----------------------------------------------
1. $foo.getBar(),    '--',     '$foo.getBar()',     false,     true,
null, '--'

2. $foo.getBlah(),   '--',     'blah',     false,     false,     Blah (which
defines toString),     'blah'

3. $foo.getBlah().toString(),      'blah',    'blah',     false,     false,
String,     'blah'

4. $test (where $test = $foo.getBar()),     '--',     '$a',    false,
true,    null,    '--'

5. $test (where $test = $foo.getBlah()),    '--',     'blah',     false,
false,    Blah,    'blah'

6. $test (where $test=$foo.getBlah().toString()),    'blah',    'blah',
false,    false,    String,    'blah'

(if this is done directly in the template, instead of within a macro the
results are the same except in #4 revloudnull results in '$test')

so, the two conditionals which SHOULD act identically when given the exact
same argument, act differently depending on the object type being tested.
results 4-6 show that this is not related to the issue with VM arguments
either (except that #4 gave '$a' instead of '$test' from revloudnull).

this frightens me.

> We can revisit this after the release if we want  (like next week) to
> talk about reversing the behavior (the real solution is something I am
> committed to pushing to conclusion - namely instead of the lottery
> approach of
>
>   #if( $a )
>    exists
>   #else
>    doesn't
>   #end
>
> lottery ->  what if $a is a 'false' boolean? :)
>
> to something like
>
>   #if( $a == NULL )
>
> so it's clear what you are asking....

yes, i am aware of the $a being a 'false' boolean difficulty.
Being able to do #if($a == NULL)  would be much better, but with all the
weirdness i've run into here, i'm not sure that would solve my problems.

Nathan Bubna


Re: macro & silent mode issue?

Posted by "Geir Magnusson Jr." <ge...@optonline.net>.
Nathan Bubna wrote:
> 
> i'm trying to use this macro to return an empty value for variables that are
> null, because I want the users to see clearly that a value is missing.  it
> is meant to be a 'not-quite-silent' mode.
> 
> #macro( loudnull $a)
>     #if ($a)
>         $a
>     #else
>         --
>     #end
> #end
> 
> this works fine except when i call something like
> 
> #loudnull( $foo.getBar() )
> [where $foo is not null, but $foo.getBar() is null]
> 
> in this case, if ($a) registers as true and i end up with "$foo.getBar()"
> being displayed to the user.
> silent mode works here as expected,
> as does #loudnull( $foo ) [where $foo is null or not]
> 
> so far the best way around this i've found is,
> #set( $this_really_sucks = $foo.getBar() )
> #loudnull( $this_really_sucks )
> 
> i don't like that.  am i missing something here or is this a bug?

The best way I can describe it is a compromise.  I was looking over the
code, and I had to make a choice (I wrote the bit that does this.)

I did a rewrite of the VM arg stuff a little while back, and the choice
was to either duplicate an existing behavior, namely that you get the
literal representation of an arg, or switch it so you would get what you
want, namely a null.

You can see the choice I made :)

So for now, you have to use that method that *really* sucks. 

We can revisit this after the release if we want  (like next week) to
talk about reversing the behavior (the real solution is something I am
committed to pushing to conclusion - namely instead of the lottery
approach of 

  #if( $a )
   exists
  #else
   doesn't
  #end

lottery ->  what if $a is a 'false' boolean? :)

to something like

  #if( $a == NULL )

so it's clear what you are asking....

geir


-- 
Geir Magnusson Jr.                               geirm@optonline.net
Developing for the web?  See http://jakarta.apache.org/velocity/