You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@forrest.apache.org by Tim Williams <wi...@gmail.com> on 2005/09/07 23:14:40 UTC

my trip through the view-related pipelines

Below is a short and long version of a trip through the view-related
pipelines.  I certainly don't claim to understand every piece of it,
but this exercise has been useful for me to get a good detailed idea
of how views really work beyond the conceptual.  My descriptions may
leave something to be desired but it's worked for me.  I realized last
night that the move to XHTML2  and Views are intimately linked.  Even
if the name and approach changes, I think picking now as a starting
point to "go deep" with them is critical.  To speed up your learning
process, I also pasted in the urls that address each relevant part of
the pipeline -- I think seeing the result along with the code and
description is really helpful.

Hopefully folks that understand better than I, can read and make sure
I'm not spreading errant information here.  I hope this doesn't end up
confusing you more than it helps;)

--tim

Both long and short version go through the generator side of things
first, then the transformer side.  Let me know if something doesn't
make sense, as these were really my notes in analyzing what's going on
and no attempt has been made to actually ensure it logically flows;(

************************************************
          Short Version:
************************************************

The "call chain" through the pipeline keyed on patterns*:

pattern="*.html"

-> pattern="*.page"
--> pattern=(abbreviated)"skinconf.xml"& build-info & tab-* & menu-* & body-*
--> pattern="prepare.view-nugget.**"
---> pattern="prepare.view.**"

-> pattern="getStylesheet.*.**"
--> pattern="prepare.view.**"
--> pattern="prepare.properties.*.**"
---> pattern="prepare.view.**"

Now, in english/psuedo-code:
- call comes in for an *html page
- call to *page aggregates all source data that might ultimately be
needed/used in contracts to present data to the user.
- call to getstylesheet
-- creates a list of all contracts and their properties
-- uses the view template and this contract-property list as the basis
for an aliased stylesheet inserting contract calls as appropriate. 
Two key templates in the generated stylesheet are getHead and getBody.
 The contract-properties list comes with boolean head and body
attributes that are used to determine whether calls to those contracts
should be inserted at that location.
- apply this dynamically generated stylesheet to the aggregated source
document and send it out.



************************************************
          Long Version:
************************************************

***** The Request **** 
URL request is matched by the following match in viewHelper.xhtml
plugin.  This "kicks
off" views.  

    <map:match pattern="*.html">
      <map:generate src="cocoon://{1}.page"/>
      <map:transform src="cocoon://getStylesheet.xhtml.{1}">
        <map:parameter name="path" value="{0}"/>
      </map:transform>
      <map:transform src="resources/stylesheets/strip_namespaces.xsl"/>
      <map:serialize type="xhtml"/>
    </map:match>

******  The Generator Call Chain ********
The following is a look at the call chain from the generator in the 
original request match whose source is cocoon://{1}.page.  

1.  The generator pulls gets its source from the following in the
internal.view plugin:

    <map:match pattern="*.page">
        <map:aggregate element="site">
          <map:part src="cocoon://skinconf.xml"/>
          <map:part src="cocoon://build-info"/>
          <map:part src="cocoon://tab-{1}.html"/>
          <map:part src="cocoon://menu-{1}.html"/>
          <map:part src="cocoon://body-{1}.html"/>
          <map:part src="cocoon:/prepare.view-nugget.{1}"/>
        </map:aggregate>
        <map:serialize type="xml"/>
    </map:match>

As you might expect, this results in a really large page that just
concatenates the different piece-parts -- some obvious, some maybe
not.

skinconf.xml - colors, familiar stuff
build-info - "info" element with version, project-skin, etc.
tab - tabs html chunk
menu - menu html chunk
body - content wrapped in a div id="content"
prepare-view-nugget.{1} - see next step

Example:
http://localhost:8888/index.page
    
2.  This is just an aggregation of all the content that might be
useful.  Unique to views really  the last part,
"prepare.view-nugget.{1}".  This resolves to the following in the same
internal.view plugin.
 
    <map:match pattern="prepare.view-nugget.**">
       <map:generate src="cocoon:/prepare.view.{1}"/>
       <map:transform src="resources/stylesheets/prepare.view.xsl">
         <map:parameter name="view" value="{1}"/>
       </map:transform>
       <map:transform type="xinclude"/>
        <map:serialize type="xml"/>
    </map:match>

This step and the next are described here because they're so close. 
The short story is that this step results in a "forrest:views" root
element to be aggregated in the step above.  The interesting part is
where these come from.  Using the prepare.view.** match below, it
allows these to be defined in multiple places even with different
extensions. Either way, this is all ultimately to get to a resultant
xml fragment that looks like default.fv.  From a views-user
perspective it may be interesting to dive into where/how all these can
be defined
but for a dev trying to understand the view-pipeline, I think we can
consider the default result, which is default.fv in the internal.view
plugin.  I've taken a look at prepare.view.xsl in the above but am not
really sure what it does.  Either my XSLT skills are really bad, or
the transform is really subtle.
http://localhost:8888/prepare.view-nugget.index.html
    

3.  You'll notice that the generator source for this one is the
following match, again in the
same internal.view plugin. 
    
        <map:match pattern="prepare.view.**">
          <map:act type="fallbackResolverAction">
            <map:parameter value="{1}" name="request"/>
            <map:parameter value="{project:theme}" name="projectFallback"/>
            <map:parameter value="{project:theme-ext}" name="projectExtension"/>
            <map:parameter value="{project:content.xdocs}" name="projectDir"/>
            <map:parameter value="{defaults:view-themes}" name="defaultDir"/>
            <map:parameter value="{defaults:theme}" name="defaultFallback"/>
            <map:parameter value="{defaults:theme-ext}"
name="defaultExtension"/>
            
            <map:generate src="file:/{uri}"/>
            <map:transform
src="resources/stylesheets/prepare.include.templates.xsl"/>
            <map:transform type="xinclude"/>
            <map:serialize type="xml"/>
          </map:act>
    </map:match>
   
 It's probably worth noting that when "call-template" is used to call
an external template, the stylesheet above
prepare.include.templates.xsl is responsible for implementing this by 
wapping out "call-template" with "xi-include".
See for yourself with:
http://localhost:8888/prepare.view.index.html

   
Generator Summary:  So, this has essentially gone out and brought
together all of the source data that we might want to use in our
output to the user, making it accessible to the transform within a
single xml document.
   
   
*******  The Transformer Call Chain **********   
Now if you go back up to the original request pattern="*.html", let's
follow the transformer call chain to understand it.

1. The source of the transformer in the original request comes
directly to the following match in the internal.view plugin.

  <map:match pattern="getStylesheet.*.**">
      <map:aggregate element="forrest:filter">
        <map:part src="cocoon://prepare.view.{2}" />
        <map:part src="cocoon://prepare.properties.{1}.{2}" /> 
      </map:aggregate>
      <map:transform src="resources/stylesheets/prepare.{1}.xsl" >
        <map:parameter name="request" value="{2}"/>
        <map:parameter name="forrestContext" value="{forrest:context}"/>
      </map:transform>
      <map:transform type="xinclude"/> 
      <map:serialize type="xml"/>
    </map:match>

See for yourself with:    
http://localhost:8888/getStylesheet.xhtml.index.html 

We've already seen that the call to prepare.view.{2} will give us an
equivalent to default.fv, so we won't cover that again.  First, go
read about this prepare.properties{1}.{2} below, then come back and
read the rest.

.... 

So now we have a list of properties about all of the contracts that
will be used and a view template document roughly equivalent to
default.fv.  Now this, in my mind is where the magic happens.  I am
unable to describe details of *how* the prepare.xhtml.xsl stylesheet
does it's work but through some pretty clever xsl:namespace-alias, the
contracts are turned into an "aliased" stylesheet(see bottom).  This
is xsl at it's best I think.  Someone smarter than I will have to
explain the details but this is roughly what I gather in psuedo-codo:

for each contract in "prepare.properties"
  locate the contract in "prepare.view"
  create an aliased "call-template" appropriate to that contract
next

grossly oversimplified?  yep, but hopefully you get the idea.

now lets take an example:

the following contract shows up in our properties result set:
<forrest:property name="siteinfo-meta" head="true" body="false" format="xhtml"/>

Now in our resultant stylesheet there will be two template calls that
get most things kicked off, they are: getHead and getBody.  The
looping through the properties let us determine which contracts should
have an aliased call-template for them.  Using our example, the
"siteinfo-meta" contract would get identified as needing to get called
in the alias:getHead template.  So the following would get created for
it:

<alias:template name="getHead">
  <alias:call-template name="siteinfo-meta-head"/>
</alias:template>

If there were other contracts that have "head=true" then they would
also get printed there too based on the loops that reside in getHead. 
Aww shucks, let's go ahead and take a look at that too:

<alias:template name="getHead">
  <xsl:for-each 
    select="/*/forrest:properties/*[@head='true' and count(. |
key('head-template', @name)[1]) = 1]">
    <xsl:variable name="name" select="@name"/>
    <xsl:apply-templates mode="head"
      select="//forrest:contract[@name=$name]"/>
  </xsl:for-each>
</alias:template>

This ultimately creates the templae above.  You see that
siteinfo-meta-head is the only contract where @head='true' hense, the
call-template to it.
    
 2.  We can see that it is aggregating two view related parts to
become the forrest:filter
 
     <map:match pattern="prepare.view.**">
       <map:act type="fallbackResolverAction">
         <map:parameter value="{1}" name="request"/>
         <map:parameter value="{project:theme}" name="projectFallback"/>
         <map:parameter value="{project:theme-ext}" name="projectExtension"/>
         <map:parameter value="{project:content.xdocs}" name="projectDir"/>
         <map:parameter value="{defaults:view-themes}" name="defaultDir"/>
         <map:parameter value="{defaults:theme}" name="defaultFallback"/>
         <map:parameter value="{defaults:theme-ext}" name="defaultExtension"/>
        
         <map:generate src="file:/{uri}"/>
         <map:transform
src="resources/stylesheets/prepare.include.templates.xsl"/>
         <map:transform type="xinclude"/>
         <map:serialize type="xml"/>
       </map:act>
    </map:match>
    
    <map:match pattern="prepare.properties.*.**">
      <map:generate src="cocoon:/prepare.view.{2}"/>
        <map:transform src="resources/stylesheets/prepare.properties.xsl">
          <!--Which output format?-->
          <map:parameter name="format" value="{1}"/>
        </map:transform>
        <map:transform type="xinclude"/>
        <map:serialize type="xml"/>
    </map:match>
  
Interestingly, the second part actually makes a request back to the
first as it's generator  source.  It goes and gets the view
(default.fv equivalent) and includes the properties for each contract
using another the cocoon match="get.contract-property.*.xhtml".  You
can see the result of that here:
http://localhost:8888/get.contract-property.siteinfo-meta.xhtml
  
  So now we have what essentially amounts to a list of properties
about all of the contracts, now
  go back to where you left off reading above.
  
  See for yourself here:
  http://localhost:8888/prepare.properties.xhtml.index.html

Transformer Summary:  So the getStylesheet is all about dynamically
creating an xslt based on contracts and view template.  We create a
list of contracts with their properties (essentially because we need
to know where they reside in the resultant xslt: head or body).  We
then go through each contract and create a real xslt call-template to
it (in an alias namespace obviously).

The use of the alias is tricky at first to those of us who aren't xslt
experts.  Once you think about it it's obvious that if you're using an
xslt to transform something into an xslt, then the namespace of the
output xslt would have to be different so as not to confuse whether a
particular xsl: element belonged to the resultant set or the
stylesheet doing the transform.

Re: my trip through the view-related pipelines

Posted by Thorsten Scherler <th...@apache.org>.
On Mon, 2005-09-12 at 01:43 -0700, Diwaker Gupta wrote:
> Wow, just finished reading this. Fantastic work Tim. We should put this in a 
> doc. I'm tied up most of this week, but if no one has taken a stab at it till 
> then, I'll get around to it. 
> 
> Again, great job. Until now I was using views pretty much as a black box. I 
> think I atleast have a vague understanding. And Thorsten, you are officially 
> crowned the XSLT woodoo master ;-)
> 

ROFL :)

Cheers dude. ;-)

> Nothing intelligent to add now, so adios
> 
> Diwaker
-- 
thorsten

"Together we stand, divided we fall!" 
Hey you (Pink Floyd)


Re: my trip through the view-related pipelines

Posted by Diwaker Gupta <di...@apache.org>.
Wow, just finished reading this. Fantastic work Tim. We should put this in a 
doc. I'm tied up most of this week, but if no one has taken a stab at it till 
then, I'll get around to it. 

Again, great job. Until now I was using views pretty much as a black box. I 
think I atleast have a vague understanding. And Thorsten, you are officially 
crowned the XSLT woodoo master ;-)

Nothing intelligent to add now, so adios

Diwaker
-- 
Web/Blog/Gallery: http://floatingsun.net
On Apache: http://people.apache.org/~diwaker

Re: my trip through the view-related pipelines

Posted by Ross Gardler <rg...@apache.org>.
Tim Williams wrote:
> Below is a short and long version of a trip through the view-related
> pipelines.  I certainly don't claim to understand every piece of it,
> but this exercise has been useful for me to get a good detailed idea
> of how views really work beyond the conceptual.  My descriptions may
> leave something to be desired but it's worked for me.  I realized last
> night that the move to XHTML2  and Views are intimately linked.  Even
> if the name and approach changes, I think picking now as a starting
> point to "go deep" with them is critical.  To speed up your learning
> process, I also pasted in the urls that address each relevant part of
> the pipeline -- I think seeing the result along with the code and
> description is really helpful.

Tim - this is fantastic. Really, really helpful. I will digest it fully 
when I have more time, but my speed reading of it confirmed everything I 
thought I'd discovered when we worked on the XHTML2 stuff together.

This should be made into a doc (yes things will change but this is a 
great reference point).

Ross

Re: my trip through the view-related pipelines

Posted by Ross Gardler <rg...@apache.org>.
Thorsten Scherler wrote:

Please cross reference what Thorsten says in this part of this thread, 
with my posting in the "xhtml2 tonights update & questions" [1] about 
this match - it seems we agree on the approach, now we need to get the 
details sorted, Thorsten makes a good start on the details in this mail.

[WARNING] Due to lack of time I have only skim read, so I'm not 
endorsing the approach 100%, just that Thorsten seems to be thinking the 
same as I. I'm willing to bet you folk can get it sorted by the time I 
get freed up again ;-)

[1] http://marc.theaimsgroup.com/?t=112616029300002&r=1&w=2

>><map:match pattern="*.page">
>><map:aggregate element="site">
>><map:part src="cocoon://skinconf.xml"/>
>><map:part src="cocoon://build-info"/>
>><map:part src="cocoon://tab-{1}.html"/>
>><map:part src="cocoon://menu-{1}.html"/>
>><map:part src="cocoon://body-{1}.html"/>
>><map:part src="cocoon:/prepare.view-nugget.{1}"/>
>></map:aggregate>
>><map:serialize type="xml"/>
>></map:match>
>>
>>
> As you might expect, this results in a really large page that just
> 
>>concatenates the different piece-parts -- some obvious, some maybe
>>not.
>>
>>
> 
> That is why we have to only request what we need. More then half of the
> stuff that you bulled point now is either not need or not used or to
> inflexible to override. I will show possible refactoring ideas.
> 
> skinconf.xml - colors, familiar stuff
> 
> <forrest:contracts/> and <forrest:properties/> is completely replacing
> the need for having the skinconf.xml. There are still contracts
> dependent on it but they need to be refactored anyway (xhmtl2). We
> should use the chance to get rid of skinconf.xml. Half of the stuff is
> about placing elements in the old fashion skin the other half is adding
> the properties of the outcome (e.g. copyright notice).
> 
> build-info - "info" element with version, project-skin, etc.
> 
> Should become:
> <forrest:contract name="siteinfo-build">
>     <forrest:properties contract="siteinfo-build">
>       <forrest:property name="siteinfo-build"
> nugget="get.build.info">
>         <url>build-info</url>
>       </forrest:property>
>     </forrest:properties>
> </forrest:contract>
> 
> tab - tabs html chunk
> 
>>menu - menu html chunk
>>
> 
> Should be united as 
> 
> xmap:
> <map:match pattern="**.navigation.xml">
>   <map:generate src="{lm:navigation.xml}"/>
>   <map:transform src="{lm:navigation.xsl}">
>    <map:parameter name="request" value="{1}"/>
>   </map:transform>
>   <map:serialize type="xml"/>
> <map:match/>
> 
> lm:
> <match pattern="*.navigation.xml">
>   <location src="site.xml" />
> </match>
> 
> <match pattern="*.navigation.xsl">
>   <location src="site2navigation.xsl" />
> </match>
> 
> Should be request with e.g. following contract (menu):
> <forrest:contract name="nav-section">
>     <forrest:properties contract="nav-section">
>       <forrest:property name="nav-section"
> nugget="get.navigation">
> <!--NOTE: JX-->
>         <url>${request}.navigation.xml</url>
>       </forrest:property>
>     </forrest:properties>
> </forrest:contract>
> 
> body - content wrapped in a div id="content"
> 
> Which should become:
> xmap:
> <map:match pattern=**.source.xml">
>   <map:generate src="{lm:{0}}"/>
>   <map:serialize type="xml"/>
> <map:match/>
> 
> lm:
> <match pattern="**.source.xml">
>   <location src="get.{0}" />
> </match>
> 
> or (depending whether you want the original source or the xdocs
> [internal format])
> Which should become:
> xmap:
> <map:match pattern=**.internal.xml">
>   <map:generate src="{lm:{0}}"/>
>   <map:serialize type="xml"/>
> <map:match/>
> 
> lm:
> <match pattern="**.internal.xml">
>   <location src="get.{0}" />
> </match>
> 
> prepare-view-nugget.{1} - see next step
> 
>>
> Which should be the only thing left in the above match="**.page":
> xmap:
> <map:match pattern="**.model.xml">
>   <map:generate src="{lm:{0}}"/>
>   <map:serialize type="xml"/>
> <map:match/>
> 
> lm:
> <match pattern="**.model.xml">
>   <location src="get.{0}" />
> </match>
> 
> Example:
> 
>>http://localhost:8888/index.page
>>
>>2. This is just an aggregation of all the content that might be
>>useful. Unique to views really the last part,
>>"prepare.view-nugget.{1}". 
>>
> 
> Yes, this unique part is as well the only thing that should be needed.
> The other aggregation parts should be request by contracts on demand.
> 
> This resolves to the following in the same
> 
>>internal.view plugin.
>>
>><map:match pattern="prepare.view-nugget.**">
>><map:generate src="cocoon:/prepare.view.{1}"/>
>><map:transform src="resources/stylesheets/prepare.view.xsl">
>><map:parameter name="view" value="{1}"/>
>></map:transform>
>><map:transform type="xinclude"/>
>><map:serialize type="xml"/>
>></map:match>
>>
>>
> 
> Which should become:
> xmap:
> <map:match pattern="get.**.model.xml">
>   <map:generate src="{lm:dispatcher.{1}}"/>
>   <map:transform src="{lm:dispatcher2model.xsl">
>     <map:parameter name="view" value="{1}"/>
>   </map:transform>
>   <map:transform type="xinclude"/> 
>   <map:serialize type="xml"/>
> <map:match/>
> 
> 
> This step and the next are described here because they're so close. 
> 
>>The short story is that this step results in a "forrest:views" root
>>element to be aggregated in the step above. The interesting part is
>>where these come from. Using the prepare.view.** match below, it
>>allows these to be defined in multiple places even with different
>>extensions. Either way, this is all ultimately to get to a resultant
>>xml fragment that looks like default.fv. 
> 
> 
> Not exactly. The forrest:structurer is not only a for presentation but
> as well for the models. You are right that in the end the
> forrest:structurer will look nearly the same but with the big difference
> that you will have all your requested models in it. This builds the
> final presentation model.
> 
> 
>>>From a views-user
>>perspective it may be interesting to dive into where/how all these can
>>be defined
>>but for a dev trying to understand the view-pipeline, I think we can
>>consider the default result, which is default.fv in the internal.view
>>plugin. I've taken a look at prepare.view.xsl in the above but am not
>>really sure what it does. Either my XSLT skills are really bad, or
>>the transform is really subtle.
>>http://localhost:8888/prepare.view-nugget.index.html
>>
> 
> 
>  
> Actually that is far more then the default.fv (like I tried to explain
> above). I imaging you have used the default.fv for testing. In this view
> we do not have dynamic includes but that has to change when we want to
> use the full potential.
> 
> On Tue, 2005-09-06 at 02:33 +0200, Thorsten Scherler wrote:
> But a dynamic contract can as well service requests for data from the
> 
>>view by simply providing access to the raw data or by formatting the
>>data. 
>><forrest:contract name="content-feeder">
>>    <forrest:properties contract="content-feeder">
>>      <forrest:property name="content-feeder"
>>nugget="get.nugget.feeder">
>>        <url>feeds/somefeed.xml</url>
>>      </forrest:property>
>>    </forrest:properties>
>></forrest:contract>
> 
> 
> Your example that you used actually were not requesting such external
> data. The above example will request rss feed from cocoon://{url}"/>
> 
> 3. You'll notice that the generator source for this one is the
> 
>>following match, again in the
>>same internal.view plugin. 
>>
>><map:match pattern="prepare.view.**">
>><map:act type="fallbackResolverAction">
>><map:parameter value="{1}" name="request"/>
>><map:parameter value="{project:theme}" name="projectFallback"/>
>><map:parameter value="{project:theme-ext}" name="projectExtension"/>
>><map:parameter value="{project:content.xdocs}" name="projectDir"/>
>><map:parameter value="{defaults:view-themes}" name="defaultDir"/>
>><map:parameter value="{defaults:theme}" name="defaultFallback"/>
>><map:parameter value="{defaults:theme-ext}"
>>name="defaultExtension"/>
>>
>><map:generate src="file:/{uri}"/>
>><map:transform
>>src="resources/stylesheets/prepare.include.templates.xsl"/>
>><map:transform type="xinclude"/>
>><map:serialize type="xml"/>
>></map:act>
>></map:match>
>>
> 
> That has to be changed like:
> xmap:
> <map:match pattern="dispatcher.**">
> <!--NOTE: JX-->
>   <map:generate src="{lm:{0}}" type="jx">
>     <map:parameter value="{1}" name="request"/>
>   </map:generate>
>   <map:serialize type="xml"/>
> </map:match>
> 
> lm: -> missing the source type action
> <match pattern="dispatcher.**">
>   <select type="exists">
> <!-- File-based - project -->
>    <location src="{1}{project:theme-ext}" />
>    <act type="RecursiveDirectoryTraversalAction">
>     <parameter value="{1}" name="request"/>
>     <parameter value="{project:theme}" name="projectFallback"/>
>     <parameter value="{project:theme-ext}" name="projectExtension"/>
>     <parameter value="{project:content.xdocs}" name="projectDir"/>
> <!-- Directory-based / Parent-directory based (recursively) - project
> -->
>     <location src="{uri}" />
>    </act>
> <!-- Theme based - default -->
>    <location
> src="{defaults:view-themes}/{project:theme}{project:theme-ext}" />
> <!-- Default theme based - default -->
>    <location
> src="{defaults:view-themes}/{defaults:theme}{defaults:theme-ext}" />
>   </select>
> </match>
> 
> 
>>It's probably worth noting that when "call-template" is used to call
>>an external template, the stylesheet above
>>prepare.include.templates.xsl is responsible for implementing this by 
>>wapping out "call-template" with "xi-include".
> 
> 
> That should be done with jx rather then the stylesheet/xi-include.
> 
> 
>>See for yourself with:
>>http://localhost:8888/prepare.view.index.html
>>
>>
>>Generator Summary: So, this has essentially gone out and brought
>>together all of the source data that we might want to use in our
>>output to the user, making it accessible to the transform within a
>>single xml document.
>>
> 
> 
> See my notes above how it should be resolved/dispatched.
> 
> 
>>******* The Transformer Call Chain ********** 
>>Now if you go back up to the original request pattern="*.html", let's
>>follow the transformer call chain to understand it.
>>
>>1. The source of the transformer in the original request comes
>>directly to the following match in the internal.view plugin.
>>
>><map:match pattern="getStylesheet.*.**">
>><map:aggregate element="forrest:filter">
>><map:part src="cocoon://prepare.view.{2}" />
>><map:part src="cocoon://prepare.properties.{1}.{2}" /> 
>></map:aggregate>
>><map:transform src="resources/stylesheets/prepare.{1}.xsl" >
>><map:parameter name="request" value="{2}"/>
>><map:parameter name="forrestContext" value="{forrest:context}"/>
>></map:transform>
>><map:transform type="xinclude"/> 
>><map:serialize type="xml"/>
>></map:match>
>>
> 
> This part is heavily build on xsl and alias-xsl. It is quite heavy to
> understand and I am still thinking how to re-factor that. I reckon it
> should be done with Java rather then xsl but I am unsure whether to
> write a generator or a transformer instead. Both have their pros and
> cons. Maybe somebody has an idea/advice?
> 
> 
>>See for yourself with: 
>>http://localhost:8888/getStylesheet.xhtml.index.html 
>>
>>We've already seen that the call to prepare.view.{2} will give us an
>>equivalent to default.fv, so we won't cover that again. First, go
>>read about this prepare.properties{1}.{2} below, then come back and
>>read the rest.
>>
>>.... 
>>
>>So now we have a list of properties about all of the contracts that
>>will be used and a view template document roughly equivalent to
>>default.fv. Now this, in my mind is where the magic happens. I am
>>unable to describe details of *how* the prepare.xhtml.xsl stylesheet
>>does it's work but through some pretty clever xsl:namespace-alias, the
>>contracts are turned into an "aliased" stylesheet(see bottom). This
>>is xsl at it's best I think. Someone smarter than I will have to
>>explain the details but this is roughly what I gather in psuedo-codo:
>>
>>for each contract in "prepare.properties"
>>locate the contract in "prepare.view"
>>create an aliased "call-template" appropriate to that contract
>>next
>>
>>grossly oversimplified? yep, but hopefully you get the idea.
>>
> 
> 
> No, perfectly explained. :) The very detail of how I finally got it
> working are not so important because this solution has one really big
> downside that it depends heavily on the boolean @head and @body. We need
> something smarter that we can better reuse in different formats. ;-) 
> 
> ...but so true, here is where the magic happens.  ;-)
> 
> 
>>now lets take an example:
>>
>>the following contract shows up in our properties result set:
>><forrest:property name="siteinfo-meta" head="true" body="false"
> 
> format="xhtml"/>
> 
>>Now in our resultant stylesheet there will be two template calls that
>>get most things kicked off, they are: getHead and getBody. The
>>looping through the properties let us determine which contracts should
>>have an aliased call-template for them. Using our example, the
>>"siteinfo-meta" contract would get identified as needing to get called
>>in the alias:getHead template. So the following would get created for
>>it:
>>
>><alias:template name="getHead">
>><alias:call-template name="siteinfo-meta-head"/>
>></alias:template>
>>
>>If there were other contracts that have "head=true" then they would
>>also get printed there too based on the loops that reside in getHead. 
>>Aww shucks, let's go ahead and take a look at that too:
>>
>><alias:template name="getHead">
>><xsl:for-each 
>>select="/*/forrest:properties/*[@head='true' and count(. |
>>key('head-template', @name)[1]) = 1]">
>><xsl:variable name="name" select="@name"/>
>><xsl:apply-templates mode="head"
>>select="//forrest:contract[@name=$name]"/>
>></xsl:for-each>
>></alias:template>
>>
> 
> Isn't that pretty. ;-)
> 
> 
>>This ultimately creates the templae above. You see that
>>siteinfo-meta-head is the only contract where @head='true' hense, the
>>call-template to it.
>>
>>2. We can see that it is aggregating two view related parts to
>>become the forrest:filter
>>
> 
> ...
> 
> 
>><map:match pattern="prepare.properties.*.**">
>><map:generate src="cocoon:/prepare.view.{2}"/>
>><map:transform src="resources/stylesheets/prepare.properties.xsl">
>><!--Which output format?-->
>><map:parameter name="format" value="{1}"/>
>></map:transform>
>><map:transform type="xinclude"/>
>><map:serialize type="xml"/>
>></map:match>
>>
>>Interestingly, the second part actually makes a request back to the
>>first as it's generator source. It goes and gets the view
>>(default.fv equivalent) and includes the properties for each contract
>>using another the cocoon match="get.contract-property.*.xhtml". You
>>can see the result of that here:
>>http://localhost:8888/get.contract-property.siteinfo-meta.xhtml
>>
> 
> 
> The problem lies in *how* the get.contract-property.*.xhtml
> transformation is picking up the properties from the contract to know
> where it has to be placed later on. It is using the attribute of the
> contract for it we have to find another way for that.
> 
> 
>>So now we have what essentially amounts to a list of properties
>>about all of the contracts, now
>>go back to where you left off reading above.
>>
>>See for yourself here:
>>http://localhost:8888/prepare.properties.xhtml.index.html
>>
>>Transformer Summary: So the getStylesheet is all about dynamically
>>creating an xslt based on contracts and view template. We create a
>>list of contracts with their properties (essentially because we need
>>to know where they reside in the resultant xslt: head or body). We
>>then go through each contract and create a real xslt call-template to
>>it (in an alias namespace obviously).
>>
>>The use of the alias is tricky at first to those of us who aren't xslt
>>experts. Once you think about it it's obvious that if you're using an
>>xslt to transform something into an xslt, then the namespace of the
>>output xslt would have to be different so as not to confuse whether a
>>particular xsl: element belonged to the resultant set or the
>>stylesheet doing the transform.
> 
> 
> Yes,  if we would not use the alias namespace everything would be
> interpreted as transformation instructions and would be applied in the
> prepare.html.xsl.
> 
> Thanks, Tim, for starting this thread it is very important to understand
> what is going on to be able to re-factor views into the core. Still this
> is only 1/3 of all involved pipelines and we have a long way to go. ;-)
> We should use POJO were ever it make sense to better reuse the code.
> Like Ross stated in another mail view pipes have some similarities with
> method and functions. I always thought about this implementation as
> prototype and think that we should mainly use xsl instead of java where
> appropriate. That is the reason for the architecture, that should make
> it easy to migrate to java.
> 
> salu2


Re: my trip through the view-related pipelines

Posted by Thorsten Scherler <th...@apache.org>.
:)

First of all good to have this fresh wind blowing in my face. ;-)
I really like having you on board.

[NOTE: I snipped the mail apart to fit my explanations and visions of
views]

[NOTE: I assume we are using jx for the forrest:structurer]

************************************************
> Long Version:
> ************************************************
> 
> ***** The Request **** 
> URL request is matched by the following match in viewHelper.xhtml
> plugin. This "kicks
> off" views. 
> 
> 
The situation that we have right now is that:

<map:match pattern="*.html">
> <map:generate src="cocoon://{1}.page"/>
> <map:transform src="cocoon://getStylesheet.xhtml.{1}">
> <map:parameter name="path" value="{0}"/>
> </map:transform>
> <map:transform src="resources/stylesheets/strip_namespaces.xsl"/>
> <map:serialize type="xhtml"/>
> </map:match>
> 
> 
Which should become:
xmap:
<map:match pattern="**.html">
  <map:generate src="{lm:{0}.model.xml}"/>
  <map:transform src="{lm:{0}.viewHelper.xsl}">
  <map:serialize type="xml"/>
<map:match/>

lm:
<match pattern="**.model.xml">
  <location src="cocoon://get.{0}" />
</match>

<match pattern="**.viewHelper.xsl">
  <location src="cocoon://get.{0}" />
</match>

****** The Generator Call Chain ********
> The following is a look at the call chain from the generator in the 
> original request match whose source is cocoon://{1}.page. 
> 
> 
The situation that we have right now is that:

1. The generator pulls gets its source from the following in the
> internal.view plugin:
> 
> <map:match pattern="*.page">
> <map:aggregate element="site">
> <map:part src="cocoon://skinconf.xml"/>
> <map:part src="cocoon://build-info"/>
> <map:part src="cocoon://tab-{1}.html"/>
> <map:part src="cocoon://menu-{1}.html"/>
> <map:part src="cocoon://body-{1}.html"/>
> <map:part src="cocoon:/prepare.view-nugget.{1}"/>
> </map:aggregate>
> <map:serialize type="xml"/>
> </map:match>
> 
> 
As you might expect, this results in a really large page that just
> concatenates the different piece-parts -- some obvious, some maybe
> not.
> 
> 
That is why we have to only request what we need. More then half of the
stuff that you bulled point now is either not need or not used or to
inflexible to override. I will show possible refactoring ideas.

skinconf.xml - colors, familiar stuff
> 
<forrest:contracts/> and <forrest:properties/> is completely replacing
the need for having the skinconf.xml. There are still contracts
dependent on it but they need to be refactored anyway (xhmtl2). We
should use the chance to get rid of skinconf.xml. Half of the stuff is
about placing elements in the old fashion skin the other half is adding
the properties of the outcome (e.g. copyright notice).

build-info - "info" element with version, project-skin, etc.
> 
Should become:
<forrest:contract name="siteinfo-build">
    <forrest:properties contract="siteinfo-build">
      <forrest:property name="siteinfo-build"
nugget="get.build.info">
        <url>build-info</url>
      </forrest:property>
    </forrest:properties>
</forrest:contract>

tab - tabs html chunk
> menu - menu html chunk
> 
Should be united as 

xmap:
<map:match pattern="**.navigation.xml">
  <map:generate src="{lm:navigation.xml}"/>
  <map:transform src="{lm:navigation.xsl}">
   <map:parameter name="request" value="{1}"/>
  </map:transform>
  <map:serialize type="xml"/>
<map:match/>

lm:
<match pattern="*.navigation.xml">
  <location src="site.xml" />
</match>

<match pattern="*.navigation.xsl">
  <location src="site2navigation.xsl" />
</match>

Should be request with e.g. following contract (menu):
<forrest:contract name="nav-section">
    <forrest:properties contract="nav-section">
      <forrest:property name="nav-section"
nugget="get.navigation">
<!--NOTE: JX-->
        <url>${request}.navigation.xml</url>
      </forrest:property>
    </forrest:properties>
</forrest:contract>

body - content wrapped in a div id="content"
> 
Which should become:
xmap:
<map:match pattern=**.source.xml">
  <map:generate src="{lm:{0}}"/>
  <map:serialize type="xml"/>
<map:match/>

lm:
<match pattern="**.source.xml">
  <location src="get.{0}" />
</match>

or (depending whether you want the original source or the xdocs
[internal format])
Which should become:
xmap:
<map:match pattern=**.internal.xml">
  <map:generate src="{lm:{0}}"/>
  <map:serialize type="xml"/>
<map:match/>

lm:
<match pattern="**.internal.xml">
  <location src="get.{0}" />
</match>

prepare-view-nugget.{1} - see next step
> 
> 
Which should be the only thing left in the above match="**.page":
xmap:
<map:match pattern="**.model.xml">
  <map:generate src="{lm:{0}}"/>
  <map:serialize type="xml"/>
<map:match/>

lm:
<match pattern="**.model.xml">
  <location src="get.{0}" />
</match>

Example:
> http://localhost:8888/index.page
> 
> 2. This is just an aggregation of all the content that might be
> useful. Unique to views really the last part,
> "prepare.view-nugget.{1}". 
> 
Yes, this unique part is as well the only thing that should be needed.
The other aggregation parts should be request by contracts on demand.

This resolves to the following in the same
> internal.view plugin.
> 
> <map:match pattern="prepare.view-nugget.**">
> <map:generate src="cocoon:/prepare.view.{1}"/>
> <map:transform src="resources/stylesheets/prepare.view.xsl">
> <map:parameter name="view" value="{1}"/>
> </map:transform>
> <map:transform type="xinclude"/>
> <map:serialize type="xml"/>
> </map:match>
> 
> 

Which should become:
xmap:
<map:match pattern="get.**.model.xml">
  <map:generate src="{lm:dispatcher.{1}}"/>
  <map:transform src="{lm:dispatcher2model.xsl">
    <map:parameter name="view" value="{1}"/>
  </map:transform>
  <map:transform type="xinclude"/> 
  <map:serialize type="xml"/>
<map:match/>


This step and the next are described here because they're so close. 
> The short story is that this step results in a "forrest:views" root
> element to be aggregated in the step above. The interesting part is
> where these come from. Using the prepare.view.** match below, it
> allows these to be defined in multiple places even with different
> extensions. Either way, this is all ultimately to get to a resultant
> xml fragment that looks like default.fv. 

Not exactly. The forrest:structurer is not only a for presentation but
as well for the models. You are right that in the end the
forrest:structurer will look nearly the same but with the big difference
that you will have all your requested models in it. This builds the
final presentation model.

> From a views-user
> perspective it may be interesting to dive into where/how all these can
> be defined
> but for a dev trying to understand the view-pipeline, I think we can
> consider the default result, which is default.fv in the internal.view
> plugin. I've taken a look at prepare.view.xsl in the above but am not
> really sure what it does. Either my XSLT skills are really bad, or
> the transform is really subtle.
> http://localhost:8888/prepare.view-nugget.index.html
> 

 
Actually that is far more then the default.fv (like I tried to explain
above). I imaging you have used the default.fv for testing. In this view
we do not have dynamic includes but that has to change when we want to
use the full potential.

On Tue, 2005-09-06 at 02:33 +0200, Thorsten Scherler wrote:
But a dynamic contract can as well service requests for data from the
> view by simply providing access to the raw data or by formatting the
> data. 
> <forrest:contract name="content-feeder">
>     <forrest:properties contract="content-feeder">
>       <forrest:property name="content-feeder"
> nugget="get.nugget.feeder">
>         <url>feeds/somefeed.xml</url>
>       </forrest:property>
>     </forrest:properties>
> </forrest:contract>

Your example that you used actually were not requesting such external
data. The above example will request rss feed from cocoon://{url}"/>

3. You'll notice that the generator source for this one is the
> following match, again in the
> same internal.view plugin. 
> 
> <map:match pattern="prepare.view.**">
> <map:act type="fallbackResolverAction">
> <map:parameter value="{1}" name="request"/>
> <map:parameter value="{project:theme}" name="projectFallback"/>
> <map:parameter value="{project:theme-ext}" name="projectExtension"/>
> <map:parameter value="{project:content.xdocs}" name="projectDir"/>
> <map:parameter value="{defaults:view-themes}" name="defaultDir"/>
> <map:parameter value="{defaults:theme}" name="defaultFallback"/>
> <map:parameter value="{defaults:theme-ext}"
> name="defaultExtension"/>
> 
> <map:generate src="file:/{uri}"/>
> <map:transform
> src="resources/stylesheets/prepare.include.templates.xsl"/>
> <map:transform type="xinclude"/>
> <map:serialize type="xml"/>
> </map:act>
> </map:match>
> 

That has to be changed like:
xmap:
<map:match pattern="dispatcher.**">
<!--NOTE: JX-->
  <map:generate src="{lm:{0}}" type="jx">
    <map:parameter value="{1}" name="request"/>
  </map:generate>
  <map:serialize type="xml"/>
</map:match>

lm: -> missing the source type action
<match pattern="dispatcher.**">
  <select type="exists">
<!-- File-based - project -->
   <location src="{1}{project:theme-ext}" />
   <act type="RecursiveDirectoryTraversalAction">
    <parameter value="{1}" name="request"/>
    <parameter value="{project:theme}" name="projectFallback"/>
    <parameter value="{project:theme-ext}" name="projectExtension"/>
    <parameter value="{project:content.xdocs}" name="projectDir"/>
<!-- Directory-based / Parent-directory based (recursively) - project
-->
    <location src="{uri}" />
   </act>
<!-- Theme based - default -->
   <location
src="{defaults:view-themes}/{project:theme}{project:theme-ext}" />
<!-- Default theme based - default -->
   <location
src="{defaults:view-themes}/{defaults:theme}{defaults:theme-ext}" />
  </select>
</match>


> It's probably worth noting that when "call-template" is used to call
> an external template, the stylesheet above
> prepare.include.templates.xsl is responsible for implementing this by 
> wapping out "call-template" with "xi-include".

That should be done with jx rather then the stylesheet/xi-include.

> See for yourself with:
> http://localhost:8888/prepare.view.index.html
> 
> 
> Generator Summary: So, this has essentially gone out and brought
> together all of the source data that we might want to use in our
> output to the user, making it accessible to the transform within a
> single xml document.
> 

See my notes above how it should be resolved/dispatched.

> 
> ******* The Transformer Call Chain ********** 
> Now if you go back up to the original request pattern="*.html", let's
> follow the transformer call chain to understand it.
> 
> 1. The source of the transformer in the original request comes
> directly to the following match in the internal.view plugin.
> 
> <map:match pattern="getStylesheet.*.**">
> <map:aggregate element="forrest:filter">
> <map:part src="cocoon://prepare.view.{2}" />
> <map:part src="cocoon://prepare.properties.{1}.{2}" /> 
> </map:aggregate>
> <map:transform src="resources/stylesheets/prepare.{1}.xsl" >
> <map:parameter name="request" value="{2}"/>
> <map:parameter name="forrestContext" value="{forrest:context}"/>
> </map:transform>
> <map:transform type="xinclude"/> 
> <map:serialize type="xml"/>
> </map:match>
> 

This part is heavily build on xsl and alias-xsl. It is quite heavy to
understand and I am still thinking how to re-factor that. I reckon it
should be done with Java rather then xsl but I am unsure whether to
write a generator or a transformer instead. Both have their pros and
cons. Maybe somebody has an idea/advice?

> See for yourself with: 
> http://localhost:8888/getStylesheet.xhtml.index.html 
> 
> We've already seen that the call to prepare.view.{2} will give us an
> equivalent to default.fv, so we won't cover that again. First, go
> read about this prepare.properties{1}.{2} below, then come back and
> read the rest.
> 
> .... 
> 
> So now we have a list of properties about all of the contracts that
> will be used and a view template document roughly equivalent to
> default.fv. Now this, in my mind is where the magic happens. I am
> unable to describe details of *how* the prepare.xhtml.xsl stylesheet
> does it's work but through some pretty clever xsl:namespace-alias, the
> contracts are turned into an "aliased" stylesheet(see bottom). This
> is xsl at it's best I think. Someone smarter than I will have to
> explain the details but this is roughly what I gather in psuedo-codo:
> 
> for each contract in "prepare.properties"
> locate the contract in "prepare.view"
> create an aliased "call-template" appropriate to that contract
> next
> 
> grossly oversimplified? yep, but hopefully you get the idea.
> 

No, perfectly explained. :) The very detail of how I finally got it
working are not so important because this solution has one really big
downside that it depends heavily on the boolean @head and @body. We need
something smarter that we can better reuse in different formats. ;-) 

...but so true, here is where the magic happens.  ;-)

> now lets take an example:
> 
> the following contract shows up in our properties result set:
> <forrest:property name="siteinfo-meta" head="true" body="false"
format="xhtml"/>
> 
> Now in our resultant stylesheet there will be two template calls that
> get most things kicked off, they are: getHead and getBody. The
> looping through the properties let us determine which contracts should
> have an aliased call-template for them. Using our example, the
> "siteinfo-meta" contract would get identified as needing to get called
> in the alias:getHead template. So the following would get created for
> it:
> 
> <alias:template name="getHead">
> <alias:call-template name="siteinfo-meta-head"/>
> </alias:template>
> 
> If there were other contracts that have "head=true" then they would
> also get printed there too based on the loops that reside in getHead. 
> Aww shucks, let's go ahead and take a look at that too:
> 
> <alias:template name="getHead">
> <xsl:for-each 
> select="/*/forrest:properties/*[@head='true' and count(. |
> key('head-template', @name)[1]) = 1]">
> <xsl:variable name="name" select="@name"/>
> <xsl:apply-templates mode="head"
> select="//forrest:contract[@name=$name]"/>
> </xsl:for-each>
> </alias:template>
> 

Isn't that pretty. ;-)

> This ultimately creates the templae above. You see that
> siteinfo-meta-head is the only contract where @head='true' hense, the
> call-template to it.
> 
> 2. We can see that it is aggregating two view related parts to
> become the forrest:filter
> 
...

> 
> <map:match pattern="prepare.properties.*.**">
> <map:generate src="cocoon:/prepare.view.{2}"/>
> <map:transform src="resources/stylesheets/prepare.properties.xsl">
> <!--Which output format?-->
> <map:parameter name="format" value="{1}"/>
> </map:transform>
> <map:transform type="xinclude"/>
> <map:serialize type="xml"/>
> </map:match>
> 
> Interestingly, the second part actually makes a request back to the
> first as it's generator source. It goes and gets the view
> (default.fv equivalent) and includes the properties for each contract
> using another the cocoon match="get.contract-property.*.xhtml". You
> can see the result of that here:
> http://localhost:8888/get.contract-property.siteinfo-meta.xhtml
> 

The problem lies in *how* the get.contract-property.*.xhtml
transformation is picking up the properties from the contract to know
where it has to be placed later on. It is using the attribute of the
contract for it we have to find another way for that.

> So now we have what essentially amounts to a list of properties
> about all of the contracts, now
> go back to where you left off reading above.
> 
> See for yourself here:
> http://localhost:8888/prepare.properties.xhtml.index.html
> 
> Transformer Summary: So the getStylesheet is all about dynamically
> creating an xslt based on contracts and view template. We create a
> list of contracts with their properties (essentially because we need
> to know where they reside in the resultant xslt: head or body). We
> then go through each contract and create a real xslt call-template to
> it (in an alias namespace obviously).
> 
> The use of the alias is tricky at first to those of us who aren't xslt
> experts. Once you think about it it's obvious that if you're using an
> xslt to transform something into an xslt, then the namespace of the
> output xslt would have to be different so as not to confuse whether a
> particular xsl: element belonged to the resultant set or the
> stylesheet doing the transform.

Yes,  if we would not use the alias namespace everything would be
interpreted as transformation instructions and would be applied in the
prepare.html.xsl.

Thanks, Tim, for starting this thread it is very important to understand
what is going on to be able to re-factor views into the core. Still this
is only 1/3 of all involved pipelines and we have a long way to go. ;-)
We should use POJO were ever it make sense to better reuse the code.
Like Ross stated in another mail view pipes have some similarities with
method and functions. I always thought about this implementation as
prototype and think that we should mainly use xsl instead of java where
appropriate. That is the reason for the architecture, that should make
it easy to migrate to java.

salu2
-- 
thorsten

"Together we stand, divided we fall!" 
Hey you (Pink Floyd)