apache > cocoon
 

Source Writing Transformer

Source Writing Transformer

Diverts xml from a pipeline, writing it to a Source (or deleting it).

Thankfully, FileSource is no longer the only Source that currently implements WritableSource; there are implementations of WebDAV and Apache Slide WritableSources in the scratchpad. Hopefully further ModifiableSource implementations (XMLDB, CVS, Email, SQL, etc.) will be appear in the future.

See the transformer in action with the Cocoon Samples for webdav block, and Wiki about it at WebDAVCMS.

  • Name : write-source
  • Class: org.apache.cocoon.transformation.SourceWritingTransformer
  • Cacheable: no.

The Tags

          
    <source:write>
      [<source:path/>]
      <source:source/>
      <source:fragment/>
    </source:write>
    
    <source:insert/>
      <source:path/>
      <source:source/>
      <source:fragment/>
      [<source:replace/>]
      [<source:reinsert/>]
    </source:insert>

    <source:delete/>
      <source:source/>
      [<source:path/>] - Ignored
      [<source:fragment/>] - Ignored
      [<source:replace/>] - Ignored
      [<source:reinsert/>] - Ignored
    </source:insert>
          
        

In the namespace xmlns:source="http://apache.org/cocoon/source/1.0".

The contents of the <source:fragment/> tag are written to the specified ModifiableSource when the document containing it is transformed by SourceWritingTransformer (or deleted if you are using the delete instruction).

Definition

          
 <map:transformer name="write-source" 
    src="org.apache.cocoon.transformation.SourceWritingTransformer">
    <map:parameter name="serializer" value="xml"/>  
 </map:transformer/>
          
        

The SourceWritingTransformer is predefined for you in the main SiteMap.

Invocation

This invokes the SourceWritingTransformer on your pipeline.

          
 <map:transform type="write-source"/>
          
        

Or you can over-ride the default serializer here.

          
 <map:transform type="write-source">
   <map:parameter name="serializer" value="my-special-serializer"/>   
 </map:transform>
          
        

The Tags in detail

source:write

The source:write tag can take optional attributes, create (defaults to 'true') and serializer (defaults to the serializer set up in the definition or invocation of the transformer).

Replaces the entire content of a Source (specified by the <source:source/> tag) with the contents of the <source:fragment/> tag, if @create is 'true', a new asset will be created if one does not already exist.

The <source:source/> and <source:fragment/> tags are required, a <source:path/> tag is optional, if specified, the value is used as an XPath to generate xml in your Source, in which to wrap your content.

source:source

The System ID of the Source to be written to.

e.g. <source:source>docs/blah.xml</source:source> or <source:source>context:/blah.xml</source:source> etc.

source:fragment

The XML Fragment to be written.

For example:

              
  <source:fragment><foo>
      <bar id="dogcow"/>
    </foo></source:fragment>
              
            

or

              
  <source:fragment>
    <foo/>
    <bar>
      <dogcow/>
    <bar/>
  </source:fragment>
              
            

etc.

Note
The second example type, can only be used when the <source:path/> tag has been specified.

source:path

[Optional] XPath to specify how your content is wrapped

e.g. <source:path>doc</source:path> - your content is placed inside a <doc/> root tag.

Note
If this parameter is omitted, your content MUST have only ONE top-level node.

source:insert

The source:insert tag can take optional attributes, create (defaults to 'true') and serializer (defaults to the serializer set up in the definition or invocation of the transformer).

Inserts into a Source (specified by the <source:source/> tag) the contents of the tag <source:fragment/> at the XPath location specified in the <source:path/> tag, if @create is 'true', a new Source will be created if one does not already exist.

The <source:source/>, <source:path/> and <source:fragment/> tags are all required, the <source:replace/> and <source:reinsert/> tags are optional.

source:source

The System ID of the Source to be inserted into.

e.g. <source:source>docs/blah.xml</source:source> or <source:source>context:/blah.xml</source:source> etc.

source:fragment

The XML Fragment to be written.

e.g.

              
  <source:fragment>
    <foo>
      <bar id="dogcow"/>
    </foo>
  </source:fragment>
              
            

or

              
  <source:fragment>
    <foo/>
    <bar>
      <dogcow/>
    <bar/>
  </source:fragment>
              
            

etc.

source:path

source:replace

[Optional] XPath (from <source:path/>) to select the node that is replaced by your new content

e.g. <source:replace>foo/bar/dogcow/@status='cut'</source:replace> (is equivalent to this in XSLT: select="foo[bar/dogcow/@status='cut']"), what gets replaced is the <foo/> which has a <bar/> with a <dogcow status="cut"/> in it.

The overwrite attribute of the parent <source:insert/> is used to check if replacing is allowed. If overwrite is 'true' (the default) the node is replaced. If overwrite is 'false' the node is only inserted if the replace node is found.

source:reinsert

[Optional] The XPath (relative to <source:replace/>) to backup the contents of the overwritten node to.

e.g. <source:reinsert>foo/versions</source:reinsert> or <source:reinsert>/doc/versions/foo</source:reinsert>.

If specified and a node is replaced, all children of this replaced node will be reinserted at the given path.

Notes

  • if 'replace' is not specified, your 'fragment' is appended as a child of 'path'.
  • if 'replace' is specified and it exists and 'overwrite' is true, your 'fragment' is inserted in 'path', before 'replace' and then 'replace' is deleted.
  • if 'replace' is specified and it exists and 'overwrite' is false, no action occurs.
  • if 'replace' is specified and it does not exist and 'overwrite' is true, your 'fragment' is appended as a child of 'path'.
  • if 'replace' is specified and it does not exist and 'overwrite' is false, your 'fragment' is appended as a child of 'path'.
  • if 'reinsert' is specified and it does not exist, no action occurs.

source:delete

This instruction takes only a <source:source/> parameter (as a child tag) and deletes the corresponding source. All other source:* tags are ignored, if present: this allows for easy source-write instructions to be generated on the fly (e.g. by a stylesheet). Just change source:write to source:delete and you're set.

Examples

Simple Write

            
 <page>
   ...
   <source:write xmlns:source="http://apache.org/cocoon/source/1.0">
     <source:source>context://doc/editable/my.xml</source:source>      
     <source:fragment><page>
       <title>Hello World</title>
       <content>
         <p>This is my first paragraph.</p>
       </content>
     </page></source:fragment>
   </source:write>
   ...
 </page>
            
          

Insert at end

            
 <page>
   ...
   <source:insert xmlns:source="http://apache.org/cocoon/source/1.0">
     <source:source>context://doc/editable/my.xml</source:source>      
     <source:path>page/content</source:path>      
     <source:fragment>
       <p>This paragraph gets <emp>inserted</emp>.</p>
       <p>With this one, at the end of the content.</p>
     </source:fragment>
   </source:insert>
   ...
 </page>
            
          

Replace

            
 <page>
   ...
   <source:insert xmlns:source="http://apache.org/cocoon/source/1.0">
     <source:source>context://doc/editable/my.xml"</source:source>      
     <source:path>page/content</source:path>      
     <source:replace>p[1]</source:replace>      
     <source:fragment>
       <p>This paragraph <emp>replaces</emp> the first paragraph.</p>
     </source:fragment>
   </source:insert>
   ...
 </page>
            
          

Insert at the beginning

            
 <page>
   ...
   <source:insert>
     <source:source>context://doc/editable/my.xml</source:source>
     <source:path>page</source:path>
     <source:replace>content</source:replace>
     <source:reinsert>content</source:reinsert>
     <source:fragment>
       <content>
         <p>This new paragraph gets inserted <emp>before</emp> the other ones.</p>
       </content>
     </source:fragment>
    <source:insert>
   ...
 </page>
            
          

This sample does not currently work, see the tests in the scratchpad at http://localhost:8080/cocoon/mount/editor/tests.

Note
You must have built Cocoon with the scratchpad included for this link to work.

Delete a source

            
 <page>
   ...
   <source:delete>
     <source:source>context://doc/editable/my.xml</source:source>
   <source:delete>
   ...
 </page>
            
            

Sample of the output of these tags

This is the kind of information that the SourceWritingTransformer outputs to the pipeline, replacing the original source:write and source:insert tags

            
 <page>
   ...
   <sourceResult>
     <action>new|overwritten|none</action>
     <behaviour>write|insert<behaviour>
     <execution>success|failure</execution>
     <serializer>xml</serializer>
     <source>source:specific/path/to/context/doc/editable/my.xml</source>
     <message>a message about what happened</message>
   </sourceResult>
   ...
 </page>
            
          

Known Problems

I cannot get the 'insert before' example working, which uses the <source:reinsert/> tag.

Warning

It is not known how robust this transformer is under even moderate load, especially when it comes to more than one person modifying the same file at the same time.