You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by ha...@apache.org on 2001/11/19 17:28:33 UTC

cvs commit: xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess AbstractAttributeHelper.java AbstractKeyAttributeHelper.java AccessHelper.java AttributeHelper.java HsqlIdentityKeyAttributeHelper.java IfxSerialKeyAttributeHelper.java KeyAttributeHelper.java ManualKeyAttributeHelper.java OutputHelper.java RequestAttributeHelper.java RequestAttributeOutputHelper.java RequestParameterHelper.java

haul        01/11/19 08:28:33

  Added:       scratchpad/webapp/mount/mod-db database.xml edit-groups.xsp
                        schema.sql sitemap.xmap stupid.xsl user-list.xsp
               scratchpad/src/org/apache/cocoon/acting
                        ModularDatabaseAddAction.java
                        ModularDatabaseDeleteAction.java
                        ModularDatabaseSelectAction.java
                        ModularDatabaseUpdateAction.java
               scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess
                        AbstractAttributeHelper.java
                        AbstractKeyAttributeHelper.java AccessHelper.java
                        AttributeHelper.java
                        HsqlIdentityKeyAttributeHelper.java
                        IfxSerialKeyAttributeHelper.java
                        KeyAttributeHelper.java
                        ManualKeyAttributeHelper.java OutputHelper.java
                        RequestAttributeHelper.java
                        RequestAttributeOutputHelper.java
                        RequestParameterHelper.java
  Log:
  This is a sample implementation that puts parameter
  reading & writing into components. In addition it uses
  the same approach to model DBMS's different autoincrement
  approaches. Component handling is somewhat clumsy, but I
  hope that that will be moved up to the sitemap so that
  the new components could be shared. See accompaning mail
  on cocoon-dev.
  
  Revision  Changes    Path
  1.1                  xml-cocoon2/scratchpad/webapp/mount/mod-db/database.xml
  
  Index: database.xml
  ===================================================================
  <root>
     <connection>personnel</connection>
     <!-- this is only used when no connection is specified in sitemap -->
  
     <!-- a table consists of key attributes and plain values, keys are
          handled differently on operations. Most significantly when
          inserting a new row, we need to determine if a value needs to
          be set by the action or wheter it's an autoincrement. Next,
          autoincrements work differently on different DBMSs, so we need
          special support to find out about the value set by the DBMS.
     -->
     <table name="user"><!-- @name is the table's name in the DB -->
        <keys>
           <key name="uid" type="int">
  	    <!-- @name is the column's name -->
  	    <!-- @type is the column's jdbc type -->
  	    <mode name="auto"  type="insert"/>
  	    <!-- this entry says:
  	    when inserting a new column (@type="insert"), use the
  	    helper named "auto" to find out about how to handle this
  	    column. All other operations use the default mode. 
  	    There are two distinct mode types: "insert" for insert
  	    operations and "others" for all other (delete, update,
  	    select) -->
  	 </key>
        </keys>
        <values>
           <value name="name"    type="string"></value>
           <value name="surname" type="string"></value>
           <value name="uname"   type="string"></value>
        </values>   
     </table>
  
     <table name="user_groups">
        <keys>
           <key name="uid" type="int">
  	    <!-- Next we have two different modes: a default and
  	    "attrib". See below for explanation. -->
  	    <mode name="request" parameter="user_groups.uid" type="request"/>
              <mode name="attribute" parameter="org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:user.uid" type="attrib"/>
  	 </key>
  	 <key name="gid" type="int" set="master">
  	    <!-- now, this is tricky: when we need to insert multiple
  	    rows, those attributes that differ in these rows are
  	    marked to belong to a "set". One column is marked to be
  	    the "master" while all others need to be "slaves". A
  	    master is used to obtain a sorted set of index values for
  	    these rows. These will then be used to get the actual
  	    values for the master and slave columns. Note that it is
  	    not necessary to have the master be a key column. 
  
  	    This attribute may be specified on a column level or on a
  	    mode level to allow different behavious. Mode level @set
  	    is only considered if @set is not present on column level.
  	    -->
  	    <!-- special mode type "all" is used for all operations -->
  	    <mode name="request" parameter="user_groups.gid" type="all"/>
  	 </key>
        </keys>
     </table>
  
     <table name="groups">
        <keys>
           <key name="gid" type="int">
  	    <mode name="auto"  type="insert"/>
  	 </key>
        </keys>
        <values>
           <value name="gname"   type="string"></value>
        </values>   
     </table>
     
  
     <!-- the existing DB actions try to work on all tables listed in
          this descriptor file. Just like some other newer actions this
          one also allows for table-sets. If no set is found, it
          defaults to the previous behaviour. Note though, that the
          syntax is slightly different from the "old" DB actions file. -->
  
  
     <table-set name="user">
        <table name="user"/>
     </table-set>
  
     <table-set name="groups">
        <table name="groups"/>
     </table-set>
  
     <table-set name="user+groups">
        <table name="user"/>
        <table name="user_groups" add-mode="attrib" others-mode="request"/>
        <!-- below we have another table set that inserts data into
        user_groups. When inserting into user_groups alone, we'd know
        the uid before hand, but when inserting a new user as well, we
        need to find out the autoincrement value first. Therefore we
        need a different approach to obtain that value. So we can
        instruct the action to use for add operations ("request") a
        different type (here "attrib") which reads the result from the
        first insert operation. We could as well specify a different
        mode for "others" operations as well: others-mode
        -->
     </table-set>
  
     <table-set name="user_groups">
        <table name="user_groups" add-mode="request" others-mode="request"/>
     </table-set>
  
  </root>
  
  
  1.1                  xml-cocoon2/scratchpad/webapp/mount/mod-db/edit-groups.xsp
  
  Index: edit-groups.xsp
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  
  <xsp:page
            language="java"
            xmlns:xsp="http://apache.org/xsp"
  	  xmlns:esql="http://apache.org/cocoon/SQL/v2"
            xmlns:xsp-request="http://apache.org/xsp/request/2.0"
  >
  <!--
  -->
  <xsp:structure>
     <xsp:include>java.util.Enumeration</xsp:include>
  </xsp:structure>
  
  <page>
     <title>edit-groups</title>
  
  
    <esql:connection>
      <esql:pool>personnel</esql:pool>
      
      <xsp:logic>
        String user = <xsp-request:get-parameter name="user.uid"/>;
        if ( user == null ) {
           user = <xsp-request:get-parameter name="user_groups.uid"/>;
        }
        int uid=-1;
        if ( user != null ) {
          try {
            uid = Integer.parseInt(user);
          } catch ( NumberFormatException e ) {
            uid = -1;
          }
        }
        String uname="";
        String name="";
        String surname="";
        if ( uid != -1 ) {
          <esql:execute-query>
            <esql:query>select * from user where uid=<esql:parameter><xsp:expr>uid</xsp:expr></esql:parameter></esql:query>
            <esql:results>
              <esql:row-results>
                <xsp:logic>
                  uid=<esql:get-int column="uid"/>;
                  uname=<esql:get-string column="uname"/>;
  	 	name=<esql:get-string column="name"/>;
  	 	surname=<esql:get-string column="surname"/>;
  	      </xsp:logic>
              </esql:row-results>
            </esql:results>
          </esql:execute-query>
        }
      </xsp:logic>
  
  
  <h1>Edit User's Groups</h1>
  <table cellpadding="2" cellspacing="2" border="0">
    <tbody>
      <form>
      <tr>
        <td valign="Top" align="Right">uname
        </td>
        <td valign="Top">
           <input type="text" name="user.uname">
  	    <xsp:attribute name="value"><xsp:expr>uname</xsp:expr></xsp:attribute>
  	 </input>
        </td>
        <td>
        </td>
      </tr>
      <tr>
        <td valign="Top" align="Right">surname, name</td>
        <td>
           <input type="text" name="user.surname">
  	    <xsp:attribute name="value"><xsp:expr>surname</xsp:expr></xsp:attribute>
  	 </input>,
           <input type="text" name="user.name">
  	    <xsp:attribute name="value"><xsp:expr>name</xsp:expr></xsp:attribute>
  	 </input>
        </td>
        <td>
           <input type="hidden" name="user.uid">
  	    <xsp:attribute name="value"><xsp:expr>uid</xsp:expr></xsp:attribute>
  	 </input>
           <input type="submit" name="upd-user" value="update"/>
        </td>
      </tr>
      </form>
      <tr>
        <td valign="Top">
        </td>
        <td valign="Top" align="right">
        <form>
          <table border="1">
            <tbody>
              <tr>
                <td>
                <table border="0">
                  <tbody>
                    <tr>
                      <td>current groups</td>
                    </tr>
                    <tr>
                      <td>
  	            <select multiple="1" name="user_groups.gid" size="7">
  		 	<esql:execute-query>
  		 	   <esql:query>select gid, gname from user_groups, groups where user_groups.gid = groups.gid and uid=<esql:parameter type="int"><xsp:expr>uid</xsp:expr></esql:parameter> order by gname</esql:query>
  		 	   <esql:results>
  		 		<esql:row-results>
  		 		   <option>			        
  		 		      <xsp:attribute name="value"><esql:get-string column="gid"/></xsp:attribute>
  		 		      <esql:get-string column="gname"/>
  		 		   </option>
  		 		</esql:row-results>
  		 	   </esql:results>
  		 	</esql:execute-query>
  		    </select>
                      </td>
                    </tr>
                    <tr>
                      <td valign="Top">
                      <input type="hidden" name="user_groups.uid">
                         <xsp:attribute name="value"><xsp:expr>uid</xsp:expr></xsp:attribute>
                      </input>
                      <input type="submit" name="remove-groups" value="remove"/></td>
                    </tr>
                  </tbody>
                </table>
                </td>
              </tr>
            </tbody>
          </table>
        </form>
        </td>
        <td valign="Top" align="left">
        <form>
          <table border="1">
            <tbody>
              <tr>
                <td>
                <table border="0">
                  <tbody>
                    <tr>
                      <td>available groups</td>
                    </tr>
                    <tr>
                      <td>
                      <select name="user_groups.gid" multiple="1" size="7">
  		 	<esql:execute-query>
  		 	   <esql:query>select gid, gname from groups where gid not in (select gid from user_groups where uid=<esql:parameter type="int"><xsp:expr>uid</xsp:expr></esql:parameter>) order by gname</esql:query>
  		 	   <esql:results>
  		 		<esql:row-results>
  		 		   <option>			        
  		 		      <xsp:attribute name="value"><esql:get-string column="gid"/></xsp:attribute>
  		 		      <esql:get-string column="gname"/>
  		 		   </option>
  		 		</esql:row-results>
  		 	   </esql:results>
  		 	</esql:execute-query>
                      </select>
                      </td>
                    </tr>
                    <tr>
                      <td valign="Top">
                      <input type="hidden" name="user_groups.uid">
                         <xsp:attribute name="value"><xsp:expr>uid</xsp:expr></xsp:attribute>
                      </input>
                      <input type="submit" name="add-groups" value="add"/></td>
                    </tr>
                  </tbody>
                </table>
                </td>
              </tr>
            </tbody>
          </table>
        </form>
        </td>
      </tr>
    </tbody>
  </table>
    <div align="Right"><form action="user-list"><input value="back to list" type="submit"/></form></div>
  
    </esql:connection>
  
    <hr/>
  
    <p><h3>Request Attributes</h3></p>
    <p>
    <table border="0">
    <tbody>
    <xsp:logic>{
         Enumeration e=request.getAttributeNames();
         while ( e.hasMoreElements() ) {
             String attribute = (String) e.nextElement();
             Object value = request.getAttribute(attribute);
             <tr>
             <td align="right"><xsp:expr>attribute</xsp:expr></td>
             <td>="<xsp:expr>value</xsp:expr>"</td>
             </tr>
         }
    }</xsp:logic>
    </tbody>
    </table><br/>
    </p>
    <hr/>
  
    <p><h3>Request Parameters</h3></p>
    <p>
    <table border="0">
    <tbody>
    <xsp:logic>{
         Enumeration e=request.getParameterNames();
         while ( e.hasMoreElements() ) {
             String attribute = (String) e.nextElement();
             Object[] value = request.getParameterValues(attribute);
  	   for (int i=0; i &lt; value.length; i++) {
                <tr>
                <td align="right"><xsp:expr>attribute</xsp:expr>[<xsp:expr>i</xsp:expr>]</td>
                <td>="<xsp:expr>value[i]</xsp:expr>"</td>
                </tr>
  	   }
         }
    }</xsp:logic>
    </tbody>
    </table><br/>
    </p>
    <hr/>
  
  
  
  
    </page>
  </xsp:page>
  
  
  
  1.1                  xml-cocoon2/scratchpad/webapp/mount/mod-db/schema.sql
  
  Index: schema.sql
  ===================================================================
  
  -- this is the hsqldb schema file
  -- to adapt it to another RDBMS, replace column type identity
  -- with appropriate autoincrement type, e.g. SERIAL for informix
  -- you might want to add "on delete cascade" to foreign keys in
  -- tab�e user_groups
  
  create table user (
  	uid integer identity primary key,
  	name varchar(50),
  	surname varchar(50),
  	uname varchar(20),
  	unique (uname)
  );
  create table groups (
  	gid integer identity primary key,
  	gname varchar(20),
  	unique (gname)
  );
  
  create table user_groups (
  	uid integer,
  	gid integer,
  	primary key (uid,gid),
  	foreign key (uid) references user(uid),
  	foreign key (gid) references groups(gid)
  );
  
  
  1.1                  xml-cocoon2/scratchpad/webapp/mount/mod-db/sitemap.xmap
  
  Index: sitemap.xmap
  ===================================================================
  <?xml version="1.0"?>
  
  <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
  
    <!-- ========================= Components ============================== -->
  
    <map:components>
  
      <map:generators default="file"/>
  
      <map:transformers default="xslt"/>
  
      <map:readers default="resource"/>
  
      <map:serializers default="html"/>
  
      <map:selectors default="browser">
        <map:selector name="request" src="org.apache.cocoon.selection.RequestSelectorFactory"/>
      </map:selectors>
  
      <map:matchers default="wildcard">
        <map:matcher name="wildcard" src="org.apache.cocoon.matching.WildcardURIMatcherFactory"/>
      </map:matchers>
  
      <map:actions>
         <map:action name="mod-db-add" src="org.apache.cocoon.acting.ModularDatabaseAddAction">
     	 <descriptor>context://mount/mod-db/database.xml</descriptor>
     	 <!-- <mode name="auto" src="org.apache.cocoon.acting.ModularDatabaseAccess.IfxSerialKeyAttributeHelper"/> -->
     	 <mode name="auto" src="org.apache.cocoon.acting.ModularDatabaseAccess.HsqlIdentityKeyAttributeHelper"/>
     	 <mode name="request" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestParameterHelper"/>
     	 <mode name="attribute" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestAttributeHelper"/>
     	 <output name="request" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestAttributeOutputHelper"/>
         </map:action>
         <map:action name="mod-db-del" src="org.apache.cocoon.acting.ModularDatabaseDeleteAction">
     	 <descriptor>context://mount/mod-db/database.xml</descriptor>
     	 <!-- <mode name="auto" src="org.apache.cocoon.acting.ModularDatabaseAccess.IfxSerialKeyAttributeHelper"/> -->
     	 <mode name="auto" src="org.apache.cocoon.acting.ModularDatabaseAccess.HsqlIdentityKeyAttributeHelper"/>
     	 <mode name="request" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestParameterHelper"/>
     	 <mode name="attribute" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestAttributeHelper"/>
     	 <output name="request" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestAttributeOutputHelper"/>
         </map:action>
         <map:action name="mod-db-upd" src="org.apache.cocoon.acting.ModularDatabaseUpdateAction">
     	 <descriptor>context://mount/mod-db/database.xml</descriptor>
     	 <!-- <mode name="auto" src="org.apache.cocoon.acting.ModularDatabaseAccess.IfxSerialKeyAttributeHelper"/> -->
     	 <mode name="auto" src="org.apache.cocoon.acting.ModularDatabaseAccess.HsqlIdentityKeyAttributeHelper"/>
     	 <mode name="request" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestParameterHelper"/>
     	 <mode name="attribute" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestAttributeHelper"/>
     	 <output name="request" src="org.apache.cocoon.acting.ModularDatabaseAccess.RequestAttributeOutputHelper"/>
         </map:action>
         <map:action name="req-params" src="org.apache.cocoon.acting.RequestParameterExistsAction"/>
      </map:actions>
  
    </map:components>
  
  
    <!-- ======================= Views ================================= -->
  
    <map:views>
      <map:view name="content" from-label="content">
        <map:serialize type="xml" mime-type="text/plain"/>
      </map:view>
    </map:views>
  
    <!-- ======================= Pipelines ================================= -->
  
    <map:pipelines>
      <map:pipeline>
  
     <!-- ========================== Modular DB ================================= -->
  
         <map:match pattern="*">
  
            <!-- ______________________________ inserts ______________________________ -->
  
            <!-- add new groups to a user's groups -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="add-groups user_groups.uid user_groups.gid"/>
    	     <map:act type="mod-db-add">
    	  	<map:parameter name="table-set" value="user_groups"/>
    	     </map:act>
    	  </map:act>
  
            <!-- add one new user -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="add-user user.name user.surname user.uname"/>
    	     <map:act type="mod-db-add">
    	  	<map:parameter name="table-set" value="user"/>
    	     </map:act>
    	  </map:act>
       
            <!-- add onw new group -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="add-group groups.gname"/>
    	     <map:act type="mod-db-add">
    	  	<map:parameter name="table-set" value="groups"/>
    	     </map:act>
    	  </map:act>
       
            <!-- add one new user plus groups -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="add-user-groups user.name user.surname user.uname"/>
    	     <map:act type="mod-db-add">
    	  	<map:parameter name="table-set" value="user+groups"/>
    	     </map:act>
    	  </map:act>
       
            <!-- ______________________________ updates ______________________________ -->
  
            <!-- update one user -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="upd-user user.uid user.name user.surname user.uname"/>
    	     <map:act type="mod-db-upd">
    	  	<map:parameter name="table-set" value="user"/>
    	     </map:act>
    	  </map:act>
       
  
            <!-- update one group -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="upd-group groups.gid groups.gname"/>
    	     <map:act type="mod-db-upd">
    	  	<map:parameter name="table-set" value="groups"/>
    	     </map:act>
    	  </map:act>
       
            <!-- ______________________________ deletes ______________________________ -->
  
            <!-- delete one user -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="del-user user.uid"/>
    	     <map:act type="mod-db-del">
    	  	<map:parameter name="table-set" value="user"/>
    	     </map:act>
    	  </map:act>
  
            <!-- delete one group -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="del-group groups.gid"/>
    	     <map:act type="mod-db-del">
    	  	<map:parameter name="table-set" value="groups"/>
    	     </map:act>
    	  </map:act>
       
            <!-- delete groups from a user's groups -->
    	  <map:act type="req-params">
    	     <map:parameter name="parameters" value="remove-groups user_groups.uid user_groups.gid"/>
    	     <map:act type="mod-db-del">
    	  	<map:parameter name="table-set" value="user_groups"/>
    	     </map:act>
    	  </map:act>
  
            <!-- ______________________________ content  ______________________________ -->
  
    	  <map:generate type="serverpages" src="{1}.xsp"/>
  	  <map:label name="content"/>
  
  	  <map:transform src="context://stylesheets/dynamic-page2html.xsl">
  	     <map:parameter name="view-source" value="{1}.xsp"/>
  	  </map:transform>
    	  <map:transform src="stupid.xsl"/>
    	  <map:serialize/>
  
         </map:match>
  
      </map:pipeline>
    </map:pipelines>
  
  </map:sitemap>
  
  
  
  1.1                  xml-cocoon2/scratchpad/webapp/mount/mod-db/stupid.xsl
  
  Index: stupid.xsl
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  
  <xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  >
    <xsl:variable name="tablecolor">#d0f0d0</xsl:variable>
    <xsl:variable name="mediumtablecolor">#e0ffe0</xsl:variable>
    <xsl:variable name="lighttablecolor">#f0fff0</xsl:variable>
   
    <xsl:template match="sqltbl">
      <xsl:element name="table"> 
        <xsl:attribute name="border">1</xsl:attribute>
        <xsl:attribute name="align">center</xsl:attribute>
        <xsl:attribute name="bgcolor"><xsl:value-of select="$tablecolor"/></xsl:attribute>
        <xsl:element name="tr"> 
          <xsl:apply-templates select="child::*[1]" mode="head"/>
        </xsl:element> 
        <xsl:for-each select="child::*">
          <xsl:element name="tr"> 
            <xsl:apply-templates select="."/>
          </xsl:element> 
        </xsl:for-each>
      </xsl:element> 
    </xsl:template>
  
    <xsl:template match="sqltblrow" mode="head">
      <xsl:for-each select="./child::*">
        <xsl:element name="th">
          <xsl:value-of select="name()"/>
        </xsl:element>
      </xsl:for-each>
    </xsl:template>
  
    <xsl:template match="sqltblrow">
      <xsl:for-each select="./child::*">
        <xsl:element name="td">
          <xsl:attribute name="valign">top</xsl:attribute>
          <xsl:apply-templates/>
        </xsl:element>
      </xsl:for-each>
    </xsl:template>
  
    <xsl:template match="sql-set">
       <xsl:element name="table">
          <xsl:attribute name="border">1</xsl:attribute>
          <xsl:attribute name="bgcolor"><xsl:value-of select="$mediumtablecolor"/></xsl:attribute>
  	<xsl:for-each select="./child::*">
  	   <xsl:element name="tr">
  	      <xsl:element name="td">
  	         <xsl:apply-templates/>
  	      </xsl:element>
  	   </xsl:element>
  	</xsl:for-each>
       </xsl:element>
    </xsl:template>
  
    <xsl:template name="sql-set-item">
       <xsl:value-of select="."/>
       <xsl:apply-templates/>
    </xsl:template>
  
  
    <xsl:template match="sql-list">
       <xsl:element name="table">
          <xsl:attribute name="border">1</xsl:attribute>
          <xsl:attribute name="bgcolor"><xsl:value-of select="$mediumtablecolor"/></xsl:attribute>
  	<xsl:variable name="i"><xsl:value-of select="position()"/></xsl:variable>
  	<xsl:for-each select="./child::*">
  	   <xsl:element name="tr">
  	      <xsl:element name="td">
  	         <xsl:attribute name="bgcolor"><xsl:value-of select="$lighttablecolor"/></xsl:attribute>
  	         <xsl:value-of select="@pos"/>.
  	      </xsl:element>
  	      <xsl:element name="td">
  	         <xsl:apply-templates/>
  	      </xsl:element>
  	   </xsl:element>
  	</xsl:for-each>
       </xsl:element>
    </xsl:template>
  
    <xsl:template name="sql-list-item">
       <xsl:value-of select="."/>
       <xsl:apply-templates/>
    </xsl:template>
  
  
    <xsl:template match="sql-row">
       <xsl:element name="table">
          <xsl:attribute name="border">1</xsl:attribute>
          <xsl:attribute name="bgcolor"><xsl:value-of select="$mediumtablecolor"/></xsl:attribute>
  	<xsl:element name="tr">
  	   <xsl:for-each select="./child::*">
  	      <xsl:element name="td">
  	         <xsl:apply-templates/>
  	      </xsl:element>
  	   </xsl:for-each>
  	</xsl:element>
       </xsl:element>
    </xsl:template>
  
    <xsl:template name="sql-row-item">
       <xsl:value-of select="."/>
       <xsl:apply-templates/>
    </xsl:template>
  
  
    <xsl:template match="@*|node()" priority="-1">
     <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
     </xsl:copy>
    </xsl:template>
  
  
    <xsl:template match="@*|node()" priority="-1" mode="head">
     <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="head"/>
     </xsl:copy>
    </xsl:template>
  
    <xsl:template match="error">
       <xsl:element name="table">
       <xsl:attribute name="align">center</xsl:attribute>
       <xsl:attribute name="width">100%</xsl:attribute>
       <xsl:attribute name="bgcolor">#ffe0e0</xsl:attribute>
          <xsl:element name="tr">
  	   <xsl:element name="td">
  	   <xsl:attribute name="align">center</xsl:attribute>
  	      <xsl:element name="big">	   
                   <xsl:apply-templates/>
  	      </xsl:element>
  	   </xsl:element>	
          </xsl:element>
       </xsl:element>
    </xsl:template>
  
  </xsl:stylesheet>
  
  
  
  1.1                  xml-cocoon2/scratchpad/webapp/mount/mod-db/user-list.xsp
  
  Index: user-list.xsp
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  
  <xsp:page
            language="java"
            xmlns:xsp="http://apache.org/xsp"
  	  xmlns:esql="http://apache.org/cocoon/SQL/v2"
            xmlns:xsp-request="http://apache.org/xsp/request/2.0"
  >
  <!--
  -->
  <xsp:structure>
     <xsp:include>java.util.Enumeration</xsp:include>
  </xsp:structure>
  
    <page>
   
     <title>user-list</title>
  
     <content>
  
  
    <h1>Modular Database Actions</h1> 
  
    <hr/> 
  
    <p>The intention is to factor out those parts that are dependent on
    the utilised DBMS as well as the methods used to obtain the values
    and communicate results. Therefore three classes of adaptors
    exist:</p>
  
    <ol>
      <li>Attribute Helpers read data from some source (e.g. the request
  	object or session attributes or whatever)</li> 
  
      <li>Key Attribute Helpers determine the value of a key attribute
          column in a database. This could be by querying the database,
          reading from an arbitrary source (e.g. request object) or just
          skipping the column when inserting a row and querying the
          database afterwards. This needs to be done e.g. for Informix's
          SERIAL or HSQLDB's IDENTITY column types.</li>
  
     <li>Output Helpers send the data to an arbitrary
         destination. Again, this could be request attributes or
         anything else. When the database transaction finishes, it is
         signalled to them whether the transaction succeeded or
         failed.</li>
    </ol>
    <hr/>
    <!-- 
                                     forms 
    -->
  
    <!--
  
    <h2>Insert a user</h2>
    <blockquote>
      <form method="Get">
        <table cellpadding="2" cellspacing="2" border="0">
  	<tbody>
  	  <tr>
  	    <td valign="Top" align="Right">surname</td>
  	    <td valign="Top">
  	      <input type="text" name="user.surname" size="20" maxsize="20">
  	        <xsp:attribute name="value"><xsp-request:get-parameter default="" name="user.surname"/></xsp:attribute>
  	      </input>
  	    </td>
  	  </tr>
  	  <tr>
  	    <td valign="Top" align="Right">name
  	    </td>
  	    <td valign="Top"><input type="text" name="user.name" size="20" maxsize="20"><xsp:attribute name="value"><xsp-request:get-parameter default="" name="user.name"/></xsp:attribute></input></td>
  	  </tr>
  	  <tr>
  	    <td valign="Top" align="Right">uname
  	    </td>
  	    <td valign="Top"><input type="text" name="user.uname" size="20" maxsize="20"><xsp:attribute name="value"><xsp-request:get-parameter default="" name="user.uname"/></xsp:attribute></input>
  	    </td>
  	  </tr>
  	  <tr>
  	    <td valign="Top">
  	    </td>
  	    <td align="Right"><input type="submit" name="add-user" value="OK"/></td>
  	  </tr>
  	</tbody>
        </table>
      </form>
      </blockquote>
      <hr/>
  
  
      <h2>Add groups to a user account</h2>
      <blockquote>
        <form method="Get">
  	<table cellpadding="2" cellspacing="2" border="0">
  	  <tbody>
  	    <tr>
  	      <td valign="Top" align="Right">user</td>
  	      <td valign="Top">
  	         <select name="user_groups.uid" size="1">
  		    <esql:connection>
  		       <esql:pool>personnel</esql:pool>
  		       <esql:execute-query>
  		          <esql:query>select uid, uname from user order by name, surname</esql:query>
  		          <esql:results>
  			     <esql:row-results>
  			        <option>			        
  				   <xsp:attribute name="value"><esql:get-string column="uid"/></xsp:attribute>
  				   <esql:get-string column="uname"/>
  				</option>
  			     </esql:row-results>
  			  </esql:results>
  		       </esql:execute-query>
  		    </esql:connection>
  		 </select>
  	      </td>
  	      <td>
  	      </td>
  	    </tr>
  	    <tr>
  	      <td valign="Top" align="Right">groups</td>
  	      <td valign="Top">
  	         <select multiple="1" name="user_groups.gid" size="4">
  		    <esql:connection>
  		       <esql:pool>personnel</esql:pool>
  		       <esql:execute-query>
  		          <esql:query>select gid, gname from groups order by gname</esql:query>
  		          <esql:results>
  			     <esql:row-results>
  			        <option>			        
  				   <xsp:attribute name="value"><esql:get-string column="gid"/></xsp:attribute>
  				   <esql:get-string column="gname"/>
  				</option>
  			     </esql:row-results>
  			  </esql:results>
  		       </esql:execute-query>
  		    </esql:connection>
  		 </select>
  	      </td>
  	    </tr>
  	    <tr>
  	      <td valign="Top">
  	      </td>
  	      <td>
  	      </td>
  	      <td align="Right"><input type="submit" name="add-groups" value="OK"/></td>
  	    </tr>
  	  </tbody>
  	</table>
        </form>
        </blockquote>
        -->
          <!--
                                              current db content
          -->
  
  	<h2>Current database content</h2>
  
       	<esql:connection>   	    
       	   <esql:pool>personnel</esql:pool>
         	  
  	   <p>
  	   <sqltbl>
       	      <esql:execute-query>
       	   	 <esql:query>select * from user order by name, surname, uname, uid</esql:query>
       	   	 <esql:results>
       	 	    <esql:row-results>
  	 	       
       	 		 <sqltblrow>
  	 		    <name>
  			       <esql:get-string column="name"/>
  	 		    </name>
  	 		    <surname>
  			       <esql:get-string column="surname"/>
  	 		    </surname>
  	 		    <uname>
  	 		       <esql:get-string column="uname"/>
  	 		    </uname>
  			    <action>
  			       <form action="edit-groups">
  	 		          <input type="hidden" name="user.uid">
  	 			     <xsp:attribute name="value"><esql:get-string column="uid"/></xsp:attribute>
  	 		          </input>
  	 		       <input type="submit" name="edit-user" value="edit"/>
  			       </form>
  			       <form>
  	 		       	  <input type="hidden" name="user.uid">
  	 		 	     <xsp:attribute name="value"><esql:get-string column="uid"/></xsp:attribute>
  	 		       	  </input>
  	 		       	  <input type="submit" name="del-user" value="delete"/>
  			       </form>
  			    </action>
       	 		 </sqltblrow>
  	 	       
       	 	    </esql:row-results>
       	   	 </esql:results>
       	   	 <esql:error-results>
       	 	    <error><esql:get-message/></error>
       	   	 </esql:error-results>
       	      </esql:execute-query>
  	      <form method="Get">
  	         <sqltblrow>
  	            <name>
  	               <input type="text" name="user.name" size="20" maxsize="20">
  	      	          <xsp:attribute name="value"><!-- <xsp-request:get-parameter default="" name="user.name"/> --></xsp:attribute>
  	      	       </input>
  	            </name>
  	            <surname>
  	               <input type="text" name="user.surname" size="20" maxsize="20">
  	      	          <xsp:attribute name="value"><!-- <xsp-request:get-parameter default="" name="user.surname"/> --></xsp:attribute>
  	      	       </input>
  	            </surname>
  	            <uname>
  	      	       <input type="text" name="user.uname" size="20" maxsize="20">
  	      	          <xsp:attribute name="value"><!-- <xsp-request:get-parameter default="" name="user.uname"/> --></xsp:attribute>
  	      	       </input>
  		    </uname>
  		    <action>
  		       <select multiple="1" name="user_groups.gid" size="4">
  		          <esql:execute-query>
  	                     <esql:query>select gid, gname from groups order by gname</esql:query>
  			     <esql:results>
  			     	<esql:row-results>
  			     	   <option>			        
  			 	      <xsp:attribute name="value"><esql:get-string column="gid"/></xsp:attribute>
  			 	      <esql:get-string column="gname"/>
  			 	   </option>
  			     	</esql:row-results>
  			     </esql:results>
  	                  </esql:execute-query>
  	               </select>
  	               <input type="submit" name="add-user-groups" value="new user"/>
                      </action>
  	         </sqltblrow>   
  	      </form>
             </sqltbl>
  	</p>
  
  	   <p>
  	   <sqltbl>
       	      <esql:execute-query>
       	         <esql:query>select * from groups order by gname</esql:query>
  		 <esql:results>
  		    <esql:row-results>
  		       <form>
  		 	     <sqltblrow>
  		 	        <gname>
  		 		   <input type="text" name="groups.gname">
  		 		      <xsp:attribute name="value"><esql:get-string column="gname"/></xsp:attribute>
  		 	           </input>
  		 		   <input type="hidden" name="groups.gid">
  		 		      <xsp:attribute name="value"><esql:get-string column="gid"/></xsp:attribute>
  		 	           </input>
  		 		   <input type="submit" name="upd-group" value="update"/>
  		 		   <input type="submit" name="del-group" value="delete"/>
  		 	       </gname>
       		         </sqltblrow>
  		       </form>
       		    </esql:row-results>
       	         </esql:results>
       	         <esql:error-results>
       		    <error><esql:get-message/></error>
       	         </esql:error-results>
       	      </esql:execute-query>
  	      <sqltblrow>
  	         <gname>
  		    <form>
  		       <input type="text" name="groups.gname" size="20" maxsize="20">
  		    	  <xsp:attribute name="value"><!-- <xsp-request:get-parameter default="" name="groups.gname"/> --></xsp:attribute>
  		       </input>
  		       <input type="submit" name="add-group" value="new group"/>
  		    </form>
  		 </gname>
  	      </sqltblrow>
             </sqltbl>
  	   </p>
  
  	   <p>
  	   <form>
       	   <esql:execute-query>
       	      <esql:query>select user.uid, user.uname, groups.gid, groups.gname from user, user_groups, groups where user.uid=user_groups.uid and user_groups.gid=groups.gid order by user.uname, groups.gname</esql:query>
       	      <esql:results>
                <sqltbl>
       		 <esql:row-results>
  		    <form action="edit-groups">
       		       <sqltblrow>
                            <uname>
  			     <input type="hidden" name="user.uid">
  			        <xsp:attribute name="value"><esql:get-string column="uid"/></xsp:attribute>
  			     </input>
  			     <esql:get-string column="uname"/>
  			  </uname>
                            <gname><esql:get-string column="gname"/></gname>
  			  <action>
  			     <input value="edit groups" type="submit"/>
  			  </action>
       		       </sqltblrow>
  		    </form>
       		 </esql:row-results>
                </sqltbl>
       	      </esql:results>
       	      <esql:error-results>
       		 <error><esql:get-message/></error>
       	      </esql:error-results>
       	   </esql:execute-query>
  	   </form>
  	   </p>
       	</esql:connection>
     
    <hr/>
  
    <p><h3>Request Attributes</h3></p>
    <p>
    <table border="0">
    <tbody>
    <xsp:logic>{
         Enumeration e=request.getAttributeNames();
         while ( e.hasMoreElements() ) {
             String attribute = (String) e.nextElement();
             Object value = request.getAttribute(attribute);
             <tr>
             <td align="right"><xsp:expr>attribute</xsp:expr></td>
             <td>="<xsp:expr>value</xsp:expr>"</td>
             </tr>
         }
    }</xsp:logic>
    </tbody>
    </table><br/>
    </p>
    <hr/>
  
    <p><h3>Request Parameters</h3></p>
    <p>
    <table border="0">
    <tbody>
    <xsp:logic>{
         Enumeration e=request.getParameterNames();
         while ( e.hasMoreElements() ) {
             String attribute = (String) e.nextElement();
             Object[] value = request.getParameterValues(attribute);
  	   for (int i=0; i &lt; value.length; i++) {
                <tr>
                <td align="right"><xsp:expr>attribute</xsp:expr>[<xsp:expr>i</xsp:expr>]</td>
                <td>="<xsp:expr>value[i]</xsp:expr>"</td>
                </tr>
  	   }
         }
    }</xsp:logic>
    </tbody>
    </table><br/>
    </p>
  
     <hr/>
  
  
     </content>
    </page>
  </xsp:page>
  
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAddAction.java
  
  Index: ModularDatabaseAddAction.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.Class;
  import java.net.URL;
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.Statement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Timestamp;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.List;
  import java.util.LinkedList;
  import java.util.ListIterator;
  import java.util.Enumeration;
  import java.util.Collections;
  import java.util.Set;
  import java.util.SortedSet;
  import java.util.TreeSet;
  import java.io.IOException;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.configuration.DefaultConfiguration;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.ContextException;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
  import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
  import org.apache.avalon.excalibur.component.RoleManager;
  import org.apache.avalon.excalibur.component.DefaultRoleManager;
  import org.apache.avalon.excalibur.datasource.DataSourceComponent;
  
  import org.apache.cocoon.Constants;
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.components.classloader.RepositoryClassLoader;
  import org.apache.cocoon.components.url.URLFactory;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.generation.ImageDirectoryGenerator;
  import org.apache.cocoon.util.ClassUtils;
  import org.apache.cocoon.util.HashMap;
  import org.apache.cocoon.selection.Selector;
  
  import org.apache.cocoon.acting.ModularDatabaseAccess.AccessHelper;
  import org.apache.cocoon.acting.ModularDatabaseAccess.KeyAttributeHelper;
  import org.apache.cocoon.acting.ModularDatabaseAccess.AttributeHelper;
  import org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper;
  
  /**
   * Adds record in a database. The action can update one or more tables,
   * and can add more than one row to a table at a time. The form descriptor
   * semantics for this are still in a bit of a state of flux. 
   *
   * The difference to the other Database*Actions is, that it uses
   * additional components for reading and writing parameters. In
   * addition the descriptor format has changed to accomodate the new
   * features.
   *
   * This action is heavily based upon the DatabaseAddAction and relies
   * on the AbstractDatabaseAction.
   *
   * Note: Component management will move to sitemap so that other
   * components can utilize the helper components.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/19 16:28:33 $
   */
  public class ModularDatabaseAddAction 
      extends AbstractDatabaseAction 
      implements Disposable, ThreadSafe, Contextualizable
  {
  
      // ========================================================================
      // inner helper classes
      // ========================================================================
  
      protected class Column {
  	boolean isKey = false;
  	boolean isSet = false;
  	String mode = null;
  	Configuration modeConf = null;
  	Configuration columnConf = null;
      }
  
      protected class CacheHelper {
  	public String queryString = null;
  	public int setMaster = -1;
  	public boolean isSet = false;
  	public int noOfKeys = 0;
  	public Column[] columns = null;
  
  	public CacheHelper( int cols ) {
  	    this(0,cols);
  	}
  	
  	public CacheHelper( int keys, int cols ) {
  	    noOfKeys = keys;
  	    columns = new Column[cols];
              for ( int i=0; i<cols; i++ ) {
                  columns[i] = new Column();
              }
  	}
      }
  
  
      protected class LookUpKey {
  	public Configuration tableConf = null;
  	public Map modeTypes = null;
  	
  	public LookUpKey( Configuration tableConf, Map modeTypes ) {
  	    this.tableConf = tableConf;
  	    this.modeTypes = modeTypes;
  	}
      }
  
  
  //     /**
  //      * This inner class stores extracted configuration data for a
  //      * table column.
  //      */
  //     protected class SetHelper {
  // 	public int master = -1;
  // 	public String mode = null;
  // 	public Configuration conf = null;
  // 	public int numbKeys = 0;
  // 	public boolean[] isKey;
  // 	public boolean[] isSet;
  // 	public String[] modes = null;
  // 	public Configuration[] modeConf = null;
  	
  // 	SetHelper( int cols ) {
  // 	    this(0,cols);
  // 	}
  	
  // 	SetHelper( int keys, int cols ) {
  // 	    numbKeys = keys;
  // 	    isKey = new boolean[cols];
  // 	    isSet = new boolean[cols];
  // 	    modes = new String[cols];
  // 	    modeConf = new Configuration[cols];
  // 	}
  //     }
  
      // ========================================================================
      // constants
      // ========================================================================
  
      private static final String LOCATION = "org.apache.cocoon.acting.ModularDatabaseAddAction";
      private static final int BYTE_ARRAY_SIZE = 1024;
  
      static final Integer MODE_INSERT = new Integer( 0 );
      static final Integer MODE_OTHERS = new Integer( 1 );
      static final Integer MODE_OUTPUT = new Integer( 2 );
  
      // ========================================================================
      // instance vars
      // ========================================================================
  
      // please ignore component management as I know this is ugly and I
      // hope that this duty is taken by the sitemap in short term.
      // please ignore ====>
      protected RoleManager roleManager;
      private Configuration defaultConfig;
  
      protected Context context;
  
      /** The component manager instance */
      protected ExcaliburComponentManager manager2;
  
      /** The URLFactory instance */
      protected URLFactory urlFactory;
  
      protected ExcaliburComponentSelector  modeMapping = null;
      protected ExcaliburComponentSelector  outputMapping = null;
  
      // default modes for other / insert operations
      protected HashMap defaultModeNames = new HashMap( 3 );
  
      // <==== please ignore
      //
  
      protected final Map cachedQueryData = new HashMap();
  
  
      //
      // please ignore ====>
  
      // ========================================================================
      // setup / class loading
      // (basically copied from AbstractSitemap)
      // This is ugly. Please ignore. Helpers should be handled at sitemap level
      // so that other components can share them.
      // ========================================================================
  
      /**
       * Set the role manager
       */
      public void setRoleManager( 
  			       RoleManager roles, 
  			       Configuration config
  			       ) 
      {
          this.roleManager = roles;
          this.defaultConfig = config;
      }
  
      /**
       * Set the current <code>ComponentManager</code> instance used by this
       * <code>Composable</code>.
       */
      public void compose( 
  			ComponentManager manager
  			) 
  	throws ComponentException 
      {
  	super.compose( manager );
  	getLogger().debug("compose");
          this.manager2 = new ExcaliburComponentManager(manager);
          this.manager2.setLogger(getLogger());
          this.manager2.contextualize(this.context);
          this.manager2.setRoleManager(this.roleManager);
  
  	try {
  	    if (this.defaultConfig != null) {
  		this.manager2.configure(this.defaultConfig);
  	    }
  	    this.urlFactory = (URLFactory)manager.lookup(URLFactory.ROLE);
  	    this.modeMapping = new ExcaliburComponentSelector();
  	    this.outputMapping = new ExcaliburComponentSelector();
  	    this.setupSelector(this.modeMapping);
  	    this.setupSelector(this.outputMapping);
  	    this.manager2.addComponentInstance(Selector.ROLE + "Selector", this.modeMapping);
  	    this.manager2.addComponentInstance(Selector.ROLE + "Selector", this.outputMapping);
          } catch (Exception e) {
              getLogger().error("cannot obtain the Component", e);
              throw new ComponentException("cannot obtain the URLFactory", e);
          }
      }
  
      private void setupSelector(
  			       ExcaliburComponentSelector selector
  			       ) 
  	throws Exception 
      {
          selector.setLogger(getLogger());
          selector.contextualize(this.context);
          selector.setRoleManager(this.roleManager);
          selector.compose(this.manager2);
      }
  
      public void contextualize(
  			      Context context
  			      ) 
  	throws ContextException 
      {
          this.context = context;
      }
  
      /**
       * Return the component manager for this sitemap
       */
      public ComponentManager getComponentManager() 
      {
          return this.manager2;
      }
  
      /**
       * Release all components.
       */
      public void dispose() 
      {
          if (this.urlFactory != null) {
              manager.release((Component)this.urlFactory);
          }
          if (this.modeMapping != null) {
              manager2.release((Component)this.modeMapping);
  	}
          if (this.outputMapping != null) {
              manager2.release((Component)this.outputMapping);
          }
  	manager2.dispose();
      }
  
  
      /**
       * Configure modes that are used to delegate database specific methods
       * and other modes.
       */
      public void configure(
  			  Configuration conf
  			  ) 
  	throws ConfigurationException 
      {
  	try {
  	    getLogger().debug("MDAA: configure");
  	    LinkedList hints = new LinkedList();
  	    if (conf != null) {
  		String val = null;
  		String nodeName = null;
  		Configuration[] parameters = conf.getChildren();
  		this.settings = new HashMap(parameters.length);
  		for ( int i = 0; i < parameters.length; i++) {
  		    nodeName= parameters[i].getName();
  		    getLogger().debug("configure -- " + nodeName);
  		    if ( nodeName != null ) {
  			if ( "mode".equals(nodeName.trim()) || "output".equals(nodeName.trim())) {
  			    String modeName = parameters[i].getAttribute("name");
  			    String src = parameters[i].getAttribute("src");
  			    if (modeName!=null && src!=null) {
  				Configuration modeConfig = parameters[i];
  				if (modeConfig == null) {
  				    modeConfig = this.defaultConfig;
  				}
  				getLogger().debug("load -- " + modeName + " -> " + src);
  				if ( "mode".equals(nodeName.trim())) {
  				    this.load_component(modeMapping, modeName, src, modeConfig);
  				    hints.add(modeName);
  				} else {
  				    this.load_component(outputMapping, modeName, src, modeConfig);
  				    if ( !defaultModeNames.containsKey( MODE_OUTPUT ) ) {
  					defaultModeNames.put( MODE_OUTPUT, modeName );
  				    }
  				}
  			    }
  			} else {
  			    val = parameters[i].getValue();
  			    this.settings.put(nodeName, val);
  			}
  		    }
  		}
  	    }
  	    
  	    this.modeMapping.initialize();
  	    this.outputMapping.initialize();
  	    this.manager2.initialize();
  
  	    ListIterator li = hints.listIterator();
  	    while ( li.hasNext() ) {
  		String modeName = (String) li.next();
  		Component helper = modeMapping.select( modeName );
  		if ( !defaultModeNames.containsKey( MODE_OTHERS ) && helper instanceof AttributeHelper ) { 
  		    defaultModeNames.put( MODE_OTHERS, modeName );
  		    getLogger().debug(modeName + " default mode for delete / update operations");
  		}
  		
  		if ( !defaultModeNames.containsKey( MODE_INSERT ) && helper instanceof KeyAttributeHelper ) { 
  		    defaultModeNames.put( MODE_INSERT, modeName );
  		    getLogger().debug(modeName + " default mode for insert operations");
  		}
  		modeMapping.release(helper);
  	    }
  	
  	    if ( !defaultModeNames.containsKey( MODE_OTHERS ) || 
  		 !defaultModeNames.containsKey( MODE_INSERT ) || 
  		 !defaultModeNames.containsKey( MODE_OUTPUT ) 
  		 ) {
  		throw new ConfigurationException("Not all default modes are configured:"
  						 + ( defaultModeNames.containsKey( MODE_INSERT ) ? " insert mode" : "" )
  						 + ( defaultModeNames.containsKey( MODE_OTHERS ) ? " others" : "" )
  						 + ( defaultModeNames.containsKey( MODE_OUTPUT ) ? " output" : "" )
  						 );
  	    }
  	    
  	} catch (Exception e) {
  	    throw new ConfigurationException(e.toString());
  	}
      }
  
  
      /**
       * Load a component.
       *
       * @param hint Object to identify this component
       * @param classURL component's class name / URL
       * @param config configuration for this component
       */
      private void load_component( 
  				ExcaliburComponentSelector selector,
  				Object hint, 
  				String classURL, 
  				Configuration config 
  				) 
  	throws Exception 
      {
  	Class clazz = null;
  	//FIXME(GP): Is it true that a class name containing a colon should be an URL?
  	if (classURL.indexOf(':') > 1) {
  	    URL url = urlFactory.getURL(classURL);
  	    byte[] b = getByteArrayFromStream(url.openStream());
  	    clazz = ((RepositoryClassLoader)ClassUtils.getClassLoader()).defineClass(b);
  	} else {
  	    clazz = ClassUtils.loadClass(classURL);
  	}
  	if (!Component.class.isAssignableFrom(clazz)) {
  	    throw new IllegalAccessException("Object " + classURL + " is not a Component");
  	}
  	selector.addComponent(hint, clazz, config);
      }
  
      /**
       * Helper to read in a class
       */
      private byte[] getByteArrayFromStream(
  					  InputStream stream
  					  ) 
      {
          List list = new ArrayList();
          byte[] b = new byte[BYTE_ARRAY_SIZE];
          int last = 0;
          try {
              while ((last = stream.read(b)) == BYTE_ARRAY_SIZE) {
                  list.add(b);
                  b = new byte[BYTE_ARRAY_SIZE];
              }
          } catch (IOException ioe) {
              getLogger().error("cannot read class byte stream", ioe);
          }
          list.add(b);
          int listSize = list.size();
          b = new byte[(listSize - 1) * BYTE_ARRAY_SIZE + last];
          int i;
          for (i = 0; i < listSize - 1; i++) {
              System.arraycopy(list.get(i), 0, b, i * BYTE_ARRAY_SIZE, BYTE_ARRAY_SIZE);
          }
          System.arraycopy(list.get(i), 0, b, i * BYTE_ARRAY_SIZE, last);
          return b;
      }
  
  
      // <==== please ignore
      //
  
      // ========================================================================
      // protected utility methods
      // ========================================================================
  
  
      /**
       * Returns the actual name of the parameter. If the name contains
       * no wildcard, the param is returned untouched, otherwise the
       * wildcard value is substituted for the * character. This probably
       * doesn't deserve a method unto itself, but I can imagine wanting
       * to use a more sophisticated matching and substitution algorithm.
       *
       * @param param the name of the parameter, possibly with a wildcard char
       * @param wildcard the wildcard value
       * @return the actual name of the parameter
       */
      String getActualParam(
  			  String param, 
  			  String wildcard
  			  ) 
      {
        int index;
        if ((index = param.indexOf('*')) != -1) {
          return param.substring(0,index)+wildcard+param.substring(index+1);
        } else {
          return param;
        }
      }
  
  
      /**
       * override super's method since we prefer to get the datasource
       * from defaults first or from sitemap parameters rather than from 
       * descriptor file.
       */
      protected DataSourceComponent getDataSource( Configuration conf, Parameters parameters )
  	throws ComponentException
      {
  	String sourceName = parameters.getParameter("connection", (String) settings.get("connection"));
  	if ( sourceName == null ) {
  	    return getDataSource( conf );
  	} else {
  	    getLogger().debug("Using datasource: "+sourceName);
  	    return (DataSourceComponent) this.dbselector.select(sourceName);
  	}
      }
  
      /**
       * Store a key/value pair in the request attributes. We prefix the key
       * with the name of this class to prevent potential name collisions.
       * This method overrides super class' method to allow an OutputHelper 
       * to take care of what to do with the values.
       */
      void setRequestAttribute(Request request, String key, Object value) {
  	try {
  	    OutputHelper oh = (OutputHelper) outputMapping.select( (String) defaultModeNames.get( MODE_OUTPUT ) );
  	    oh.setAttribute(request, key, value);
  	    outputMapping.release(oh);
  	} catch (Exception e) {
  	    getLogger()
  		.warn( "Could not select output mode " 
  		       + (String) defaultModeNames.get( MODE_OUTPUT ) 
  		       + ":" + e.getMessage() );
  	}
      }
  
      /**
       * Retrieve a value from the request attributes.
       * This method overrides super class' method to allow an OutputHelper 
       * to take care of where to get the values.
       */
      Object getRequestAttribute(Request request, String key) {
  	Object value = null;
  	try {
  	    OutputHelper oh = (OutputHelper) outputMapping.select( (String) defaultModeNames.get( MODE_OUTPUT ) );
  	    value = oh.getAttribute(request, key);
  	    outputMapping.release(oh);
  	} catch (Exception e) {
  	    getLogger()
  		.warn( "Could not select output mode "
  		       + (String) defaultModeNames.get( MODE_OUTPUT )
  		       + ":" + e.getMessage());
  	}
  	return value;
      }
  
  
      // ========================================================================
      // main method
      // ========================================================================
  
  
      /**
       * Add a record to the database.  This action assumes that
       * the file referenced by the "descriptor" parameter conforms
       * to the AbstractDatabaseAction specifications.
       */
      public Map act(
  		   Redirector redirector, 
  		   SourceResolver resolver, 
  		   Map objectModel, 
  		   String source, 
  		   Parameters param
  		   ) 
  	throws Exception 
      {
          DataSourceComponent datasource = null;
          Connection conn = null;
          Map results = new HashMap();
  
  	// read global parameter settings
  	boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT;
  	Request request = (Request) objectModel.get(Constants.REQUEST_OBJECT);
  
  	getLogger().debug("MDAA: act");
  	if (this.settings.containsKey("reloadable"))
  	    reloadable = Boolean.getBoolean((String) this.settings.get("reloadable"));
  	// read local parameter settings
          try {
              Configuration conf = 
  		this.getConfiguration(param.getParameter("descriptor", (String) this.settings.get("descriptor")), 
  				      param.getParameterAsBoolean("reloadable",reloadable));
  
              datasource = this.getDataSource(conf, param);
              conn = datasource.getConnection();
              if (conn.getAutoCommit() == true) {
                  conn.setAutoCommit(false);
              }
  
  	    Configuration[] tables = conf.getChildren("table");
  	    String tablesetname = param.getParameter("table-set", (String) this.settings.get("table-set"));
  	    Map set_tables = null; // default to old behaviour
  	    
  	    HashMap modeTypes = null;
  
  	    if (tablesetname != null) {
  		// new set based behaviour
  		Configuration[] tablesets = conf.getChildren("table-set");
  		String setname = null;
  		boolean found = false;
  		
  		// find tables contained in tableset
  		int j = 0;
  		for (j=0; j<tablesets.length; j++) {
                      setname = tablesets[j].getAttribute ("name", "");
                      if (tablesetname.trim().equals (setname.trim ())) {
                          found = true;
                          break;
                      }
                  }
                  if (!found) {
                      throw new IOException(" given set " + tablesetname + " does not exists in a description file.");
                  }
  
  		Configuration[] set = tablesets[j].getChildren("table");
  		
  		// construct a Map that contains the names of the tables
  		// contained in the requested tableset
  		set_tables = new HashMap(set.length);
  		for (int i=0; i<set.length; i++) {
  		    // look for alternative modes
  		    modeTypes = new HashMap(2);
  		    modeTypes.put( MODE_INSERT, set[i].getAttribute( "add-mode",    "insert" ) );
  		    modeTypes.put( MODE_OTHERS, set[i].getAttribute( "others-mode", "others" ) );
  		    set_tables.put(set[i].getAttribute("name",""), modeTypes);
  		}
  	    } else {
  		modeTypes = new HashMap(2);
  		modeTypes.put( MODE_INSERT, "insert" );
  		modeTypes.put( MODE_OTHERS, "others" );
  	    };
  	    for (int i=0; i<tables.length; i++) {
  		if (set_tables == null || set_tables.containsKey(tables[i].getAttribute("name"))) {
  		    if (tablesetname != null) {
  			modeTypes = (HashMap) set_tables.get(tables[i].getAttribute("name"));
  		    }
  		    processTable( tables[i], conn, request, results, modeTypes );
  		}
  	    }
  
              conn.commit();
  	    OutputHelper oh = (OutputHelper) outputMapping.select( (String) defaultModeNames.get( MODE_OUTPUT ) );
  	    oh.commit(request);
  	    outputMapping.release(oh);
          } catch (Exception e) {
              if (conn != null) {
                  try {
  		    getLogger().debug( "Rolling back transaction. Caused by " + e.getMessage() );
                      conn.rollback();
  		    OutputHelper oh = (OutputHelper) outputMapping.select( (String) defaultModeNames.get( MODE_OUTPUT ) );
  		    oh.rollback(request, e);
  		    outputMapping.release(oh);
                  } catch (SQLException se) {
                      getLogger().debug("There was an error rolling back the transaction", se);
                  }
              }
  
              //throw new ProcessingException("Could not add record :position = " + currentIndex, e);
  
  	    // don't throw an exception, an error has been signalled, that should suffice
              //throw new ProcessingException("Could not add record",e);
          } finally {
              if (conn != null) {
                  try {
                      conn.close();
                  } catch (SQLException sqe) {
                      getLogger().warn("There was an error closing the datasource", sqe);
                  }
              }
  
              if (datasource != null) this.dbselector.release(datasource);
          }
  
          return Collections.unmodifiableMap(results);
      }
  
      /**
       * Inserts a row or a set of rows into the given table based on the
       * request parameters
       *
       * @param table the table's configuration
       * @param conn the database connection
       * @param request the request
       */
      void processTable(
  		      Configuration table, 
  		      Connection conn, 
  		      Request request, 
  		      Map results,
  		      HashMap modeTypes
  		      ) 
  	throws SQLException,
  	       ConfigurationException,
  	       Exception 
      {
        PreparedStatement statement = null;
        try {
  	  getLogger()
  	      .debug( "i-mode: " + (String) modeTypes.get( MODE_INSERT ) +" (" + (String) defaultModeNames.get( MODE_INSERT ) + ")  " +
  		      "o-mode: " + (String) modeTypes.get( MODE_OTHERS ) +" (" + (String) defaultModeNames.get( MODE_OTHERS ) + ")" );
  	  CacheHelper queryData = this.getQuery( table, modeTypes, defaultModeNames );
  	  getLogger().debug("query: "+queryData.queryString);
  	  statement = conn.prepareStatement(queryData.queryString);
  
  	  if ( !queryData.isSet ) {
  	      getLogger().debug("=====> single row");
  	      processRow ( request, conn, statement, table, queryData, -1, null, results );
  	  } else {
  	      getLogger().debug("=====> multiple rows");
  	      if ( queryData.setMaster == -1 ) {
  		  throw new ConfigurationException( "Database description configuration error: Set mode without set master" );
  	      }
  	      AccessHelper dah = (AccessHelper) modeMapping.select( queryData.columns[ queryData.setMaster ].mode );
  	      Set vSet = dah.getAttributeSet( table.getAttribute( "name" ) 
  					      + "." 
  					      + queryData.columns[  queryData.setMaster ].columnConf.getAttribute( "name" ), 
  					      queryData.columns[  queryData.setMaster ].modeConf, 
  					      request );
  	      Iterator vIter = vSet.iterator();
  	      getLogger().debug("=====> set size is " + vSet.size());
  	      getLogger().debug("=====> set is empty? " + vSet.isEmpty());
  	      int rowIndex = 0;
  	      while( vIter.hasNext() ) {
  		  Object idx = vIter.next();
  		  getLogger().debug("=====> idx="+idx);
  		  processRow ( request, conn, statement, table, queryData, rowIndex, idx, results );
  		  rowIndex++;
  	      }
  	      modeMapping.release(dah);
  	  }
  	
        } finally {
  	  try {
  	      if (statement != null) {
  		  statement.close();
  	      }
  	  } catch (SQLException e) {}
        }
      }
  
      /**
       * Put key values to to request attributes.
       */
  
      void storeKeyValue( 
  		       Configuration tableConf, 
  		       Column key,
  		       Object idx,
  		       int rowIndex,
  		       Connection conn, 
  		       Statement statement, 
  		       Request request, 
  		       Map results
  		       ) 
  	throws SQLException, 
  	       ConfigurationException, 
  	       ComponentException 
      {
  	KeyAttributeHelper dah = (KeyAttributeHelper) modeMapping.select( key.mode );
  	if (!dah.includeAsValue()) {
  	    String keyname = getOutputName( tableConf, key.columnConf, rowIndex );
  	    Object value = dah.getPostValue( tableConf, key.columnConf, key.modeConf, conn, statement, request );
  	    getLogger().debug( "Retrieving autoincrement as " + value );
  	    setRequestAttribute( request, keyname, value );
  	    results.put( key.columnConf.getAttribute("name"), String.valueOf( value ) );
  	}
  	modeMapping.release(dah);
      }
  
      /**
       * compose name for output a long the lines of "table.column[row]"
       * or "table.column" if rowIndex is -1 
       */
      String getOutputName (
  			  Configuration tableConf,
  			  Configuration columnConf,
  			  int rowIndex
  			  )
  	throws ConfigurationException
      {
  	return tableConf.getAttribute("name")+"."+columnConf.getAttribute("name")+(rowIndex==-1?"": "["+rowIndex+"]");
      }
  
  
  
      /**
       * Sets the key value on the prepared statement. There are four modes:
       *
       * <dl>
       *   <dt>automatic (default)</dt>
       *   <dd>let the database automatically create the key. note this
       *       prohibits the action from storing the key value anywhere.</dd>
       *   <dt>manual</dt>
       *   <dd>create the key value using SELECT(dbcol)+1 from TABLE</dd>
       *   <dt>form</dt>
       *   <dd>look for the key value in the request parameters</dd>
       *   <dt>request-attribute</dt>
       *   <dd>look for the key value in the request attributes</dd>
       * </dl>
       *
       * This method has a couple of side effects. If the mode is manual,
       * the key value is stored in the request object's attributes for use 
       * by other inserts. The key is the string "key:TABLENAME:DBCOL". 
       * This method also puts the value of manually created keys in the results
       * map. That key is simply the value of the dbcol attribute. Note this
       * stuff is definitely up in the air.
       *
       * @param table the table's configuration object
       * @param key the key's configuration object
       * @param conn the database connection
       * @param statement the insert statement
       * @param currentIndex the position of the key column
       * @param request the request object
       * @param param the actual name of the request parameter
       * @return the number of columns by which to increment the currentIndex
       */
      int setKey(
  	       Configuration table, 
  	       Column column, 
  	       int currentIndex, 
  	       Object idx,
  	       int rowIndex,
  	       Connection conn, 
  	       PreparedStatement statement, 
  	       Request request, 
  	       Map results
  	       ) 
  	throws ConfigurationException, 
  	       SQLException, 
  	       ComponentException, 
  	       Exception 
      {
        KeyAttributeHelper dah = (KeyAttributeHelper) modeMapping.select( column.mode );
        String keyname = table.getAttribute( "name" ) + '.' +
  	  column.columnConf.getAttribute( "name" ) + 
  	  ( rowIndex == -1 ? "" : "[" + rowIndex + "]" );
  
        int columnCount = 0;
        if ( dah.includeInQuery() ) {
  	  if ( dah.includeAsValue() ) {
  	      Object value = dah.getPreValue( table, column.columnConf, column.modeConf, conn, request,  ( column.isSet ? idx : null ) );
  	      getLogger().debug( "Setting key " + keyname + " to " + value );
  	      statement.setObject( currentIndex, value );
  	      setRequestAttribute( request, keyname, value );
  	      results.put( column.columnConf.getAttribute( "name" ), String.valueOf( value ) );
  	      columnCount = 1;
  	  }
        } else {
  	  getLogger().debug( "Automatically setting key" );
        }  
        modeMapping.release( dah );
        return columnCount;
      }
  
      /**
       * Get the String representation of the PreparedStatement.  This is
       * mapped to the Configuration object itself, so if it doesn't exist,
       * it will be created.
       *
       * @param table the table's configuration object
       * @return the insert query as a string
       */
      protected CacheHelper getQuery(
  				   Configuration table,
  				   HashMap modeTypes,
  				   HashMap defaultModeNames
  				   ) 
  	throws ConfigurationException, 
  	       ComponentException 
      {
  	LookUpKey lookUpKey = new LookUpKey( table, modeTypes );
          CacheHelper queryData = null;
          synchronized( this.cachedQueryData ) {
              queryData = (CacheHelper) this.cachedQueryData.get( lookUpKey );
  
              if (queryData == null) {
                  Configuration[] values = table.getChild("values").getChildren("value");
                  Configuration[] keys = table.getChild("keys").getChildren("key");
  
  		queryData = new CacheHelper( keys.length, keys.length + values.length );
  		fillModes( keys,   true,  defaultModeNames, modeTypes, queryData );
   		fillModes( values, false, defaultModeNames, modeTypes, queryData );
  
                  StringBuffer queryBuffer = new StringBuffer("INSERT INTO ");
                  StringBuffer valueBuffer = new StringBuffer(") VALUES (");
  		KeyAttributeHelper dah;
  
                  queryBuffer.append(table.getAttribute("name"));
                  queryBuffer.append(" (");
  		int actualColumns = 0;
  
                  for (int i = 0; i < queryData.columns.length; i++) {
  		    if ( actualColumns > 0 ) {
  			queryBuffer.append( ", " );
  			valueBuffer.append( ", " );
  		    }
  		    if ( queryData.columns[i].isKey ) {
  			dah = (KeyAttributeHelper) modeMapping.select( queryData.columns[i].mode );
  			if ( dah.includeInQuery() ) {
  			    actualColumns++;
  			    queryBuffer.append( queryData.columns[i].columnConf.getAttribute( "name" ) );
  			    if ( dah.includeAsValue() ) {
  				valueBuffer.append( "?" );
  			    } else {
  				valueBuffer.append( dah.getSubquery( table, queryData.columns[i].columnConf, queryData.columns[i].modeConf ) );
  			    }
  			}
  			modeMapping.release( dah );
  		    } else {
  			actualColumns++;
  			queryBuffer.append( queryData.columns[i].columnConf.getAttribute( "name" ) );
  			valueBuffer.append( "?" );
  		    }
  		}
  
                  valueBuffer.append(")");
  		queryBuffer.append(valueBuffer);
  
                  queryData.queryString = queryBuffer.toString();
  
                  this.cachedQueryData.put( lookUpKey, queryData );
              }
          }
  
          return queryData;
      }
  
  
      /**
       * Set a column based in a prepared statement by asking an AttributeHelper.
       * Delegates 'real' work to similar method in super class.
       */
      protected void setColumn(
  			     Configuration table,
  			     Column column,
  			     int index,
  			     Object idx,
  			     int rowIndex,
  			     PreparedStatement stmt,
  			     Request request
  			      ) 
  	throws ConfigurationException, 
  	       Exception
      {
  	AttributeHelper dph = (AttributeHelper) modeMapping.select( column.mode );
  	Object value = dph.getValue( table.getAttribute( "name" ) + "." + column.columnConf.getAttribute( "name" ), 
  				     column.modeConf, request, ( column.isSet ? idx : null ) );
  	modeMapping.release( dph );
  	String cname = getOutputName( table, column.columnConf, rowIndex );
  	getLogger().debug( "Setting column " + cname + " to " + value +" using index " + idx );
  	this.setColumn( stmt, index, request, column.columnConf, cname, value,	rowIndex );
      }
  
  
      /**
       * set all necessary ?s and execute the query
       */
      protected void processRow ( 
  			       Request request,
  			       Connection conn,
  			       PreparedStatement statement,
  			       Configuration table,
  			       CacheHelper queryData,
  			       int rowIndex,
  			       Object idx,
  			       Map results
  			       )
  	throws SQLException,
  	       ConfigurationException,
  	       Exception 
      {
  	
  	int currentIndex = 1;
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		currentIndex += setKey( table, queryData.columns[i], currentIndex, idx, rowIndex, 
  					conn, statement, request, results );
  	    } else {
  		this.setColumn( table, queryData.columns[i], currentIndex, idx, rowIndex, 
  				statement, request );
  		currentIndex++;
  	    }
  	}
  	statement.execute();
  	// get resulting ids
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		storeKeyValue( table, queryData.columns[i], idx, rowIndex, 
  			       conn, statement, request, results );
  	    }
  	}
      }
  
  
      
      /**
       * Setup parsed attribute configuration object
       */
      protected void fillModes ( Configuration[] conf,
  			       boolean isKey,
  			       HashMap defaultModeNames,
  			       HashMap modeTypes,
  			       CacheHelper set
  			       )
  	throws ConfigurationException
      {
  	String setMode = null;
  	int setMaster = -1;
  	String setMastersMode = null;
  	boolean manyrows = false;
  	int offset = ( isKey ? 0: set.noOfKeys);
  
  	getLogger().debug("isKey="+(isKey?"true":"false")+" set.columns.length="+set.columns.length);
  	getLogger().debug(set.columns[0].getClass().getName());
  
          for ( int i = offset; i < conf.length + offset; i++ ) {
  	    getLogger().debug("i="+i);
  	    set.columns[i].columnConf =  conf[ i - offset ];
  	    set.columns[i].modeConf = getMode( set.columns[i].columnConf, selectMode( isKey, modeTypes ) );
  	    set.columns[i].mode = ( set.columns[i].modeConf != null ? 
  				    set.columns[i].modeConf.getAttribute( "name", selectMode( isKey, defaultModeNames ) ) :  selectMode( isKey, defaultModeNames ) );
  	    set.columns[i].isSet = false;
  	    set.columns[i].isKey = isKey;
  	    // Determine set mode for a whole column ...
  	    setMode = set.columns[i].columnConf.getAttribute("set", null);  // master vs slave vs null
  	    if ( setMode == null && set.columns[i].modeConf != null ) {
  		// ... or for each mode individually
  		setMode = set.columns[i].modeConf.getAttribute("set", null);
  	    }
  	    if ( setMode != null ) {
  		manyrows = true;
  		set.columns[i].isSet = true;
  		set.isSet = true;
  		if ( setMode.equals("master") ) {
  		    set.setMaster = i;
  		}
  	    } 
  	}
      }
  
  
      /**
       * determine which mode to use as default mode
       * here: INSERT
       * highly specific to operation INSERT / UPDATE / DELETE / SELECT
       */
  
      protected String selectMode (
  				  boolean isKeyColumns,
  				  HashMap modes
  				  )
      {
  	if (isKeyColumns)
  	    return (String) modes.get( MODE_INSERT );
  	else 
  	    return (String) modes.get( MODE_OTHERS );
      }
  
  
  
      /**
       * Choose a mode configuration based on its name.
       * @param conf Configuration (i.e. a column's configuration) that might have
       * several children configurations named "mode".
       * @param type desired type (i.e. every mode has a type attribute), find the first
       * mode that has a compatible type. Known types are "insert", used for key columns
       * when new data is inserted, "others" used for all other situations (i.e. non key
       * columns or non insert operations with key columns). Special mode "all" matches
       * all queried types.
       * @return configuration that has desired type or type "all" or null.
       */
      protected Configuration getMode( 
  				    Configuration conf,
  				    String type
  				    ) throws ConfigurationException
      {
  	String modeAll = "all";
  	Configuration[] modes = conf.getChildren("mode");
  	Configuration modeConfig = null;;
  
  	for ( int i=0; i<modes.length; i++ ) {
  	    String modeType = modes[i].getAttribute("type", "others");
  	    getLogger().debug("requested mode is \""+type+"\" found \""+modeType+"\"");
  	    if ( modeType.equals(type) || modeType.equals(modeAll)) {
  		getLogger().debug("returning "+modeType);
  		modeConfig = modes[i];
  		break;
  	    };
  	}
  
  	return modeConfig;
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseDeleteAction.java
  
  Index: ModularDatabaseDeleteAction.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting;
  
  import java.util.Map;
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.SQLException;
  import org.apache.cocoon.util.HashMap;
  
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import org.apache.cocoon.environment.Request;
  
  /**
   * Updates a record in a database. The action can update one or more
   * tables, and can update more than one row to a table at a time. The
   * form descriptor semantics for this are still in a bit of a state of
   * flux. 
   *
   * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
   * @author <a href="mailto:balld@apache.org">Donald Ball</a>
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/19 16:28:33 $
   */
  public class ModularDatabaseDeleteAction 
      extends ModularDatabaseAddAction
  {
  
      /**
       * determine which mode to use as default mode
       * here: DELETE
       * highly specific to operation INSERT / UPDATE / DELETE / SELECT
       */
  
      protected String selectMode (
  				  boolean isKeyColumns,
  				  HashMap modes
  				  )
      {
  	return (String) modes.get( MODE_OTHERS );
      }
  
  
  
      /**
       * Get the String representation of the PreparedStatement.  This is
       * mapped to the Configuration object itself, so if it doesn't exist,
       * it will be created.
       *
       * @param table the table's configuration object
       * @return the insert query as a string
       */
      protected CacheHelper getQuery(
  				   Configuration table,
  				   HashMap modeTypes,
  				   HashMap defaultModeNames
  				   ) 
  	throws ConfigurationException, 
  	       ComponentException 
      {
  	LookUpKey lookUpKey = new LookUpKey( table, modeTypes );
          CacheHelper queryData = null;
          synchronized( this.cachedQueryData ) {
              queryData = (CacheHelper) this.cachedQueryData.get( lookUpKey );
              if (queryData == null) {
                  Configuration[] keys = table.getChild("keys").getChildren("key");
  
  		queryData = new CacheHelper( keys.length, keys.length );
  		fillModes( keys, true, defaultModeNames, modeTypes, queryData );
  
                  StringBuffer queryBuffer = new StringBuffer("DELETE FROM ");
                  queryBuffer.append(table.getAttribute("name")).append(" WHERE ");
                  for (int i = 0; i < queryData.columns.length; i++) {
                      if ( i > 0 ) {
  			queryBuffer.append(" AND ");
  		    }
  		    queryBuffer
  			.append( queryData.columns[i].columnConf.getAttribute( "name" ) )
  			.append( "= ?" );
                  }
  
                  queryData.queryString = queryBuffer.toString();
  
                  this.cachedQueryData.put( lookUpKey, queryData );
              }
          }
  
          return queryData;
      }
  
  
      /**
       * set all necessary ?s and execute the query
       */
      protected void processRow ( 
  			       Request request,
  			       Connection conn,
  			       PreparedStatement statement,
  			       Configuration table,
  			       CacheHelper queryData,
  			       int rowIndex,
  			       Object idx,
  			       Map results
  			       )
  	throws SQLException,
  	       ConfigurationException,
  	       Exception 
      {
  	
  	int currentIndex = 1;
  
  	// ordering is different for DELETE just needs keys
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		currentIndex += setKey( table, queryData.columns[i], currentIndex, idx, rowIndex,
  					conn, statement, request, results );
  	    }
  	}
  	statement.execute();
  	// get resulting ids
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		storeKeyValue( table, queryData.columns[i], idx, rowIndex,
  			       conn, statement, request, results );
  	    }
  	}
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseSelectAction.java
  
  Index: ModularDatabaseSelectAction.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting;
  
  import java.util.Map;
  import java.sql.Connection;
  import java.sql.Clob;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Struct;
  import java.sql.Types;
  
  import java.io.InputStream;
  import java.io.BufferedInputStream;
  
  import org.apache.cocoon.util.HashMap;
  
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import org.apache.cocoon.environment.Request;
  
  /**
   * Selects a record from a database. The action can select one or more
   * tables, and can select from more than one row of a table at a
   * time. The form descriptor semantics for this are still in a bit of
   * a state of flux.
   *
   * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
   * @author <a href="mailto:balld@apache.org">Donald Ball</a>
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/19 16:28:33 $
   */
  public class ModularDatabaseSelectAction 
      extends ModularDatabaseAddAction
  {
  
      /**
       * determine which mode to use as default mode
       * here: SELECT
       * highly specific to operation INSERT / UPDATE / DELETE / SELECT
       */
  
      protected String selectMode (
  				  boolean isKeyColumns,
  				  HashMap modes
  				  )
      {
  	return (String) modes.get( MODE_OTHERS );
      }
  
  
  
      /**
       * Get the String representation of the PreparedStatement.  This is
       * mapped to the Configuration object itself, so if it doesn't exist,
       * it will be created.
       *
       * @param table the table's configuration object
       * @return the insert query as a string
       */
      protected CacheHelper getQuery(
  				   Configuration table,
  				   HashMap modeTypes,
  				   HashMap defaultModeNames
  				   ) 
  	throws ConfigurationException, 
  	       ComponentException 
      {
  	LookUpKey lookUpKey = new LookUpKey( table, modeTypes );
          CacheHelper queryData = null;
          synchronized( this.cachedQueryData ) {
              queryData = (CacheHelper) this.cachedQueryData.get( lookUpKey );
              if (queryData == null) {
                  Configuration[] keys = table.getChild("keys").getChildren("key");
                  Configuration[] values = table.getChild("values").getChildren("value");
  
  		queryData = new CacheHelper( keys.length, keys.length );
  		fillModes( keys, true, defaultModeNames, modeTypes, queryData );
  		fillModes( values, false, defaultModeNames, modeTypes, queryData );
  
                  StringBuffer queryBuffer = new StringBuffer("SELECT ");
  
                  queryBuffer.append(table.getAttribute("name")).append(" WHERE ");
                  for (int i = 0; i < queryData.columns.length; i++) {
  		    if ( !queryData.columns[i].isKey ) {
  			if ( i > 0 ) {
  			    queryBuffer.append(", ");
  			}
  			queryBuffer
  			    .append( queryData.columns[i].columnConf.getAttribute( "name" ) );
  		    }
                  }
  
                  queryBuffer.append(" FROM ").append(table.getAttribute("name")).append(" WHERE ");
                  for (int i = 0; i < queryData.columns.length; i++) {
  		    if ( queryData.columns[i].isKey ) {
  			if ( i > 0 ) {
  			    queryBuffer.append(" AND ");
  			}
  			queryBuffer
  			    .append( queryData.columns[i].columnConf.getAttribute( "name" ) )
  			    .append( "= ?");
  		    }
                  }
  
                  queryData.queryString = queryBuffer.toString();
  
                  this.cachedQueryData.put( lookUpKey, queryData );
              }
          }
  
          return queryData;
      }
  
  
      /**
       * set all necessary ?s and execute the query
       */
      protected void processRow ( 
  			       Request request,
  			       Connection conn,
  			       PreparedStatement statement,
  			       Configuration table,
  			       CacheHelper queryData,
  			       int rowIndex,
  			       Object idx,
  			       Map results
  			       )
  	throws SQLException,
  	       ConfigurationException,
  	       Exception 
      {
  	
  	int currentIndex = 1;
  
  	// ordering is different for SELECT just needs keys
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		currentIndex += setKey( table, queryData.columns[i], currentIndex, idx, rowIndex,
  					conn, statement, request, results );
  	    }
  	}
  	statement.execute();
  	// get resulting ids
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    storeKeyValue( table, queryData.columns[i], idx, rowIndex, 
  			   conn, statement, request, results );
  	}
  	// retrieve values
  	ResultSet resultset = statement.getResultSet();
  	rowIndex = -1;
  	while ( resultset.next() ){
  	    if ( ! ( rowIndex == -1 && resultset.isLast() ) ) {
  		rowIndex++;
  	    }
  	    for (int i = 0; i < queryData.columns.length; i++) {
  		if ( !queryData.columns[i].isKey ) {
  		    Object value = this.getColumn( resultset, queryData.columns[i].columnConf );
  		    String param = getOutputName( table, queryData.columns[i].columnConf, rowIndex );
  		    this.setRequestAttribute( request, param, value );
  		}
  	    }
  	}
      }
  
  
      /**
       * Get the Statement column so that the results are mapped correctly.
       * (this has been copied from AbstractDatabaseAction and modified slightly)
       */
      protected Object getColumn(ResultSet set, Configuration column )
      throws Exception {
          Integer type = (Integer) AbstractDatabaseAction.typeConstants.get(column.getAttribute("type"));
          String dbcol = column.getAttribute("name");
          Object value = null;
  
          switch (type.intValue()) {
              case Types.CLOB:
                  Clob dbClob = set.getClob(dbcol);
                  int length = (int) dbClob.length();
                  InputStream asciiStream = new BufferedInputStream(dbClob.getAsciiStream());
                  byte[] buffer = new byte[length];
                  asciiStream.read(buffer);
                  String str = new String(buffer);
                  asciiStream.close();
                  value = str;
                  break;
              case Types.BIGINT:
                  value = set.getBigDecimal(dbcol);
                  break;
              case Types.TINYINT:
                  value = new Byte(set.getByte(dbcol));
                  break;
              case Types.VARCHAR:
                  value  = set.getString(dbcol);
                  break;
              case Types.DATE:
                  value = set.getDate(dbcol);
                  break;
              case Types.DOUBLE:
                  value = new Double(set.getDouble(dbcol));
                  break;
              case Types.FLOAT:
                  value = new Float(set.getFloat(dbcol));
                  break;
              case Types.INTEGER:
                  value = new Integer(set.getInt(dbcol));
                  break;
              case Types.NUMERIC:
                  value = new Long(set.getLong(dbcol));
                  break;
              case Types.SMALLINT:
                  value = new Short(set.getShort(dbcol));
                  break;
              case Types.TIME:
                  value = set.getTime(dbcol);
                  break;
              case Types.TIMESTAMP:
                  value = set.getTimestamp(dbcol);
                  break;
              case Types.ARRAY:
                  value = set.getArray(dbcol); // new Integer(set.getInt(dbcol));
                  break;
              case Types.BIT:
                  value = new Integer(set.getInt(dbcol));
                  break;
              case Types.CHAR:
                  value = new Integer(set.getInt(dbcol));
                  break;
  	    case Types.STRUCT:
  		value = (Struct) set.getObject(dbcol);
  		break;
  	    case Types.OTHER:
  		value = set.getObject(dbcol);
  		break;
  
              default:
                  // The blob types have to be requested separately, via a Reader.
                  value = "";
                  break;
          }
  
          return value;
      }
  
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseUpdateAction.java
  
  Index: ModularDatabaseUpdateAction.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting;
  
  import java.util.Map;
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.SQLException;
  import org.apache.cocoon.util.HashMap;
  
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  import org.apache.cocoon.environment.Request;
  
  /**
   * Updates a record in a database. The action can update one or more
   * tables, and can update more than one row to a table at a time. The
   * form descriptor semantics for this are still in a bit of a state of
   * flux. 
   *
   * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
   * @author <a href="mailto:balld@apache.org">Donald Ball</a>
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/19 16:28:33 $
   */
  public class ModularDatabaseUpdateAction 
      extends ModularDatabaseAddAction
  {
  
      /**
       * determine which mode to use as default mode
       * here: UPDATE
       * highly specific to operation INSERT / UPDATE / DELETE / SELECT
       */
  
      protected String selectMode (
  				  boolean isKeyColumns,
  				  HashMap modes
  				  )
      {
  	return (String) modes.get( MODE_OTHERS );
      }
  
  
  
      /**
       * Get the String representation of the PreparedStatement.  This is
       * mapped to the Configuration object itself, so if it doesn't exist,
       * it will be created.
       *
       * @param table the table's configuration object
       * @return the insert query as a string
       */
      protected CacheHelper getQuery(
  				   Configuration table,
  				   HashMap modeTypes,
  				   HashMap defaultModeNames
  				   ) 
  	throws ConfigurationException, 
  	       ComponentException 
      {
  	LookUpKey lookUpKey = new LookUpKey( table, modeTypes );
          CacheHelper queryData = null;
          synchronized( this.cachedQueryData ) {
              queryData = (CacheHelper) this.cachedQueryData.get( lookUpKey );
              if (queryData == null) {
                  Configuration[] keys = table.getChild("keys").getChildren("key");
                  Configuration[] values = table.getChild("values").getChildren("value");
  
  		queryData = new CacheHelper( keys.length, keys.length + values.length );
  		fillModes( keys,   true,  defaultModeNames, modeTypes, queryData );
  		fillModes( values, false, defaultModeNames, modeTypes, queryData );
  
                  StringBuffer queryBuffer = new StringBuffer("UPDATE ");
                  queryBuffer.append(table.getAttribute("name")).append(" SET ");
  
  		
  		int cols = 0;
  		for (int i = 0; i < queryData.columns.length; i++) {
  		    if ( !queryData.columns[i].isKey ) {
  			if ( cols > 0 ) {
  			    queryBuffer.append(", ");
  			}
  			cols++;
  			queryBuffer
  			    .append( queryData.columns[i].columnConf.getAttribute( "name" ) )
  			    .append( "= ?" );
  		    }
  		}
                  queryBuffer.append(" WHERE ");
                  for (int i = 0; i < queryData.columns.length; i++) {
  		    if ( queryData.columns[i].isKey ) {
  			if ( i > 0 ) {
  			    queryBuffer.append(" AND ");
  			}
  			queryBuffer
  			    .append( queryData.columns[i].columnConf.getAttribute( "name" ) )
  			    .append( "= ?" );
  		    }
  		}
  
                  queryData.queryString = queryBuffer.toString();
  
                  this.cachedQueryData.put( lookUpKey, queryData );
              }
          }
  
          return queryData;
      }
  
  
      /**
       * set all necessary ?s and execute the query
       */
      protected void processRow ( 
  			       Request request,
  			       Connection conn,
  			       PreparedStatement statement,
  			       Configuration table,
  			       CacheHelper queryData,
  			       int rowIndex,
  			       Object idx,
  			       Map results
  			       )
  	throws SQLException,
  	       ConfigurationException,
  	       Exception 
      {
  	
  	int currentIndex = 1;
  
  	// ordering is different for UPDATE than for INSERT: values, keys
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( !queryData.columns[i].isKey ) {
  		this.setColumn( table, queryData.columns[i], currentIndex, idx, rowIndex,
  				statement, request );
  		currentIndex++;
  	    }
  	}
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		currentIndex += setKey( table, queryData.columns[i], currentIndex, idx, rowIndex,
  					conn, statement, request, results );
  	    }
  	}
  	statement.execute();
  	// get resulting ids
  	for (int i = 0; i < queryData.columns.length; i++) {
  	    if ( queryData.columns[i].isKey ) {
  		storeKeyValue( table, queryData.columns[i], idx, rowIndex,
  			       conn, statement, request, results );
  	    }
  	}
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AbstractAttributeHelper.java
  
  Index: AbstractAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  
  import java.util.HashMap;
  
  /**
   * AbstractDatabaseHelper gives you the infrastructure for easily
   * deploying more database access helpers.  In order to get at the
   * Logger, use getLogger().
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/19 16:28:33 $ */
  public abstract class AbstractAttributeHelper extends AbstractLoggable
      implements AttributeHelper, Configurable, Disposable { 
  
      /**
       * Stores (global) configuration parameters as <code>key</code> /
       * <code>value</code> pairs.
       */
      protected HashMap settings = null;
  
      /**
       * Configures the database access helper.
       *
       * Takes all elements nested in component declaration and stores
       * them as key-value pairs in <code>settings</code>. Nested
       * configuration option are not catered for. This way global
       * configuration options can be used.
       *
       * For nested configurations override this function.  
       * */
      public void configure(Configuration conf) throws ConfigurationException {
          if (conf != null) {
  	    String key = null;
  	    String val = null;
  	    Configuration[] parameters = conf.getChildren();
  	    this.settings = new HashMap(parameters.length);
  	    for ( int i = 0; i < parameters.length; i++) {
  		key = parameters[i].getName();
  		val = parameters[i].getValue();
  		if ( key != null )
  		    this.settings.put(key, val);
  	    }
  	}
      }
  
      /**
       *  dispose
       */
      public void dispose() {
          // Purposely empty so that we don't need to implement it in every
          // class.
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AbstractKeyAttributeHelper.java
  
  Index: AbstractKeyAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  
  import java.util.HashMap;
  
  /**
   * AbstractDatabaseHelper gives you the infrastructure for easily
   * deploying more DatabaseHelpers.  In order to get at the Logger, use
   * getLogger().
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/11/19 16:28:33 $ */
  public abstract class AbstractKeyAttributeHelper extends AbstractLoggable
      implements KeyAttributeHelper, Configurable, Disposable {
  
      /**
       * Stores (global) configuration parameters as <code>key</code> /
       * <code>value</code> pairs.
       */
      protected HashMap settings = null;
  
      /**
       * Configures the database access helper.
       *
       * Takes all elements nested in component declaration and stores
       * them as key-value pairs in <code>settings</code>. Nested
       * configuration option are not catered for. This way global
       * configuration options can be used.
       *
       * For nested configurations override this function. 
       * */
      public void configure(Configuration conf) throws ConfigurationException {
  	getLogger().debug("ADBH: configure");
          if (conf != null) {
  	    String key = null;
  	    String val = null;
  	    Configuration[] parameters = conf.getChildren();
  	    this.settings = new HashMap(parameters.length);
  	    for ( int i = 0; i < parameters.length; i++) {
  		key = parameters[i].getName();
  		val = parameters[i].getValue();
  		if ( key != null )
  		    this.settings.put(key, val);
  	    }
  	}
      }
  
      /**
       *  dispose
       */
      public void dispose() {
          // Purposely empty so that we don't need to implement it in every
          // class.
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AccessHelper.java
  
  Index: AccessHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.util.SortedSet;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.component.Component;
  
  /**
   * AccessHelper specifies an interface for components that provide
   * access to a set of parameters e.g. request parameters, request
   * attributes, session attributes &c.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: AccessHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   * */
  public interface AccessHelper extends Component {
      String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.AccessHelper";
      
      
      /**
       * Return a sorted set of wildcard matches.
       * @param name a String that specifies what the caller thinks
       * would identify a set of parameters. This is mainly a fallback
       * if no modeConf is present.
       * @param modeConf column's mode configuration from resource
       * description.  
       * @param request the request object
       * */
      public SortedSet getAttributeSet( String name, 
  				      Configuration modeConf,
  				      Request request
  				     )
  	throws ConfigurationException;
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AttributeHelper.java
  
  Index: AttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /**
   * AttributeHelper specifies an interface for components that provide
   * access to individual parameters e.g. request parameters, request
   * attributes, session attributes &c.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: AttributeHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   * */
  public interface AttributeHelper extends AccessHelper {
      String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.AttributeHelper";
      
      /**
       * Return an object that is to be taken for insertion.
       * @param name a String that specifies what the caller thinks
       * would identify a set of parameters. This is mainly a fallback
       * if no modeConf is present.
       * @param modeConf column's mode configuration from resource
       * description.  
       * @param request the request object
       * @param index Element from the getParameterSet() to identify an
       * instance in a set of similar parameters, when multiple rows are
       * inserted.
       * */
      public Object getValue( String name,
  			    Configuration modeConf,
  			    Request request, 
  			    Object index
  			    )
  	throws ConfigurationException;
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/HsqlIdentityKeyAttributeHelper.java
  
  Index: HsqlIdentityKeyAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.lang.Long;
  import java.util.SortedSet;
  import java.sql.CallableStatement;
  import java.sql.PreparedStatement;
  import java.sql.Connection;
  import java.sql.ResultSet;
  import java.sql.Statement;
  import java.sql.Types;
  import java.sql.SQLException;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  /**
   * Abstraction layer to encapsulate different DBMS behaviour for autoincrement columns.
   *
   * Here: HSQLDB 1.6 IDENTITY columns
   * {@link http://hsqldb.sourceforge.net}
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: HsqlIdentityKeyAttributeHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   */
  public class HsqlIdentityKeyAttributeHelper 
      implements KeyAttributeHelper, ThreadSafe
  {
      
      public Object getPostValue( Configuration tableConf, 
  				Configuration columnConf, 
  				Configuration modeConf, 
  				Connection conn, 
  				Statement stmt, 
  				Request request
  				) throws SQLException, ConfigurationException 
      {
  	Long id = null;
  	/*
  	CallableStatement callStmt = conn.prepareCall("? = {CALL IDENTITY()}");
  	callStmt.registerOutParameter(1, Types.INTEGER);
  	ResultSet resultSet = callStmt.executeQuery();
  	*/
  
  	// hsqldb doesn't support callable statements
  	PreparedStatement pstmt = conn.prepareStatement("CALL IDENTITY()");
  	ResultSet resultSet = pstmt.executeQuery();
  	while ( resultSet.next() ) {
  	    id = new Long(resultSet.getInt(1));
  	}
  	resultSet.close();
  
  	return id;
      };
      
      public boolean includeInQuery() {
  	return false;
      };
      public boolean includeAsValue() {
  	return false;
      };
  
      public Object getPreValue( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf, 
  			       Connection conn, 
  			       Request request,
  			       Object idx
  			       ) throws SQLException, ConfigurationException {
  	return null;
      };
  
      public String getSubquery( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf 
  			       ) throws ConfigurationException {
  	return null;
      };
  
      public SortedSet getAttributeSet( String name,
  				      Configuration modeConf,
  				      Request request
  				     )
  	throws ConfigurationException
      {
  	return null;
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/IfxSerialKeyAttributeHelper.java
  
  Index: IfxSerialKeyAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.lang.Long;
  import java.util.SortedSet;
  import java.sql.Connection;
  import java.sql.Statement;
  import java.sql.SQLException;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import com.informix.jdbc.IfxStatement;
  
  /**
   * Abstraction layer to encapsulate different DBMS behaviour for autoincrement columns.
   *
   * Here: Informix IUS 9.21 SERIAL columns
   * (need another one for SERIAL8 ones!)
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: IfxSerialKeyAttributeHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   */
  public class IfxSerialKeyAttributeHelper 
      implements KeyAttributeHelper, ThreadSafe
  {
      
      public Object getPostValue( Configuration tableConf, 
  				Configuration columnConf, 
  				Configuration modeConf, 
  				Connection conn, 
  				Statement stmt, 
  				Request request
  				) throws SQLException, ConfigurationException 
      {
  	return new Long(((com.informix.jdbc.IfxStatement) stmt).getSerial());
      };
      
      public boolean includeInQuery() {
  	return false;
      };
      public boolean includeAsValue() {
  	return false;
      };
  
      public Object getPreValue( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf, 
  			       Connection conn, 
  			       Request request,
  			       Object idx
  			       ) throws SQLException, ConfigurationException {
  	return null;
      };
  
      public String getSubquery( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf 
  			       ) throws ConfigurationException {
  	return null;
      };
  
      public SortedSet getAttributeSet( String name,
  				      Configuration modeConf,
  				      Request request
  				     )
  	throws ConfigurationException
      {
  	return null;
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/KeyAttributeHelper.java
  
  Index: KeyAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.sql.Connection;
  import java.sql.Statement;
  import java.sql.SQLException;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /**
   * Abstraction layer to encapsulate different DBMS behaviour for key
   * attribute columns.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: KeyAttributeHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $ 
   * */
  public interface KeyAttributeHelper extends AccessHelper {
      String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.KeyAttributeHelper";
      
      /**
       * Return key attribute value of last inserted row.
       *
       * @param name a String that specifies what the caller thinks
       * would identify a set of parameters. This is mainly a fallback
       * if no modeConf is present.
  
       * @param tableConf Table's configuration from resource description.
       * @param columnConf column's configuration from resource description.
       * @param mdoeConf this mode's configuration from resource description.
       * @param conn Connection
       * @param stmt Statement that was executed to insert the last row.
       * @param request The request object
       * @return value representing the last key value value.
       * */
      public Object getPostValue( Configuration tableConf, 
  				Configuration columnConf, 
  				Configuration modeConf, 
  				Connection conn, 
  				Statement stmt, 
  				Request request
  			      ) 
  	throws SQLException, ConfigurationException;
      
      /**
       * Boolean whether the key attribute column needs to be included
       * in the insert query.
       *
       * @return true if the column is needed, false if the column
       * should be skipped.  
       * */
      public boolean includeInQuery( );
  
      /**
       * Boolean whether the key attribute needs to be included in the
       * insert query as an attribute value (no subquery).
       *
       * @return true if a value is needed, false if a subquery
       * expression is used or the column is skipped altogether.
       * */
      public boolean includeAsValue( );
  
      /**
       * Provide the value for the key attribute column.
       *
       * If a value for the key value column is needed (i.e. the column
       * is not skipped), this value is computed here.
       *
       * @param tableConf Table's configuration from resource description.
       * @param columnConf column's configuration from resource description.
       * @param mdoeConf this mode's configuration from resource description.
       * @param conn Connection
       * @param request The request object
       * @param idx In case of multiple rows to be inserted, index to the desired row
       * @return exact value for key attribute column 
       * */
      public Object getPreValue( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf, 
  			       Connection conn, 
  			       Request request,
  			       Object idx
  			     ) 
  	throws SQLException, ConfigurationException;
  
      /**
       * Provide subquery string for the key attribute column.
       *
       * If a value for the autoincrement column is needed (i.e. the
       * column is not skipped), and the value can be determined through
       * a nested subquery, this function provides the subquery as a
       * string.
       *
       * @return subquery string for autoincrement column.
       */
      public String getSubquery( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf
  			       ) 
  	throws ConfigurationException;
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/ManualKeyAttributeHelper.java
  
  Index: ManualKeyAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.sql.Connection;
  import java.sql.Statement;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import java.lang.Long;
  import java.util.Map;
  import java.util.HashMap;
  import java.util.SortedSet;
  
  /**
   * Abstraction layer to encapsulate different DBMS behaviour for
   * autoincrement columns.
   *
   * Here: manual mode The new value is determined by doing a "select
   * max(column)+1 from table" query. With transactions and correct
   * isolation levels, this should to the trick almost everywhere.
   *
   * Note however, that the above query does not prevent a parallel
   * transaction to try to insert a row with the same ID since it
   * requires only shared locks. C.f. "Phantom Problem"
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: ManualKeyAttributeHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   */
  public class ManualKeyAttributeHelper 
      extends AbstractKeyAttributeHelper 
      implements ThreadSafe
  {
      private Map selectStatements = new HashMap();
      
      public Object getPostValue ( Configuration tableConf, 
  				 Configuration columnConf,
  				 Configuration modenConf,
  				 Connection conn, 
  				 Statement stmt, 
  				 Request request
  				 ) 
  	throws SQLException, ConfigurationException 
      {
  	return null;
      };
      
      public boolean includeInQuery ( ) {
  	return true;
      };
      
      public boolean includeAsValue ( ) {
  	return true;
      };
  
      public Object getPreValue ( Configuration tableConf, 
  				Configuration columnConf,
  				Configuration modeConf,
  				Connection conn, 
  				Request request,
  				Object idx
  				) 
  	throws SQLException, ConfigurationException 
      {
          /** Set the key value using SELECT MAX(keyname)+1 **/
  	String tableName = tableConf.getAttribute("name","");
          String selectQuery = this.getSelectQuery(tableName, columnConf);
          PreparedStatement select_statement = conn.prepareStatement(selectQuery);
          ResultSet set = select_statement.executeQuery();
          set.next();
          int maxid = set.getInt("maxid");
          set.close();
          select_statement.close();
  	getLogger().debug("autoincrementValue " + (maxid+1));
          return new Long(maxid + 1);
      };
  
      public String getSubquery ( Configuration tableConf,
  				Configuration columnConf,
  				Configuration modeConf 
  				) 
  	throws ConfigurationException 
      {
  	return null;
      };
  
  
      public SortedSet getAttributeSet( String name,
  				      Configuration modeConf,
  				      Request request
  				     )
  	throws ConfigurationException
      {
  	return null;
      }
  
  
      /**
       * Set the String representation of the MaxID lookup statement.  This is
       * mapped to the Configuration object itself, so if it doesn't exist,
       * it will be created.
       */
      protected final synchronized void setSelectQuery( String tableName, 
  						      Configuration entry
  						      ) 
  	throws ConfigurationException 
      {
          StringBuffer queryBuffer = new StringBuffer("SELECT max(");
          queryBuffer.append(entry.getAttribute("name"));
          queryBuffer.append(") AS maxid FROM ");
          queryBuffer.append(tableName);
  
          this.selectStatements.put(entry, queryBuffer.toString());
      }
  
      protected final synchronized String getSelectQuery( String tableName, 
  							Configuration entry
  							) 
  	throws ConfigurationException 
      {
          String result = (String) this.selectStatements.get(entry);
  	if (result == null) {
  	    setSelectQuery(tableName, entry);
  	    result = (String) this.selectStatements.get(entry);
  	}
  	return result;
      }
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/OutputHelper.java
  
  Index: OutputHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.component.Component;
  
  /**
   * Communicate results to other components. This could be done via
   * request attributes, session attribute etc. Implementors should obey
   * the transactional nature and e.g. queue values as request
   * attributes and do the real communication e.g. to a bean only when
   * the transaction completes successfully.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: OutputHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   * */
  public interface OutputHelper extends Component {
      String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper";
      
      
      /**
       * communicate an attribute value to further processing logic. 
       * @param request The request object
       * @param name The attribute's label, consisting of "table.column"
       * or "table.column[index]" in case of multiple attributes of the
       * same spec.
       * @param value The attriute's value.
       * */
      public void setAttribute( Request request,
  			      String name,
  			      Object value
  			      );
  
  
      /**
       * retrieve an attribute from other processing steps. <em>Do we
       * really need this thingy?</em>
       * @param request The request object
       * @param name The attribute's label, consisting of "table.column"
       * or "table.column[index]" in case of multiple attributes of the
       * same spec.
       * @return requested object.
       * */
      public Object getAttribute( Request request,
  				String name );
  
      /**
       * If a database transaction needs to rollback, this is called to
       * inform the further processing logic about this fact. All
       * already set attribute values are invalidated. <em>This is difficult
       * because only the request object can be used to synchronize this
       * and build some kind of transaction object. Beaware that sending
       * your data straight to some beans or other entities could result
       * in data corruption!</em>
       * */
      public void rollback( Request request, Exception e );
  
  
      /**
       * Signal that the database transaction completed
       * successfully. See notes on @link{rollback}.
       * */
      public void commit( Request request );
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestAttributeHelper.java
  
  Index: RequestAttributeHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.util.Enumeration;
  import java.util.SortedSet;
  import java.util.TreeSet;
  import java.sql.Connection;
  import java.sql.Statement;
  import java.sql.SQLException;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  /**
   * RequestAttributeHelper accesses request attributes.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: RequestAttributeHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   */
  public class RequestAttributeHelper 
      extends AbstractAttributeHelper 
      implements KeyAttributeHelper, ThreadSafe
  {
      
      // AccessHelper methods
  
      /**
       * Return a sorted set of wildcard matches.
       * @param name String identifying the parameter
       * @param modeConf mode's configuration from resource description.
       * @param request the request object
       */
      public SortedSet getAttributeSet( String name,
  				      Configuration modeConf,
  				      Request request
  				      )
  	throws ConfigurationException
      {
  	// split the parameter's name so that the "*" could be determined
  	// by looking at the parameters' names that start with the prefix
  	// and end with the suffix
  	String wildcard = name;
  	if ( modeConf != null ) {
  	    wildcard = modeConf.getAttribute( "parameter", wildcard );
  	}
  	int wildcardIndex = wildcard.indexOf( "*" );
  	String prefix = wildcard.substring( 0, wildcardIndex );
  	String suffix;
  	if ( wildcard.length() >= wildcardIndex + 1 ) {
  	    suffix = wildcard.substring( wildcardIndex + 1 );
  	} else {
  	    suffix = "";
  	}
  	
  	Enumeration names = request.getAttributeNames();
  	SortedSet matchset = new TreeSet();
  
  	while (names.hasMoreElements()) {
  	    String pname = (String) names.nextElement();
  	    if ( pname.startsWith( prefix ) && pname.endsWith( suffix ) ) {
  		String _wildcard = pname.substring( prefix.length() );
  		_wildcard = _wildcard.substring( 0, _wildcard.length() - suffix.length() );
  		matchset.add( _wildcard );
  	    }
  	}
  	return matchset;
      }
  
      // AttributeHelper methods
  
      public Object getValue( String name,
  			    Configuration modeConf,
  			    Request request, 
  			    Object index
  			    )
  	throws ConfigurationException
      {
  	int idx;
  	String pname = name;
  	if ( modeConf != null ) {
  	    pname = modeConf.getAttribute( "parameter", pname );
  	}
  	if ( index != null && ( idx = pname.indexOf( '*' ) ) != -1 ) {
  	    return request.getAttribute( pname.substring( 0, idx ) + index + pname.substring( idx + 1 ) );
  	} else {
  	    return request.getAttribute( pname );
  	}
      }
  
      
      // KeyAttributeHelper methods
      
  
      public Object getPostValue( Configuration tableConf, 
  				Configuration columnConf, 
  				Configuration modeConf, 
  				Connection conn, 
  				Statement stmt, 
  				Request request
  				) 
  	throws SQLException, ConfigurationException
      {
  	return null;
      }
  
      public boolean includeInQuery() 
      {
  	return true;
      }
  
      public boolean includeAsValue()
      {
  	return true;
      }
  
      public Object getPreValue( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf, 
  			       Connection conn, 
  			       Request request,
  			       Object idx
  			       ) 
  	throws SQLException, ConfigurationException
      {
  	return this.getValue( tableConf.getAttribute( "name" ) + "." + columnConf.getAttribute( "name" ), 
  			      modeConf, request, idx );
      }
  
      public String getSubquery( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf
  			       ) 
  	throws ConfigurationException
      {
  	return null;
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestAttributeOutputHelper.java
  
  Index: RequestAttributeOutputHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.util.Enumeration;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  
  /**
   * Abstraction layer to encapsulate different DBMS behaviour for
   * normal i.e. non key columns.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: RequestAttributeOutputHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   * */
  public class RequestAttributeOutputHelper 
      extends AbstractLoggable
      implements OutputHelper {
      
      /**
       * communicate an attribute value to further processing logic. 
       * @param request The request object
       * @param name The attribute's label, consisting of "table.column"
       * or "table.column[index]" in case of multiple attributes of the
       * same spec.
       * @param value The attriute's value.
       * */
      public void setAttribute( Request request,
  			      String name,
  			      Object value
  			      )
      {
  	request.setAttribute("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:"+name,value);
      }
  
  
      /**
       * retrieve an attribute from other processing steps. <em>Do we
       * really need this thingy?</em>
       * @param request The request object
       * @param name The attribute's label, consisting of "table.column"
       * or "table.column[index]" in case of multiple attributes of the
       * same spec.
       * @return requested object.
       * */
      public Object getAttribute( Request request,
  				String name )
      {
  	return request.getAttribute("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:"+name);
      }
  
      /**
       * If a database transaction needs to rollback, this is called to
       * inform the further processing logic about this fact. All
       * already set attribute values are invalidated. <em>This is difficult
       * because only the request object can be used to synchronize this
       * and build some kind of transaction object. Beaware that sending
       * your data straight to some beans or other entities could result
       * in data corruption!</em>
       * */
      public void rollback( Request request, Exception e )
      {
  	/*
  	Enumeration attributes = request.getAttributeNames();
  	while ( attributes.hasMoreElements() ) {
  	    String name = (String) attributes.nextElement();
  	    if ( name.startsWith("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:") ) {
  		request.removeAttribute(name);
  	    }
  	}
  	*/
  	request.setAttribute("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper.Exception",e.getMessage());
      };
  
  
      /**
       * Signal that the database transaction completed
       * successfully. See notes on @link{rollback}.
       * */
      public void commit( Request request )
      {
  	// empty method
      }
  
  }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestParameterHelper.java
  
  Index: RequestParameterHelper.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included  with this distribution in *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  package org.apache.cocoon.acting.ModularDatabaseAccess;
  
  import java.util.Enumeration;
  import java.util.SortedSet;
  import java.util.TreeSet;
  import java.sql.Connection;
  import java.sql.Statement;
  import java.sql.SQLException;
  import org.apache.cocoon.environment.Request;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  /**
   * RequestParameterHelper accesses request parameters.
   *
   * @author <a href="mailto:haul@apache.org">Christian Haul</a>
   * @version CVS $Id: RequestParameterHelper.java,v 1.1 2001/11/19 16:28:33 haul Exp $
   */
  public class RequestParameterHelper 
      extends AbstractAttributeHelper 
      implements KeyAttributeHelper, ThreadSafe
  {
      
      // AccessHelper methods
  
      /**
       * Return a sorted set of wildcard matches.
       * @param name String identifying the parameter
       * @param modeConf mode's configuration from resource description.
       * @param request the request object
       */
      public SortedSet getAttributeSet( String name,
  				      Configuration modeConf,
  				      Request request
  				      )
  	throws ConfigurationException
      {
  	// split the parameter's name so that the "*" could be determined
  	// by looking at the parameters' names that start with the prefix
  	// and end with the suffix
  	String wildcard = name;
  	SortedSet matchset = new TreeSet();
  
  	if ( modeConf != null ) {
  	    wildcard = modeConf.getAttribute( "parameter" );
  	}
  	int wildcardIndex = wildcard.indexOf( "*" );
  	if ( wildcardIndex != -1 ) {
  	    String prefix = wildcard.substring( 0, wildcardIndex );
  	    String suffix;
  	    if ( wildcard.length() >= wildcardIndex + 1 ) {
  		suffix = wildcard.substring( wildcardIndex + 1 );
  	    } else {
  		suffix = "";
  	    }
  	
  	    Enumeration names = request.getParameterNames();
  
  	    while ( names.hasMoreElements() ) {
  		String pname = (String) names.nextElement();
  		if ( pname.startsWith( prefix ) && pname.endsWith( suffix ) ) {
  		    String _wildcard = pname.substring( prefix.length() );
  		    _wildcard = _wildcard.substring( 0, _wildcard.length() - suffix.length() );
  		    matchset.add( _wildcard );
  		}
  	    }
  	} else {
  	    // no wildcard, this might be an array, though
  	    Object[] parameterValues = request.getParameterValues( wildcard );
  	    if ( parameterValues != null ) {
  		for ( int i = 0; i < parameterValues.length; i++ ) {
  		    matchset.add( new Integer(i) );
  		}
  	    }
  	}
  
  	return matchset;
      }
  
      // AttributeHelper methods
  
      public Object getValue( String name,
  			    Configuration modeConf,
  			    Request request, 
  			    Object index
  			    )
  	throws ConfigurationException
      {
  	int idx;
  	String pname = name;
  	Object result = null;
  	if ( modeConf != null ) {
  	    pname = modeConf.getAttribute( "parameter", pname );
  	}
  
  	if (index != null && (idx = pname.indexOf('*')) != -1) {
  	    // index is wildcard match
  	    result = request.getParameter( pname.substring( 0, idx ) + index + pname.substring( idx + 1 ) );
  	} else {
  	    if ( index != null ) {
  		// index to array of multiple parameter values
  		Object[] parameterValues = request.getParameterValues( pname );
  		if ( parameterValues != null ) {
  		    result = parameterValues[((Integer) index).intValue()];
  		}
  	    } else {
  		// no index present
  		result = request.getParameter( pname );
  	    }
  	}
  	return result;
      }
  
      
      // KeyAttributeHelper methods
      
  
      public Object getPostValue( Configuration tableConf, 
  				Configuration columnConf, 
  				Configuration modeConf, 
  				Connection conn, 
  				Statement stmt, 
  				Request request
  				) 
  	throws SQLException, ConfigurationException
      {
  	return null;
      }
  
      public boolean includeInQuery() 
      {
  	return true;
      }
  
      public boolean includeAsValue()
      {
  	return true;
      }
  
      public Object getPreValue( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf, 
  			       Connection conn, 
  			       Request request,
  			       Object idx
  			       ) 
  	throws SQLException, ConfigurationException
      {
  	return this.getValue( tableConf.getAttribute("name") + "." + columnConf.getAttribute("name"),
  			      modeConf, request, idx );
      }
  
      public String getSubquery( Configuration tableConf, 
  			       Configuration columnConf, 
  			       Configuration modeConf
  			       ) 
  	throws ConfigurationException
      {
  	return null;
      }
  
  }
  
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     webmaster@xml.apache.org
To unsubscribe, e-mail:          cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org