jwalt       2003/09/17 12:36:19

  Modified:    lib/Apache/AxKit/Language/XSP SimpleTaglib.pm
  Log:
  - added Run-Time callable subs
  - added XSP_ prefix to all attrs to make module warning-clean
  - more changes for 'use warnings'
  - implemented XML::Smart parsing instead of childStruct
  - implemented 'default' tag handler
  - implemented nestable object tags
  - updated docs
  
  Revision  Changes    Path
  1.9       +262 -265  xml-axkit/lib/Apache/AxKit/Language/XSP/SimpleTaglib.pm
  
  Index: SimpleTaglib.pm
  ===================================================================
  RCS file: /home/cvs/xml-axkit/lib/Apache/AxKit/Language/XSP/SimpleTaglib.pm,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- SimpleTaglib.pm   18 Jun 2003 13:30:08 -0000      1.8
  +++ SimpleTaglib.pm   17 Sep 2003 19:36:19 -0000      1.9
  @@ -4,17 +4,24 @@
   require 5.006;
   use strict;
   use Apache::AxKit::Language::XSP;
  +use Data::Dumper;
   eval { require WeakRef; };
  +eval { require XML::Smart; };
   use attributes;
  -$Apache::AxKit::Language::XSP::SimpleTaglib::VERSION = 0.1;
  [EMAIL PROTECTED]::AxKit::Language::XSP::SimpleTaglib::ISA = 
('Apache::AxKit::Language::XSP');
  +our $VERSION = 0.3;
  +our @ISA = ('Apache::AxKit::Language::XSP');
   
   # utility functions
   
   sub makeSingleQuoted($) { $_ = shift; s/([\\%])/\\$1/g; 'q%'.$_.'%'; }
  -sub _makeAttributeQuoted(@) { $_ = join(',',@_); s/([\\()])/\\\1/g; 
'('.$_.')'; }
  +sub _makeAttributeQuoted(@) { $_ = join(',',@_); s/([\\()])/\\$1/g; 
'('.$_.')'; }
   sub makeVariableName($) { $_ = shift; s/[^a-zA-Z0-9]/_/g; $_; }
   
  +my $dumper = new Data::Dumper([]);
  +$dumper->Quotekeys(0);
  +$dumper->Terse(1);
  +$dumper->Indent(0);
  +
   # perl attribute handlers
   
   my %handlerAttributes;
  @@ -100,13 +107,16 @@
   
   sub MODIFY_CODE_ATTRIBUTES {
       my ($pkg,$sub,@attr) = @_;
  +    return unless defined $sub;
       my @rest;
       $handlerAttributes{$sub} ||= {};
       my $handlerAttributes = $handlerAttributes{$sub};
       foreach my $a (@attr) {
           #warn("attr: $a");
           my ($attr,$param) = ($a =~ m/([^(]*)(?:\((.*)\))?$/);
  -        $param = eval "q($param)";
  +        my $warn = 0;
  +        $attr =~ s/^XSP_// || $warn++;
  +        $param = (defined $param?eval "q($param)":"");
           my @param = split(/,/,$param);
   
           if ($attr eq 'expr') {
  @@ -130,6 +140,11 @@
           } elsif ($attr eq 'struct') {
               $$handlerAttributes{'result'} = STRUCT;
               $$handlerAttributes{'namespace'} = $param[0];
  +        } elsif ($attr eq 'stack') {
  +            $$handlerAttributes{'stack'} = $param[0];
  +        } elsif ($attr eq 'smart') {
  +            $$handlerAttributes{'smart'} = 1;
  +            $$handlerAttributes{'capture'} = 1;
           } elsif ($attr eq 'nodeAttr') {
               my %namespace;
               while (@param > 1) {
  @@ -165,10 +180,18 @@
               $$handlerAttributes{'keepWS'} = 1;
           } elsif ($attr eq 'captureContent') {
               $$handlerAttributes{'capture'} = 1;
  +        } elsif ($attr eq 'compile') {
  +            $$handlerAttributes{'compile'} = 1;
  +        } elsif ($attr eq 'XSP' && $warn) {
  +            $warn = 0;
  +            $$handlerAttributes{'xsp'} = 1;
           } else {
               push @rest, $a;
  +            $warn = 0;
           }
  +        warn("Please prefix your XSP attributes with 'XSP_' (${pkg}::${sub} 
: $attr)") if $warn;
       }
  +    delete $handlerAttributes{$sub} if not keys %$handlerAttributes;
       return @rest;
   }
   
  @@ -176,28 +199,32 @@
       my ($pkg,$sub) = @_;
       my @attr;
       my $handlerAttributes = $handlerAttributes{$sub};
  +    return () if !defined $handlerAttributes;
       if (exists $$handlerAttributes{'result'}) {
           if ($$handlerAttributes{'result'} == NODELIST) {
  -            push @attr, 
'nodelist'._makeAttributeQuoted($$handlerAttributes{'nodename'});
  +            push @attr, 
'XSP_nodelist'._makeAttributeQuoted($$handlerAttributes{'nodename'});
           } elsif ($$handlerAttributes{'result'} == EXPRORNODELIST) {
  -            push @attr, 
'exprOrNodelist'._makeAttributeQuoted($$handlerAttributes{'nodename'},$$handlerAttributes{'resultparam'},$$handlerAttributes{'resultnode'});
  +            push @attr, 
'XSP_exprOrNodelist'._makeAttributeQuoted($$handlerAttributes{'nodename'},$$handlerAttributes{'resultparam'},$$handlerAttributes{'resultnode'});
           } elsif ($$handlerAttributes{'result'} == NODE) {
  -            push @attr, 
'node'._makeAttributeQuoted($$handlerAttributes{'nodename'});
  +            push @attr, 
'XSP_node'._makeAttributeQuoted($$handlerAttributes{'nodename'});
           } elsif ($$handlerAttributes{'result'} == EXPRORNODE) {
  -            push @attr, 
'exprOrNode'._makeAttributeQuoted($$handlerAttributes{'nodename'},$$handlerAttributes{'resultparam'},$$handlerAttributes{'resultnode'});
  +            push @attr, 
'XSP_exprOrNode'._makeAttributeQuoted($$handlerAttributes{'nodename'},$$handlerAttributes{'resultparam'},$$handlerAttributes{'resultnode'});
           } elsif ($$handlerAttributes{'result'} == EXPR) {
  -            push @attr, 'expr';
  +            push @attr, 'XSP_expr';
           } elsif ($$handlerAttributes{'result'} == STRUCT) {
  -            push @attr, 'struct';
  +            push @attr, 'XSP_struct';
               $attr[-1] .= 
_makeAttributeQuoted($$handlerAttributes{'namespace'})
                 if defined $$handlerAttributes{'namespace'};
           }
       }
  -    push @attr, 
'nodeAttr'._makeAttributeQuoted(%{$$handlerAttributes{'resultattr'}}) if 
$$handlerAttributes{'resultattr'};
  -    push @attr, 'keepWhitespace' if $$handlerAttributes{'keepWS'};
  -    push @attr, 'captureContent' if $$handlerAttributes{'capture'};
  +    push @attr, 
'XSP_nodeAttr'._makeAttributeQuoted(%{$$handlerAttributes{'resultattr'}}) if 
$$handlerAttributes{'resultattr'};
  +    push @attr, 
'XSP_stack'._makeAttributeQuoted($$handlerAttributes{'stack'}) if 
$$handlerAttributes{'stack'};
  +    push @attr, 'XSP_smart' if $$handlerAttributes{'smart'};
  +    push @attr, 'XSP_keepWhitespace' if $$handlerAttributes{'keepWS'};
  +    push @attr, 'XSP_captureContent' if $$handlerAttributes{'capture'};
  +    push @attr, 'XSP_compile' if $$handlerAttributes{'compile'};
   
  -    push @attr, 
'childStruct'._makeAttributeQuoted(serializeChildStructSpec($$handlerAttributes{'struct'},{}))
  +    push @attr, 
'XSP_childStruct'._makeAttributeQuoted(serializeChildStructSpec($$handlerAttributes{'struct'},{}))
           if ($$handlerAttributes{'struct'});
   
       my (@attribs, @children, @both);
  @@ -213,9 +240,10 @@
               push @children, $param;
           }
       }
  -    push @attr, 'attrib'._makeAttributeQuoted(@attribs) if @attribs;
  -    push @attr, 'child'._makeAttributeQuoted(@children) if @children;
  -    push @attr, 'attribOrChild'._makeAttributeQuoted(@both) if @both;
  +    push @attr, 'XSP_attrib'._makeAttributeQuoted(@attribs) if @attribs;
  +    push @attr, 'XSP_child'._makeAttributeQuoted(@children) if @children;
  +    push @attr, 'XSP_attribOrChild'._makeAttributeQuoted(@both) if @both;
  +    push @attr, 'XSP' if [EMAIL PROTECTED];
       return @attr;
   }
   
  @@ -300,6 +328,8 @@
   my %frame = ();
   my @globalframe = ();
   my $structStack;
  +my %stacklevel = ();
  +my %stackcur = ();
   
   # generic tag handler subs
   
  @@ -309,7 +339,7 @@
       return '$attr_'.makeVariableName($tag).' = ""';
   }
   
  -sub set_attribOrChild_value : keepWhitespace {
  +sub set_attribOrChild_value : XSP_keepWhitespace {
       return '; ';
   }
   
  @@ -398,6 +428,19 @@
       return '';
   }
   
  +sub set_XmlSmart_value__open {
  +    my ($e, $tag, %attribs) = @_;
  +    $dumper->Values([\%attribs]);
  +    return 
'XML::Smart::Tree::_Start($xml_subtree_parser,'.makeSingleQuoted($tag).','.$dumper->Dumpxs().');'."\n";
  +}
  +
  +sub set_XmlSmart_value : XSP_captureContent {
  +    my ($e, $tag) = @_;
  +    return 'XML::Smart::Tree::_Char($xml_subtree_parser,$_) if 
(length($_));'."\n".
  +      
'XML::Smart::Tree::_End($xml_subtree_parser,'.makeSingleQuoted($tag).');"";'."\n";
  +}
  +
  +
   # code called from compiled XSP scripts
   sub parse_namespace {
       local( $_ ) = shift;
  @@ -583,9 +626,11 @@
       my $ns = $element->{'NamespaceURI'};
       my $frame = ($frame{$ns} ||= []);
       $structStack = ($structStack{$ns} ||= []);
  -    my $pkg = $Apache::AxKit::Language::XSP::tag_lib{$ns}."::Handlers";
  -    my ($sub, $subOpen);
  +    my $rtpkg = $Apache::AxKit::Language::XSP::tag_lib{$ns};
  +    my $pkg = $rtpkg."::Handlers";
  +    my ($sub, $subOpen, $rtsub, $rtsubOpen);
       my $attribs = {};
  +    my $longtag;
       #warn("full struct: 
".serializeChildStructSpec($$structStack[0][$#{$$structStack[0]}]{'sub'})) if 
$$structStack[0];
       #warn("current node: ".$$structStack[0][0]{'name'}) if $$structStack[0];
       #warn("rest struct: 
".serializeChildStructSpec($$structStack[0][0]{'sub'})) if $$structStack[0];
  @@ -619,20 +664,36 @@
           if (!$sub) {
               my @backframes = (reverse(map{ ${$_}{'name'} } @{$frame}),$tag);
               #warn("frames: "[EMAIL PROTECTED]", backframes: 
".join(",",@backframes));
  -            while (@backframes) {
  -                my $longtag = join('___', @backframes);
  +            my $i = @backframes+1;
  +            while ($i) {
  +                $longtag = join('___', @backframes) || '_default';
                   shift @backframes;
  +                $i--;
                   #warn("checking for $longtag");
                   if ($sub = $pkg->can(makeVariableName($longtag))) {
                       $subOpen = 
$pkg->can(makeVariableName($longtag)."__open");
  -                    last;
                   }
  +                if ($handlerAttributes{$rtsub} and $rtsub = 
$rtpkg->can(makeVariableName($longtag))) {
  +                    $rtsubOpen = 
$rtpkg->can(makeVariableName($longtag)."__open");
  +                }
  +                die("Simultaneous run-time and compile-time handlers for one 
tag not supported") if $sub and $rtsub;
  +                last if $sub or $rtsub;
               }
           }
       }
  -    die "invalid tag: $tag (namespace: $ns, package $pkg, parents ".join(", 
",map{ ${$_}{'name'} } @{$frame}).")" unless $sub;
  -
  -    my $handlerAttributes = $handlerAttributes{$sub};
  +    if (((!$sub && !$rtsub) || $longtag eq '_default') && $frame{smart}) {
  +        $sub = &set_XmlSmart_value;
  +        $subOpen = &set_XmlSmart_value__open;
  +    }
  +    die "invalid tag: $tag (namespace: $ns, package $pkg, parents ".join(", 
",map{ ${$_}{'name'} } @{$frame}).")" unless $sub or $rtsub;
  +
  +    my $handlerAttributes = $handlerAttributes{$sub || $rtsub};
  +    if ($$handlerAttributes{'compile'}) {
  +        $sub = $rtsub;
  +        undef $rtsub;
  +        $subOpen = $rtsubOpen;
  +        undef $rtsubOpen;
  +    }
   
       if ($$handlerAttributes{'result'} == STRUCT || 
!$$handlerAttributes{'result'} ||
           $$handlerAttributes{'result'} == NODELIST ||
  @@ -645,7 +706,7 @@
           # that one doesn't work reliably neither, it probably doesn't make 
any
           # difference
           $e->append_to_script('.') if ($globalframe[0]{'capture'});
  -        $e->append_to_script('do { ');
  +        $e->append_to_script('do { ') if ($element->{Parent});
   
       } elsif ($$handlerAttributes{'result'} == NODE ||
           ($$handlerAttributes{'result'} == EXPRORNODE
  @@ -670,13 +731,30 @@
   
       unshift @{$frame}, {};
       unshift @globalframe,{};
  +    if (!$stacklevel{$rtpkg}) {
  +        $stacklevel{$rtpkg} = [];
  +        $stackcur{$rtpkg} = [0];
  +        $e->append_to_script('my @'.makeVariableName($rtpkg)."_stack = 
({});\n");
  +        $e->append_to_script('my $'.makeVariableName($rtpkg).'_stack = 
$'.makeVariableName($rtpkg)."_stack[0];\n");
  +    }
  +    if ($$handlerAttributes{'stack'}) {
  +        unshift @{$stacklevel{$rtpkg}}, $$handlerAttributes{'stack'};
  +        unshift @{$stackcur{$rtpkg}}, 0;
  +        $e->append_to_script('unshift @'.makeVariableName($rtpkg)."_stack, 
{};\n");
  +        $e->append_to_script('$'.makeVariableName($rtpkg).'_stack = 
$'.makeVariableName($rtpkg)."_stack[0];\n");
  +    } elsif ($attribs{$stacklevel{$rtpkg}[0]}) {
  +        unshift @{$stackcur{$rtpkg}}, 
(@{$stacklevel{$rtpkg}}-$attribs{$stacklevel{$rtpkg}[0]});
  +        $e->append_to_script('$'.makeVariableName($rtpkg).'_stack = 
$'.makeVariableName($rtpkg).'_stack['.$stackcur{$rtpkg}[0]."];\n");
  +    }
       $$frame[0]{'attribs'} = $attribs;
  +    $$frame[0]{'smart'} = $$frame[1]{'smart'};
       $globalframe[0]{'ignoreWS'} = !$$handlerAttributes{'keepWS'};
       $globalframe[0]{'capture'} = $$handlerAttributes{'capture'};
  -    $globalframe[0]{'pkg'} = $pkg;
  -    $globalframe[0]{'ns'} = $pkg;
  +    $globalframe[0]{'pkg'} = ($sub?$pkg:$rtpkg);
  +    $globalframe[0]{'ns'} = ($sub?$pkg:$rtpkg);
       $$frame[0]{'name'} = $tag;
       $$frame[0]{'sub'} = $sub;
  +    $$frame[0]{'rtsub'} = $rtsub;
       if ($$handlerAttributes{'struct'}) {
           unshift @{$structStack}, [{ 'sub' => $$handlerAttributes{'struct'}, 
'name' => $tag }];
           $$frame[0]{'struct'} = 1;
  @@ -692,7 +770,13 @@
       $$frame[0]{'vars'} = $$handlerAttributes{'children'};
   
       $e->append_to_script($subOpen->($e,$tag,%$attribs)) if $subOpen;
  +    
$e->append_to_script("${rtpkg}::".makeVariableName($longtag)."__open(".makeSingleQuoted($tag).",{".join(",",map
 { makeSingleQuoted($_)."=>".makeSingleQuoted($$attribs{$_}) } keys 
%$attribs).'}, $'.makeVariableName($rtpkg)."_stack);\n") if $rtsubOpen;
   
  +    if ($$handlerAttributes{'smart'}) {
  +        $$frame[0]{'smart'} = 1;
  +        $e->append_to_script("my \$xml_subtree_parser = {};\n");
  +        $e->append_to_script(set_XmlSmart_value__open($e,$tag,%attribs));
  +    }
       if ($$handlerAttributes{'capture'}) {
           $e->append_to_script('local($_) = ""');
           $e->{'Current_Element'}->{'SimpleTaglib_SavedNS'} = 
$e->{'Current_Element'}->{'NamespaceURI'};
  @@ -709,11 +793,13 @@
       my $ns = $element->{'NamespaceURI'};
       my $frame = $frame{$ns};
       $structStack = $structStack{$ns};
  -    my $pkg = $Apache::AxKit::Language::XSP::tag_lib{$ns}."::Handlers";
  +    my $rtpkg = $Apache::AxKit::Language::XSP::tag_lib{$ns};
  +    my $pkg = $rtpkg."::Handlers";
       my $longtag;
       my $sub = $$frame[0]{'sub'};
  -    die "invalid closing tag: $tag (namespace: $ns, package $pkg, sub 
".makeVariableName($tag).")" unless $sub;
  -    my $handlerAttributes = $handlerAttributes{$sub};
  +    my $rtsub = $$frame[0]{'rtsub'};
  +    die "invalid closing tag: $tag (namespace: $ns, package $pkg, sub 
".makeVariableName($tag).")" unless $sub or $rtsub;
  +    my $handlerAttributes = $handlerAttributes{$sub || $rtsub};
   
       if ($globalframe[0]{'capture'}) {
           $e->append_to_script('; ');
  @@ -724,10 +810,28 @@
       }
   
       my $attribs = $$frame[0]{'attribs'};
  +    if ($$handlerAttributes{'smart'}) {
  +        $e->append_to_script(set_XmlSmart_value($e,$tag));
  +        $e->append_to_script("my \$xml_subtree = new XML::Smart('');\n");
  +        $e->append_to_script("\$xml_subtree->{tree} = 
XML::Smart::Tree::_Final(\$xml_subtree_parser);\n");
  +    }
       shift @{$structStack} if $$frame[0]{'struct'};
       shift @{$frame};
       shift @globalframe;
  -    $e->append_to_script($sub->($e, $tag, %{$attribs}));
  +    if ($sub) {
  +        $e->append_to_script($sub->($e, $tag, %{$attribs}));
  +    } else {
  +        foreach my $attrib (keys %{$$handlerAttributes{'attribs'}}, keys 
%{$$handlerAttributes{'children'}}) {
  +            if (exists $$handlerAttributes{'children'}{$attrib}) {
  +                $$attribs{$attrib} = '$attr_'.makeVariableName($attrib);
  +            } else {
  +                $$attribs{$attrib} = makeSingleQuoted($$attribs{$attrib});
  +            }
  +        }
  +        $$attribs{$$handlerAttributes{'resultparam'}} = 
makeSingleQuoted($$attribs{$$handlerAttributes{'resultparam'}})
  +          if $$handlerAttributes{'resultparam'};
  +        
$e->append_to_script("${rtpkg}::".makeVariableName($longtag)."(".makeSingleQuoted($tag).",{".join(",",map
 { makeSingleQuoted($_)."=>".$$attribs{$_} } keys 
%$attribs).'},$'.makeVariableName($rtpkg)."_stack,".($$handlerAttributes{'smart'}?'$xml_subtree':'undef').");");
  +    }
   
       if (defined $e->{'Current_Element'}->{'SimpleTaglib_SavedNS'}) {
           $e->{'Current_Element'}->{'NamespaceURI'} = 
$e->{'Current_Element'}->{'SimpleTaglib_SavedNS'};
  @@ -746,8 +850,11 @@
           $e->end_expr();
           end_elem($e);
           $e->append_to_script("} ");
  -        $e->append_to_script('""; ') if ($globalframe[0]{'capture'});
  -        $e->append_to_script("};\n");
  +        if ($globalframe[0]{'capture'}) {
  +            $e->append_to_script("\"\"; }\n");
  +        } else {
  +            $e->append_to_script(" };\n");
  +        }
       } elsif ($$handlerAttributes{'result'} == NODE ||
           ($$handlerAttributes{'result'} == EXPRORNODE
            && $$attribs{$$handlerAttributes{'resultparam'}} eq
  @@ -803,9 +910,18 @@
           if ($globalframe[0]{'capture'}) {
               $e->append_to_script("\"\"; }\n");
           } else {
  -            $e->append_to_script(" };\n");
  +            $e->append_to_script(" };\n") if ($element->{Parent});
           }
       }
  +    if ($$handlerAttributes{'stack'}) {
  +        shift @{$stacklevel{$rtpkg}};
  +        shift @{$stackcur{$rtpkg}};
  +        $e->append_to_script('shift @'.makeVariableName($rtpkg)."_stack;\n");
  +        $e->append_to_script('$'.makeVariableName($rtpkg).'_stack = 
$'.makeVariableName($rtpkg)."_stack[".$stackcur{$rtpkg}[0]."];\n");
  +    } elsif ($$attribs{$stacklevel{$rtpkg}[0]}) {
  +        shift @{$stackcur{$rtpkg}};
  +        $e->append_to_script('$'.makeVariableName($rtpkg).'_stack = 
$'.makeVariableName($rtpkg).'_stack['.$stackcur{$rtpkg}[0]."];\n");
  +    }
       #warn('script len: '.length($e->{XSP_Script}).', end tag: '.$tag);
       return '';
   }
  @@ -828,19 +944,19 @@
       ... more initialization stuff, start_document handler, utility 
functions, whatever
        you like, but no parse_start/end handler needed - if in doubt, just 
leave empty ...
   
  -    package Your::XSP::Package::Handlers;
  -
  -    sub some_tag : attrib(id) attribOrChild(some-param) node(result) 
keepWhitespace {
  -        my ($e, $tag, %attr) = @_;
  -        return 'do_something($attr_some_param,'.$attr{'id'}.');';
  +    sub some_tag : XSP_attrib(id) XSP_attribOrChild(some-param) 
XSP_node(result) XSP_keepWhitespace {
  +        my ($tag, $attr, $stack, $struct) = @_;
  +        return do_something($$attr{'some-param'},$$attr{'id'});
       }
  +    
  +    # old style usage no longer documented, but still supported
   
   
   =head1 DESCRIPTION
   
   This taglib helper allows you to easily write tag handlers with most of the 
common
   behaviours needed. It manages all 'Design Patterns' from the XSP man page 
plus
  -several other useful tag styles.
  +several other useful tag styles, including object-like access as in ESQL.
   
   =head2 Simple handler subs
   
  @@ -849,13 +965,27 @@
   using Perl function attributes. In the rare cases where some action has to 
happen during
   the opening tag event, you may provide a sub "foo__open" (double underscore)
   which will be called at the appropriate time. Usually you would only do that 
for 'if'-
  -style tags which enclose some block of code.
  +style tags which enclose some block of code. 'if'-style tags usually also 
need the C<XSP_compile>
  +flag.
  +
  +Contrary to the former behaviour, your tag handler is called during the XSP 
execution stage,
  +so you should directly return the result value. The C<XSP_compile> flag is 
available to
  +have your handler called in the parse stage, when the XSP script is being 
constructed. Then,
  +it is the responsibility of the handler to return a I<Perl code fragment> to 
be appended to
  +the XSP script.
  +
  +As a comparison, TaglibHelper subs are strictly run-time called, while plain 
taglibs without
  +any helper are strictly compile-time called.
  +
  +B<Warning:> The old usage is still fully supported, but you should not use 
it anymore. It may
  +become deprecated in a future release and will be removed entirely 
afterwards. Porting it
  +to the new style usage is quite easy: remove the line reading "package 
I<your-taglib>::Handler;",
  +then prefix "XSP_" to all Perl attributes (e.g., "childStruct" becomes 
"XSP_childStruct"), and add
  +"XSP_compile" to every handler sub. If after your refactoring some handler 
sub doesn't carry any
  +Perl attribute anymore, add a plain "XSP" Perl attribute.
   
  -It is important to understand that your tag handler is called during the XSP 
parse stage,
  -when the XSP script is being constructed. Therefore, it is the 
responsibility of the
  -handler to return a I<Perl code fragment> to be appended to the XSP script, 
as shown
  -above. Contrast this behaviour to TaglibHelper, where the handler is called 
when the XSP
  -script is being run, and it returns I<data> to be included in the XML output.
  +Perl attributes without the 'XSP_' prefix cause a warning (actually, 
sometimes even two, one from Perl
  +and one from SimpleTaglib), as lower-case Perl attributes are reserved for 
Perl itself.
   
   =head2 Context sensitive handler subs
   
  @@ -869,16 +999,21 @@
   Names for subs and variables get created by replacing any non-alphanumeric 
characters in the
   original tag or attribute to underscores. For example, 'get-id' becomes 
'get_id'.
   
  -The called subs get passed 3 parameters: The parser object, the tag name, 
and an
  -attribute hash. This hash only contains XML attributes declared using the 
'attrib()' Perl
  -function attribute. (Try not to confuse these two meanings of 'attribute' - 
unfortunately
  -XML and Perl both call them that way.) The other declared parameters get 
converted into
  -local variables with prefix 'attr_', or, in the case of 'childStruct', 
converted into the
  -'%_' hash. These local variables are only available inside your code 
fragment which
  +In the default (run-time called) mode, subs get 4 parameters: the tag name, 
a hashref
  +containing attributes and child tags specified through 
C<XSP_child>/C<XSP_attribOrChild>, a
  +hashref for taglib private data (the current C<XSP_stack> hashref, if 
C<XSP_stack> is used),
  +and the C<XSP_smart> object, if applicable.
  +
  +In C<XSP_compile> mode, the called subs get passed 3 parameters: The parser 
object, the tag name,
  +and an attribute hash (no ref!). This hash only contains XML attributes 
declared using the
  +'attrib()' Perl function attribute. (Try not to confuse these two meanings 
of 'attribute' -
  +unfortunately XML and Perl both call them that way.) The other declared 
parameters get converted
  +into local variables with prefix 'attr_', or, in the case of 'XSP_smart', 
converted into the
  +'$xml_subtree' object. These local variables are only available inside your 
code fragment which
   becomes part of the XSP script, unlike the attribute hash which is passed 
directly to
   your handler as the third parameter.
   
  -If a sub has an output attribute ('node', 'expr', etc.), the code fragment 
will be run
  +If a sub has an output attribute ('node', 'expr', etc.), the sub (or code 
fragment) will be run
   in list context. If necessary, returned lists get converted to scalars by 
joining them
   without separation. Code fragments from plain subs (without an output 
attribute) inherit
   their context and have their return value left unmodified.
  @@ -892,17 +1027,21 @@
   
   =item 1.
   
  -If the innermost tag has a 'childStruct' spec which matches, the internal 
childStruct
  -handler takes precedence.
  +If any surrounding tag has a matching 'child' or 'attribOrChild'
  +attribute, the internal handler for the innermost matching tag gets chosen.
   
   =item 2.
   
  -Otherwise, if any surrounding tag has a matching 'child' or 'attribOrChild'
  -attribute, the internal handler for the innermost matching tag gets chosen.
  +If any, the handler sub with the deepest tag hierarchy gets called.
   
   =item 3.
   
  -Otherwise, the handler sub with the deepest tag hierarchy gets called.
  +If any parent tag carries the C<XSP_smart> attribute, the tag is collected 
in the
  +XML::Smart object tree.
  +
  +=item 4.
  +
  +If no handler sub matches, sub "_default" is tried.
   
   =back
   
  @@ -939,28 +1078,33 @@
   
   =head2 Output attributes
   
  -Choose none or one of these to select output behaviour.
  +Choose exactly one of these to select output behaviour.
  +
  +=head3 C<XSP>
  +
  +Makes this tag behave not special at all. If merely flags this sub as being 
a valig tag handler.
  +For security and stability reasons, only subs carrying any XSP attribute are 
available as tags.
   
  -=head3 C<expr>
  +=head3 C<XSP_expr>
   
   Makes this tag behave like an '<xsp:expr>' tag, creating text nodes or 
inline text as appropriate.
   Choose this if you create plain text which may be used everywhere, including 
inside code. This
   attribute has no parameters.
   
  -=head3 C<node(name)>
  +=head3 C<XSP_node(name)>
   
   Makes this tag create an XML node named C<name>. The tag encloses all 
content as well as the
   results of the handler sub.
   Choose this if you want to create one XML node with all your output.
   
  -=head3 C<nodelist(name)>
  +=head3 C<XSP_nodelist(name)>
   
   Makes this tag create a list of XML nodes named C<name>. The tag(s) do not 
enclose content nodes,
   which become preceding siblings of the generated nodes. The return value 
gets converted to a
   node list by enclosing each element with an XML node named C<name>.
   Choose this if you want to create a list of uniform XML nodes with all your 
output.
   
  -=head3 C<exprOrNode(name,attrname,attrvalue)>
  +=head3 C<XSP_exprOrNode(name,attrname,attrvalue)>
   
   Makes this tag behave described under either 'node()' or 'expr', depending 
on the value of
   XML attribute C<attrname>. If that value matches C<attrvalue>, it will work 
like 'node()',
  @@ -968,11 +1112,11 @@
   'node', thus leaving out both parameters means that 'as="node"' will select 
'node()' behaviour.
   Choose this if you want to let the XSP author decide what to generate.
   
  -=head3 C<exprOrNodelist(name,attrname,attrvalue)>
  +=head3 C<XSP_exprOrNodelist(name,attrname,attrvalue)>
   
   Like exprOrNode, selecting between 'expr' and 'nodelist()' behaviour.
   
  -=head3 C<struct>
  +=head3 C<XSP_struct>
   
   Makes this tag create a more complex XML fragment. You may return a single 
hashref or an array
   of hashrefs, which get converted into an XML structure. Each hash element 
may contain a scalar,
  @@ -987,13 +1131,20 @@
   location. You may also return an array of documents/nodes, and you may even 
mix plain hashrefs
   with DOM objects as you desire.
   
  +Finally, you may also return an XML::Simple object.
  +
   In an expression context, passes on the unmodified return value.
   
   =head2 Other output attributes
   
   These may appear more than once and modify output behaviour.
   
  -=head3 C<nodeAttr(name,expr,...)>
  +=head3 C<XSP_compile>
  +
  +Makes this tag called at XSP compile time, not run time. It must return a 
Perl code fragment.
  +For more details, see the sections above.
  +
  +=head3 C<XSP_nodeAttr(name,expr,...)>
   
   Adds an XML attribute named C<name> to all generated nodes. C<expr> gets 
evaluated at run time.
   Evaluation happens once for each generated node. Of course, this tag only 
makes sense with
  @@ -1003,135 +1154,60 @@
   
   These tags specify how input gets handled. Most may appear more than once, 
if that makes sense.
   
  -=head3 C<attrib(name,...)>
  +=head3 C<XSP_attrib(name,...)>
   
   Declares C<name> as a (non-mandatory) XML attribute. All attributes declared 
this way get
   passed to your handler sub in the attribute hash (the third argument to your 
handler).
   
  -=head3 C<child(name,...)>
  +=head3 C<XSP_child(name,...)>
   
   Declares a child tag C<name>. It always lies within the same namespace as 
the taglib itself. The
   contents of the tag, if any, get saved in a local variable named 
$attr_C<name> and made
   available to your code fragment. If the child tag appears more than once, 
the last value
   overrides any previous value.
   
  -=head3 C<attribOrChild(name,...)>
  +=head3 C<XSP_attribOrChild(name,...)>
   
   Declares an attribute or child tag named C<name>. A variable is created just 
like for 'child()',
   containing the attribute or child tag contents. If both appear, the contents 
of the child tag take
   precedence.
   
  -=head3 C<keepWhitespace>
  +=head3 C<XSP_keepWhitespace>
   
   Makes this tag preserve contained whitespace.
   
  -=head3 C<captureContent>
  +=head3 C<XSP_captureContent>
   
   Makes this tag store the enclosed content in '$_' for later retrieval in 
your code fragment instead
   of adding it to the enclosing element. Non-text nodes will not work as 
expected.
   
  -=head3 C<childStruct(spec)>
  -
  -Marks this tag to take a complex XML fragment as input. The resulting data 
structure is available
  -as %_ in your code fragment. Whitespace is always preserved.
  -
  -C<spec> has the following syntax:
  -
  -=over 4
  -
  -=item 1.
  -
  -A C<spec> consists of a list of tag names, separated by whitespace (not 
commas!).
  -
  -=item 2.
  -
  -Tags may appear in any order.
  -
  -=item 3.
  -
  -A tag name prefixed by '@' may appear more than once in the XML document. A 
tag
  -name prefixed by '$' or without any prefix may only appear once.
  -
  -=item 4.
  -
  -If a '{' follows a tag name, that tag has child tags. A valid C<spec> and a
  -closing '}' must follow.
  -
  -=item 5.
  -
  -A tag name prefixed by '*' does not indicate an input tag but specifies the 
name
  -for the text contents of the surrounding tag in the resulting data 
structure. Such a tag name may
  -not bear a '{...}' block.
  -
  -=item 6.
  -
  -Any tag without child tags may also appear as attribute of the parent tag.
  -
  -=item 7.
  +=head3 C<XSP_stack(attrname)>
   
  -A tag name followed by one or more parameter specs in parentheses means a 
hash
  -gets created with the value of the corresponding attribute (or child tag) as 
key. This usage does
  -not forbid appending a '{...}' block, which would result in a nested hash.
  +This will create a stack of objects for your taglib. Each taglib has exactly 
one stack, however.
   
  -=item 8.
  +Stacks work like this:
   
  -A tag name prefixed by '&' denotes a recursive structure. The tag name must 
appear
  -as the name of one of the surrounding '{...}'-blocks. The innermost matching 
block gets chosen.
  +Whenever a tag is encountered that is flagged with C<XSP_stack>, a new empty 
stack frame (hashref) is created.
  +ALL handler subs get this hashref as their third argument where they may 
store/retrieve any data.
   
  -=back
  -
  -Example:
  +If the user wants to access a outer stack frame (object), she may add the 
I<attrname> attribute to ANY of your
  +tags. The value of that attribute specifies how much to go back: 0 is the 
innermost stack frame, 1 is
  +the surrounding one, and so on. The tag with I<attrname> and ALL tags below 
then get the selected stack frame
  +instead of the innermost one until another I<attrname> selects a new frame, 
or another C<XSP_stack> opens a
  +new one.
   
  -Given the following handler sub:
  +Most prominent example of this mode of operation is ESQL, which uses this 
technique to nest SQL queries.
   
  -    set_permission : childStruct([EMAIL PROTECTED] *name} $target 
$comment(lang)(day)} [EMAIL PROTECTED] *name} $target})
  +=head3 C<XSP_smart>
   
  -and the following XML as input:
  +Collects all unknown tags (that are in your taglib's name space) in an 
XML::Smart object and
  +passes it as the fourth element to your handler sub.
   
  -    <set-permission>
  -        <add>
  -            <permission type="user">
  -                foo
  -            </permission>
  -            <permission>
  -                <type>group</type>
  -                bar
  -            </permission>
  -            <target>/test.html</target>
  -            <comment lang="en" day="Sun">Test entry</comment>
  -            <comment lang="en" day="Wed">Test entry 2</comment>
  -            <comment><lang>de</lang>Testeintrag</comment>
  -        </add>
  -        <remove target="/test2.html">
  -            <permission type="user">
  -                baz
  -            </permission>
  -        </remove>
  -    </set-permission>
  -
  -then the local variable '%_' will be made available to your code fragment 
(returned by
  -your set_permission handler sub).  It will be initialized like this:
  -
  -    %_ = (
  -        add => {
  -            permission => [
  -                { type => "user", name => 'foo' },
  -                { type => "group", name => 'bar' },
  -            ],
  -            target => '/test.html',
  -            comment => {
  -                'en' => { 'Sun' => 'Test entry', 'Wed' => 'Test entry 2' },
  -                'de' => { '' => 'Testeintrag' },
  -            }
  -        },
  -        remove => {
  -            permission => [
  -                { type => "user", name => 'baz' },
  -            ],
  -            target => '/test2.html',
  -        },
  -    );
  +You must have XML::Smart installed to use this feature.
   
  +B<Note:> This attribute replaces the former childStruct attribute, which was 
way too complex.
  +The code is not yet removed, however, so legacy taglibs will still work. 
Backwards compatibility
  +will be removed in a future release.
   
   =head1 XML NAMESPACES
   
  @@ -1160,16 +1236,16 @@
   attribute may be used to specify namespaces.  For example, the
   following are equivalent:
   
  -  sub sample_struct : struct {
  -    return "{ '{http://mydomain/NS/other/v1}otherNS:name' => 'value' }";
  +  sub sample_struct : XSP_struct {
  +    return { '{http://mydomain/NS/other/v1}otherNS:name' => 'value' };
     }
   
  -  sub sample_struct : struct {
  -    return q{{
  +  sub sample_struct : XSP_struct {
  +    return {
           'otherNS:name' =>
           { '@xmlns:otherNS' => 'http://mydomain/NS/other/v1',
             '' => 'value' }
  -    }};
  +    };
     }
   
   Namespace scoping in the hashref is patterned after XML documents.
  @@ -1181,36 +1257,36 @@
   To specify a default namespace for all unqualified node names in the
   hashref, state it as a parameter to the C<struct> output attribute:
   
  -  struct(namespaceURI)
  +  XSP_struct(namespaceURI)
   
   You may also specify a prefix:
   
  -  struct({namespaceURI}prefix)
  +  XSP_struct({namespaceURI}prefix)
   
   For example, the following is equivalent to the previous example:
   
  -  sub sample_struct : struct({http://mydomain/NS/other/v1}otherNS) {
  -    return "{ 'name' => 'value' }";
  +  sub sample_struct : XSP_struct({http://mydomain/NS/other/v1}otherNS) {
  +    return { 'name' => 'value' };
     }
   
   To turn off the default namespace for all node names, use an empty
   namespace:
   
  -  sub sample_struct : struct({}) {
  -    return "{ 'name' => 'value' }";
  +  sub sample_struct : XSP_struct({}) {
  +    return { 'name' => 'value' };
     }
   
   By default, XML attributes created with the C<nodeAttr> output
   attribute are not in a namespace.  The curly-braced notation can be
   used to specify a namespace.  For example:
   
  -  
nodeAttr({http://www.w3.org/TR/REC-html40}html:href,'http://www.axkit.org/')
  +  
XSP_nodeAttr({http://www.w3.org/TR/REC-html40}html:href,'http://www.axkit.org/')
   
   If you are specifying more than one attribute in the same namespace,
   you can refer to previously declared namespaces by using the same
   prefix:
   
  -  
nodeAttr({http://www.w3.org/TR/REC-html40}html:href,'http://www.axkit.org/',html:class,'link')
  +  
XSP_nodeAttr({http://www.w3.org/TR/REC-html40}html:href,'http://www.axkit.org/',html:class,'link')
   
   A prefix is required to associate a namespace with an attribute. Default 
namespaces
   (those without a prefix) do not apply to attributes and are ignored.
  @@ -1219,7 +1295,8 @@
   =head1 EXAMPLES
   
   Refer to the Demo tag libraries included in the AxKit distribution and look 
at the source
  -code of AxKit::XSP::Sessions and AxKit::XSP::Auth for full-featured examples.
  +code of AxKit::XSP::Sessions and AxKit::XSP::Auth for full-featured 
examples. Beware, though,
  +they probably still use the old syntax.
   
   =head1 BUGS AND HINTS
   
  @@ -1232,89 +1309,8 @@
   you start using heavy trickery.
   
   If some tags don't work as expected, try surrounding the offending tag with
  -<xsp:content>, this is a common gotcha (but correct and intended). If you 
find yourself needing
  -<xsp:expr> around a tag, please contact the author, as that is probably a 
bug.
  -
  -If you use the '&' flag of childStruct and are reloading your taglib through 
Apache::StatINC or
  -a similar method, consider installing the 'WeakRef' module from CPAN to 
prevent memory leaks. If
  -you never use '&' or don't reload the taglib in the running server, this is 
not necessary.
  -
  -TODO: to be fixed: childStruct currently does not allow hash keys to be 
child nodes, they must be attributes of their
  -parent node. For example, given childStruct(text(lang)), this is valid: 
<text lang="en">foo</text>
  -but this is not: <text><lang>en</lang>foo</text>
  -
  -=head2 Request-time handler
  -
  -TODO: This shall be enhanced in a future release.
  -
  -If you pine for the TaglibHelper-style handlers that get called at request 
time, and you
  -do not need the flexibility of custom-generated code fragments provided by 
SimpleTaglib,
  -you can define a subroutine in your tag library to be called at request time 
instead of
  -at parse time.  Just place a call to your subroutine inside the code 
fragment returned by
  -your handler.  You can even pass it some request-time variables such as $r 
and $cgi.  For
  -example,
  -
  -    package Your::XSP::Package;
  -    use Apache::AxKit::Language::XSP::SimpleTaglib;
  -
  -    sub some_tag {
  -     my($r, $cgi, $some_param) = @_;
  -     # define code here to be run at request time
  -    }
  -
  -    package Your::XSP::Package::Handlers;
  -
  -    sub some_tag : attribOrChild(some-param) node(result) {
  -        'Your::XSP::Package::some_tag($r,$cgi,$attr_some_param);';
  -    }
  -
  -=head2 Using attrib and childStruct together
  -
  -TODO: to be fixed.
  -
  -You may need a list-valued parameter to be specified by XML child tags for 
your tag
  -handler, but you also want the option that a single value can be passed in 
as an XML
  -attribute.  For example:
  -
  -  <yourNS:some_tag>
  -    <yourNS:format>XML</yourNS:format>
  -    <yourNS:format>HTML</yourNS:format>
  -    <yourNS:format>PDF</yourNS:format>
  -  </yourNS:some_tag>
  -
  -  <yourNS:some_tag format="XML"/>
  -
  -The 'attribOrChild' Perl attribute will not suffice here because the child 
tag overwrites
  -the previous value each time instead of creating a list (format will be set 
to 'PDF').
  -What you need is a combination of 'attrib' and 'childStruct':
  -
  -    sub some_tag : attrib(format) childStruct(@format) node(result) {
  -     my ($e, $tag, %attr) = @_;
  -     my $code = '';
  -     if ( defined $attr{format} ) {
  -         my $quoted = 
Apache::AxKit::Language::XSP::makeSingleQuoted($attr{format});
  -         $code .= '$_{format} = ' . $quoted . ' unless defined $_{format};';
  -     }
  -        $code .= 'Your::XSP::Package::some_tag($r,$cgi,%_);';
  -     $code;
  -    }
  -
  -This technique can be generalized to support any number of parameters.  In 
your handler,
  -iterate over the '%attr' hash (defined by 'attrib') and merge the values 
into the '%_'
  -hash (defined by 'childStruct') inside your code fragment.  Remember that 
parameters
  -defined in the childStruct attribute are separated by spaces, not commas.
  -
  -    sub some_tag : attrib(format,option) childStruct(@format @option) 
node(result) {
  -     my ($e, $tag, %attr) = @_;
  -        my $code = '';
  -     while ( my($key, $value) = each %attr ) {
  -         next unless defined $value;
  -         $value = Apache::AxKit::Language::XSP::makeSingleQuoted($value);
  -         $code .= "\$_{'_$key'} = $value unless defined \$_{'_$key'};\n"
  -     }
  -        $code .= 'Your::XSP::Package::some_tag($r,$cgi,%_);';
  -     $code;
  -    }
  +<xsp:content>, this is a common gotcha (but correct and intended). If you 
find you need
  +<xsp:expr> around a tag, please contact the author, that is probably a bug.
   
   =head1 AUTHOR
   
  @@ -1322,11 +1318,12 @@
   
   =head1 COPYRIGHT
   
  +Copyright 2001-2003 Jörg Walter <[EMAIL PROTECTED]>
   All rights reserved. This program is free software; you can redistribute it 
and/or
   modify it under the same terms as AxKit itself.
   
   =head1 SEE ALSO
   
  -AxKit, Apache::AxKit::Language::XSP, 
Apache::AxKit::Language::XSP::TaglibHelper
  +AxKit, Apache::AxKit::Language::XSP, 
Apache::AxKit::Language::XSP::TaglibHelper, XML::Smart
   
   =cut
  
  
  

Reply via email to