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/23 11:50:36 UTC

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

haul        01/11/23 02:50:36

  Modified:    webapp/WEB-INF/db cocoondb.properties cocoondb.script
               scratchpad/webapp/mount/mod-db database.xml edit-groups.xsp
                        schema.sql sitemap.xmap 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 AttributeHelper.java
                        HsqlIdentityKeyAttributeHelper.java
                        IfxSerialKeyAttributeHelper.java
                        ManualKeyAttributeHelper.java OutputHelper.java
                        RequestAttributeHelper.java
                        RequestAttributeOutputHelper.java
                        RequestParameterHelper.java
  Added:       scratchpad/src/org/apache/cocoon/acting
                        ModularDatabaseAction.java
               scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess
                        AbstractAutoIncrementHelper.java
                        AbstractOutputHelper.java AutoIncrementHelper.java
  Removed:     scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess
                        AbstractKeyAttributeHelper.java AccessHelper.java
                        KeyAttributeHelper.java
  Log:
  Polished interfaces + some restructuring.
  Now open for review.
  
  Revision  Changes    Path
  1.5       +1 -1      xml-cocoon2/webapp/WEB-INF/db/cocoondb.properties
  
  Index: cocoondb.properties
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/webapp/WEB-INF/db/cocoondb.properties,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- cocoondb.properties	2001/11/19 16:06:33	1.4
  +++ cocoondb.properties	2001/11/23 10:50:35	1.5
  @@ -1,4 +1,4 @@
   #HSQL database
  -#Mon Nov 19 17:19:34 CET 2001
  +#Fri Nov 23 11:13:29 CET 2001
   version=1.6
   modified=no
  
  
  
  1.3       +3 -3      xml-cocoon2/webapp/WEB-INF/db/cocoondb.script
  
  Index: cocoondb.script
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/webapp/WEB-INF/db/cocoondb.script,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- cocoondb.script	2001/11/19 16:06:34	1.2
  +++ cocoondb.script	2001/11/23 10:50:35	1.3
  @@ -1,10 +1,10 @@
   CREATE TABLE DEPARTMENT_TABLE(ID INTEGER,NAME VARCHAR,UNIQUE(ID))
   CREATE TABLE EMPLOYEE_TABLE(ID INTEGER,DEPARTMENT_ID INTEGER,NAME VARCHAR,UNIQUE(ID))
  -CREATE TABLE USER(UID INTEGER IDENTITY PRIMARY KEY,NAME VARCHAR,SURNAME VARCHAR,UNAME VARCHAR,UNIQUE(UNAME))
  +CREATE TABLE USER(UID INTEGER IDENTITY PRIMARY KEY,NAME VARCHAR,FIRSTNAME VARCHAR,UNAME VARCHAR,UNIQUE(UNAME))
   CREATE TABLE GROUPS(GID INTEGER IDENTITY PRIMARY KEY,GNAME VARCHAR,UNIQUE(GNAME))
   CREATE TABLE USER_GROUPS(UID INTEGER,GID INTEGER,UNIQUE(UID,GID),FOREIGN KEY(UID)REFERENCES USER(UID),FOREIGN KEY(GID)REFERENCES GROUPS(GID))
  -GRANT ALL ON CLASS "java.lang.Math" TO PUBLIC
   GRANT ALL ON CLASS "org.hsqldb.Library" TO PUBLIC
  +GRANT ALL ON CLASS "java.lang.Math" TO PUBLIC
   CREATE USER SA PASSWORD "" ADMIN
   CREATE ALIAS DAYNAME FOR "org.hsqldb.Library.dayname"
   CREATE ALIAS SPACE FOR "org.hsqldb.Library.space"
  @@ -52,7 +52,7 @@
   CREATE ALIAS CURTIME FOR "org.hsqldb.Library.curtime"
   CREATE ALIAS DIFFERENCE FOR "org.hsqldb.Library.difference"
   CREATE ALIAS INSERT FOR "org.hsqldb.Library.insert"
  -CREATE ALIAS SUBSTR FOR "org.hsql.Library.substring"
  +CREATE ALIAS SUBSTR FOR "org.hsqldb.Library.substring"
   CREATE ALIAS DATABASE FOR "org.hsqldb.Library.database"
   CREATE ALIAS MINUTE FOR "org.hsqldb.Library.minute"
   CREATE ALIAS HOUR FOR "org.hsqldb.Library.hour"
  
  
  
  1.2       +38 -24    xml-cocoon2/scratchpad/webapp/mount/mod-db/database.xml
  
  Index: database.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/webapp/mount/mod-db/database.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- database.xml	2001/11/19 16:28:32	1.1
  +++ database.xml	2001/11/23 10:50:35	1.2
  @@ -9,35 +9,50 @@
           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 -->
  +   <table name="user" alias="user">
  +      <!-- @name is the table's name in the DB -->
  +      <!-- @alias is used instead (if present) for those weirdos that put -->
  +      <!-- complex queries into @name ;-) You obviously loose all but select -->
  +      <!-- functionality, although this is not enforced. -->
         <keys>
  -         <key name="uid" type="int">
  +         <key name="uid" type="int" autoincrement="true">
   	    <!-- @name is the column's name -->
   	    <!-- @type is the column's jdbc type -->
  -	    <mode name="auto"  type="insert"/>
  +            <!-- @autoincrement : column value is determined by special component -->
  +	    <mode name="auto"  type="autoincr"/>
   	    <!-- 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) -->
  +	    when inserting a new column into a autoincrement column
  +            (@type="autoincr"), 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: "autoincr" for insert
  +	    operations on autoincrement columns and "others" for all other
  +            (delete, update, select) operations on autoincrement columns and
  +            all operations on other columns. 
  +            -->
   	 </key>
         </keys>
         <values>
  -         <value name="name"    type="string"></value>
  -         <value name="surname" type="string"></value>
  -         <value name="uname"   type="string"></value>
  +         <value name="name"      type="string"></value>
  +         <value name="firstname" 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
  +	    <!-- Next we have two different modes: "request" 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"/>
  +            <mode name="attribute" parameter="org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:user.uid[0]" type="attrib"/>
  +	    <!--  note here, that the actual parameter has a row index
  +	    to it. We don't expect to insert more than one user plus
  +	    her groups at any time, so we append just "[0]". However,
  +	    if that would be the case, we could let this column be
  +	    part of a set and ask for "[*]" instead. Might be tricky
  +	    to find the associated groups, though, if we insert x
  +	    users plus y_1, y_2, ... , y_x groups.... -->
   	 </key>
   	 <key name="gid" type="int" set="master">
   	    <!-- now, this is tricky: when we need to insert multiple
  @@ -47,7 +62,8 @@
   	    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. 
  +	    not necessary to have the master be a key column. Note too, that an
  +            autoincrement column may not serve as a master here.
   
   	    This attribute may be specified on a column level or on a
   	    mode level to allow different behavious. Mode level @set
  @@ -61,12 +77,12 @@
   
      <table name="groups">
         <keys>
  -         <key name="gid" type="int">
  -	    <mode name="auto"  type="insert"/>
  +         <key name="gid" type="int" autoincrement="true">
  +	    <mode name="auto" type="autoincr"/>
   	 </key>
         </keys>
         <values>
  -         <value name="gname"   type="string"></value>
  +         <value name="gname" type="string"/>
         </values>   
      </table>
      
  @@ -88,21 +104,19 @@
   
      <table-set name="user+groups">
         <table name="user"/>
  -      <table name="user_groups" add-mode="attrib" others-mode="request"/>
  +      <table name="user_groups" others-mode="attrib"/>
         <!-- 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
  +      instruct the action to use a different type (here "attrib") which reads
  +      the result from the first insert operation. 
         -->
      </table-set>
   
      <table-set name="user_groups">
  -      <table name="user_groups" add-mode="request" others-mode="request"/>
  +      <table name="user_groups" others-mode="request"/>
      </table-set>
   
   </root>
  
  
  
  1.2       +210 -209  xml-cocoon2/scratchpad/webapp/mount/mod-db/edit-groups.xsp
  
  Index: edit-groups.xsp
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/webapp/mount/mod-db/edit-groups.xsp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- edit-groups.xsp	2001/11/19 16:28:32	1.1
  +++ edit-groups.xsp	2001/11/23 10:50:35	1.2
  @@ -5,229 +5,230 @@
             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>
  -
  +  >
  +  <!--
  +       -->
  +  <xsp:structure>
  +    <xsp:include>java.util.Enumeration</xsp:include>
  +  </xsp:structure>
  +  
  +  <page>
  +    <title>edit-groups</title>
  +    
   
  -  <esql:connection>
  -    <esql:pool>personnel</esql:pool>
  +    <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>
  +      <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 firstname="";
  +         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"/>;
  +            	   firstname=<esql:get-string column="firstname"/>;
  +                 </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>
  -              <table border="0">
  +              <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">firstname, name</td>
  +            <td>
  +              <input type="text" name="user.firstname">
  +                <xsp:attribute name="value"><xsp:expr>firstname</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>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>
  +                      <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>
  -                  <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">
  +            </form>
  +          </td>
  +          <td valign="Top" align="left">
  +            <form>
  +              <table border="1">
                   <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/>
  -
  +                      <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>
  +    </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>
  +    </p>
   
  +    <hr/>
   
  -
     </page>
  +
   </xsp:page>
  
  
  
  1.2       +1 -1      xml-cocoon2/scratchpad/webapp/mount/mod-db/schema.sql
  
  Index: schema.sql
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/webapp/mount/mod-db/schema.sql,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- schema.sql	2001/11/19 16:28:32	1.1
  +++ schema.sql	2001/11/23 10:50:35	1.2
  @@ -8,7 +8,7 @@
   create table user (
   	uid integer identity primary key,
   	name varchar(50),
  -	surname varchar(50),
  +	firstname varchar(50),
   	uname varchar(20),
   	unique (uname)
   );
  
  
  
  1.2       +52 -17    xml-cocoon2/scratchpad/webapp/mount/mod-db/sitemap.xmap
  
  Index: sitemap.xmap
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/webapp/mount/mod-db/sitemap.xmap,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- sitemap.xmap	2001/11/19 16:28:32	1.1
  +++ sitemap.xmap	2001/11/23 10:50:35	1.2
  @@ -5,6 +5,8 @@
     <!-- ========================= Components ============================== -->
   
     <map:components>
  +    <!-- most components are inherited from parent sitemap -->
  +    <!-- just make sure that defaults suit our needs -->
   
       <map:generators default="file"/>
   
  @@ -14,17 +16,19 @@
   
       <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:selectors default="browser"/>
   
  +    <map:matchers default="wildcard"/>
  +
       <map:actions>
  +       <!-- these action are in addition to the ones inherited -->
          <map:action name="mod-db-add" src="org.apache.cocoon.acting.ModularDatabaseAddAction">
      	 <descriptor>context://mount/mod-db/database.xml</descriptor>
  +   	 <throw-exception>false</throw-exception>
  +	 <!-- shall we throw an exception in addition to rolling back
  +	      the transaction when encountering an error during
  +	      database ops? 
  +	 -->
      	 <!-- <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"/>
  @@ -33,6 +37,14 @@
          </map:action>
          <map:action name="mod-db-del" src="org.apache.cocoon.acting.ModularDatabaseDeleteAction">
      	 <descriptor>context://mount/mod-db/database.xml</descriptor>
  +   	 <throw-exception>false</throw-exception>
  +	 <!--
  +	    It doesn't make sense to declare AutoIncrementHelpers for
  +	    this action, but until component handling is removed from
  +	    ModularDatabaseAction and done e.g. in sitemap,
  +	    ModularDatabaseAction will complain if such a helper is
  +	    missing. 
  +	 -->
      	 <!-- <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"/>
  @@ -41,6 +53,14 @@
          </map:action>
          <map:action name="mod-db-upd" src="org.apache.cocoon.acting.ModularDatabaseUpdateAction">
      	 <descriptor>context://mount/mod-db/database.xml</descriptor>
  +   	 <throw-exception>false</throw-exception>
  +	 <!--
  +	    It doesn't make sense to declare AutoIncrementHelpers for
  +	    this action, but until component handling is removed from
  +	    ModularDatabaseAction and done e.g. in sitemap,
  +	    ModularDatabaseAction will complain if such a helper is
  +	    missing. 
  +	 -->
      	 <!-- <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"/>
  @@ -68,8 +88,22 @@
   
      <!-- ========================== Modular DB ================================= -->
   
  +       <map:match pattern="">
  +          <map:redirect-to uri="user-list"/>
  +       </map:match>
  +
  +
          <map:match pattern="*">
   
  +          <!--
  +	     First, the logic to do all the database operations. Note,
  +	     that we do no parameter validation here, just see if some
  +	     parameters are present. For a real application, you'd
  +	     want to check their values as well. Note too, that in a
  +	     more complex setup you'd want to use an action set for
  +	     this rather than spell it out everywhere.
  +	  -->
  +
             <!-- ______________________________ inserts ______________________________ -->
   
             <!-- add new groups to a user's groups -->
  @@ -80,25 +114,28 @@
     	     </map:act>
     	  </map:act>
   
  -          <!-- add one new user -->
  +          <!-- add onw new group -->
     	  <map:act type="req-params">
  -  	     <map:parameter name="parameters" value="add-user user.name user.surname user.uname"/>
  +  	     <map:parameter name="parameters" value="add-group groups.gname"/>
     	     <map:act type="mod-db-add">
  -  	  	<map:parameter name="table-set" value="user"/>
  +  	  	<map:parameter name="table-set" value="groups"/>
     	     </map:act>
     	  </map:act>
        
  -          <!-- add onw new group -->
  +          <!-- add one new user -->
  +	  <!-- Actually, this one is not used anymore. The one below
  +	       works well when no groups are supplied.
  +	   -->
     	  <map:act type="req-params">
  -  	     <map:parameter name="parameters" value="add-group groups.gname"/>
  +  	     <map:parameter name="parameters" value="add-user user.name user.firstname user.uname"/>
     	     <map:act type="mod-db-add">
  -  	  	<map:parameter name="table-set" value="groups"/>
  +  	  	<map:parameter name="table-set" value="user"/>
     	     </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:parameter name="parameters" value="add-user-groups user.name user.firstname user.uname"/>
     	     <map:act type="mod-db-add">
     	  	<map:parameter name="table-set" value="user+groups"/>
     	     </map:act>
  @@ -108,7 +145,7 @@
   
             <!-- update one user -->
     	  <map:act type="req-params">
  -  	     <map:parameter name="parameters" value="upd-user user.uid user.name user.surname user.uname"/>
  +  	     <map:parameter name="parameters" value="upd-user user.uid user.name user.firstname user.uname"/>
     	     <map:act type="mod-db-upd">
     	  	<map:parameter name="table-set" value="user"/>
     	     </map:act>
  @@ -152,8 +189,6 @@
             <!-- ______________________________ 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>
  
  
  
  1.2       +241 -346  xml-cocoon2/scratchpad/webapp/mount/mod-db/user-list.xsp
  
  Index: user-list.xsp
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/webapp/mount/mod-db/user-list.xsp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- user-list.xsp	2001/11/19 16:28:32	1.1
  +++ user-list.xsp	2001/11/23 10:50:35	1.2
  @@ -5,354 +5,249 @@
             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"/>
  +       -->
  +  <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>Auto Increment Helpers determine the value of a key attribute
  +            column in a database if it's is of auto increment type. 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/>
  +
  +      <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, firstname, uname, uid</esql:query>
  +              <esql:results>
  +                <esql:row-results>
  +             	       
  +                  <sqltblrow>
  +                    <name>
  +                      <esql:get-string column="name"/>
  +                    </name>
  +                    <firstname>
  +                      <esql:get-string column="firstname"/>
  +                    </firstname>
  +                    <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>   
  -	      </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>
  +                  </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>
  +                <firstname>
  +                  <input type="text" name="user.firstname" size="20" maxsize="20">
  +                    <xsp:attribute name="value"><!-- <xsp-request:get-parameter default="" name="user.firstname"/> --></xsp:attribute>
  +                  </input>
  +                </firstname>
  +                <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>
  +      </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.2       +70 -951   xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAddAction.java
  
  Index: ModularDatabaseAddAction.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAddAction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModularDatabaseAddAction.java	2001/11/19 16:28:33	1.1
  +++ ModularDatabaseAddAction.java	2001/11/23 10:50:35	1.2
  @@ -7,64 +7,19 @@
    *****************************************************************************/
   package org.apache.cocoon.acting;
   
  -import java.io.IOException;
  -import java.io.InputStream;
  -import java.lang.Class;
  -import java.net.URL;
  +import java.util.Map;
   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.cocoon.util.HashMap;
   
  -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;
  +import org.apache.cocoon.acting.ModularDatabaseAccess.AutoIncrementHelper;
   
   /**
    * Adds record in a database. The action can update one or more tables,
  @@ -83,772 +38,101 @@
    * 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 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/23 10:50:35 $
    */
   public class ModularDatabaseAddAction 
  -    extends AbstractDatabaseAction 
  -    implements Disposable, ThreadSafe, Contextualizable
  +    extends ModularDatabaseAction 
   {
  -
  -    // ========================================================================
  -    // 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.
  +     * set all necessary ?s and execute the query
        */
  -    public void configure(
  -			  Configuration conf
  -			  ) 
  -	throws ConfigurationException 
  +    protected void processRow ( 
  +			       Request request,
  +			       Connection conn,
  +			       PreparedStatement statement,
  +			       Configuration table,
  +			       CacheHelper queryData,
  +			       Object[][] columnValues,
  +			       int rowIndex,
  +			       Map results
  +			       )
  +	throws SQLException,
  +	       ConfigurationException,
  +	       Exception 
       {
  -	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" : "" )
  -						 );
  +	int currentIndex = 1;
  +	for (int i = 0; i < queryData.columns.length; i++) {
  +	    Column col = queryData.columns[i];
  +	    if ( col.isAutoIncrement && col.isKey ) {
  +		currentIndex += setKeyAuto( table, col, currentIndex, rowIndex, 
  +					    conn, statement, request, results );
  +	    } else {
  +		this.setColumn( statement, currentIndex, request, col.columnConf, 
  +				getOutputName( table, col.columnConf, rowIndex ),
  +				columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ], 
  +				rowIndex);
  +		currentIndex++;
   	    }
  -	    
  -	} 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");
  +	statement.execute();
  +	// get resulting ids for autoincrement columns
  +	for (int i = 0; i < queryData.columns.length; i++) {
  +	    if ( queryData.columns[i].isAutoIncrement && queryData.columns[i].isKey ) {
  +		storeKeyValue( table, queryData.columns[i], rowIndex, 
  +			       conn, statement, request, results );
  +	    }
   	}
  -	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.
  +     * determine which mode to use as default mode
  +     * here: INSERT
  +     * highly specific to operation INSERT / UPDATE / DELETE / SELECT
        */
  -    protected DataSourceComponent getDataSource( Configuration conf, Parameters parameters )
  -	throws ComponentException
  +    protected String selectMode (
  +				  boolean isAutoIncrement,
  +				  HashMap modes
  +				  )
       {
  -	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() );
  -	}
  +	if ( isAutoIncrement )
  +	    return (String) modes.get( MODE_AUTOINCR );
  +	else 
  +	    return (String) modes.get( MODE_OTHERS );
       }
   
       /**
  -     * 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.
  +     * determine whether autoincrement columns should be honoured by
  +     * this operation. This is usually snsible only for INSERTs.
        */
  -    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;
  -    }
  +    protected boolean honourAutoIncrement() { return true; }
   
   
  -    // ========================================================================
  -    // main method
  -    // ========================================================================
  -
  -
       /**
  -     * Add a record to the database.  This action assumes that
  -     * the file referenced by the "descriptor" parameter conforms
  -     * to the AbstractDatabaseAction specifications.
  +     * Fetch all values for all columns that are needed to do the
  +     * database operation.
        */
  -    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 ) );
  +    Object[][] getColumnValues( 
  +			     Configuration tableConf,
  +			     CacheHelper queryData,
  +			     Request request
  +			     )
  +	throws ConfigurationException,
  +	       ComponentException
  +    {
  +	Object[][] columnValues = new Object[ queryData.columns.length ][];
  +	for ( int i = 0; i < queryData.columns.length; i++ ){
  +	    columnValues[i] = this.getColumnValue( tableConf, queryData.columns[i], request );
   	}
  -	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+"]");
  +	return columnValues;
       }
   
   
  -
  -    /**
  -     * 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
        */
  @@ -875,7 +159,7 @@
   
                   StringBuffer queryBuffer = new StringBuffer("INSERT INTO ");
                   StringBuffer valueBuffer = new StringBuffer(") VALUES (");
  -		KeyAttributeHelper dah;
  +		AutoIncrementHelper dah;
   
                   queryBuffer.append(table.getAttribute("name"));
                   queryBuffer.append(" (");
  @@ -886,8 +170,8 @@
   			queryBuffer.append( ", " );
   			valueBuffer.append( ", " );
   		    }
  -		    if ( queryData.columns[i].isKey ) {
  -			dah = (KeyAttributeHelper) modeMapping.select( queryData.columns[i].mode );
  +		    if ( queryData.columns[i].isKey && queryData.columns[i].isAutoIncrement ) {
  +			dah = (AutoIncrementHelper) modeMapping.select( queryData.columns[i].mode );
   			if ( dah.includeInQuery() ) {
   			    actualColumns++;
   			    queryBuffer.append( queryData.columns[i].columnConf.getAttribute( "name" ) );
  @@ -917,170 +201,5 @@
           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.2       +42 -14    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseDeleteAction.java
  
  Index: ModularDatabaseDeleteAction.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseDeleteAction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModularDatabaseDeleteAction.java	2001/11/19 16:28:33	1.1
  +++ ModularDatabaseDeleteAction.java	2001/11/23 10:50:35	1.2
  @@ -28,10 +28,10 @@
    * @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 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/23 10:50:35 $
    */
   public class ModularDatabaseDeleteAction 
  -    extends ModularDatabaseAddAction
  +    extends ModularDatabaseAction
   {
   
       /**
  @@ -41,7 +41,7 @@
        */
   
       protected String selectMode (
  -				  boolean isKeyColumns,
  +				  boolean isAutoIncrement,
   				  HashMap modes
   				  )
       {
  @@ -49,7 +49,38 @@
       }
   
   
  +    /**
  +     * determine whether autoincrement columns should be honoured by
  +     * this operation. This is usually snsible only for INSERTs.
  +     */
  +    protected boolean honourAutoIncrement() { return false; }
  +
  +
  +    /**
  +     * Fetch all values for all key columns that are needed to do the
  +     * database operation.
  +     */
  +    Object[][] getColumnValues( 
  +			     Configuration tableConf,
  +			     CacheHelper queryData,
  +			     Request request
  +			     )
  +	throws ConfigurationException,
  +	       ComponentException
  +    {
  +	Object[][] columnValues = new Object[ queryData.columns.length ][];
  +	for ( int i = 0; i < queryData.columns.length; i++ ){
  +	    if ( queryData.columns[i].isKey ) {
  +		columnValues[i] = this.getColumnValue( tableConf, queryData.columns[i], request );
  +	    } else {
  +		// columnValues[i] = new Object[1]; // this should not be needed
  +	    }
  +	}
  +	return columnValues;
  +    }
  +
   
  +
       /**
        * Get the String representation of the PreparedStatement.  This is
        * mapped to the Configuration object itself, so if it doesn't exist,
  @@ -106,8 +137,8 @@
   			       PreparedStatement statement,
   			       Configuration table,
   			       CacheHelper queryData,
  +			       Object[][] columnValues,
   			       int rowIndex,
  -			       Object idx,
   			       Map results
   			       )
   	throws SQLException,
  @@ -119,19 +150,16 @@
   
   	// 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 );
  +	    Column col = queryData.columns[i];
  +	    if ( col.isKey ) {
  +		this.setColumn( statement, currentIndex, request, col.columnConf, 
  +				getOutputName( table, col.columnConf, rowIndex ),
  +				columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ], 
  +				rowIndex);
  +		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 );
  -	    }
  -	}
       }
   
   }
  
  
  
  1.2       +41 -12    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseSelectAction.java
  
  Index: ModularDatabaseSelectAction.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseSelectAction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModularDatabaseSelectAction.java	2001/11/19 16:28:33	1.1
  +++ ModularDatabaseSelectAction.java	2001/11/23 10:50:35	1.2
  @@ -36,10 +36,10 @@
    * @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 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/23 10:50:35 $
    */
   public class ModularDatabaseSelectAction 
  -    extends ModularDatabaseAddAction
  +    extends ModularDatabaseAction
   {
   
       /**
  @@ -49,7 +49,7 @@
        */
   
       protected String selectMode (
  -				  boolean isKeyColumns,
  +				  boolean isAutoIncrement,
   				  HashMap modes
   				  )
       {
  @@ -57,7 +57,13 @@
       }
   
   
  +    /**
  +     * determine whether autoincrement columns should be honoured by
  +     * this operation. This is usually snsible only for INSERTs.
  +     */
  +    protected boolean honourAutoIncrement() { return false; }
   
  +
       /**
        * Get the String representation of the PreparedStatement.  This is
        * mapped to the Configuration object itself, so if it doesn't exist,
  @@ -122,6 +128,30 @@
   
   
       /**
  +     * Fetch all values for all key columns that are needed to do the
  +     * database operation.
  +     */
  +    Object[][] getColumnValues( 
  +			     Configuration tableConf,
  +			     CacheHelper queryData,
  +			     Request request
  +			     )
  +	throws ConfigurationException,
  +	       ComponentException
  +    {
  +	Object[][] columnValues = new Object[ queryData.columns.length ][];
  +	for ( int i = 0; i < queryData.columns.length; i++ ){
  +	    if ( queryData.columns[i].isKey ) {
  +		columnValues[i] = this.getColumnValue( tableConf, queryData.columns[i], request );
  +	    } else {
  +		// columnValues[i] = new Object[1]; // this should not be needed
  +	    }
  +	}
  +	return columnValues;
  +    }
  +
  +
  +    /**
        * set all necessary ?s and execute the query
        */
       protected void processRow ( 
  @@ -130,8 +160,8 @@
   			       PreparedStatement statement,
   			       Configuration table,
   			       CacheHelper queryData,
  +			       Object[][] columnValues,
   			       int rowIndex,
  -			       Object idx,
   			       Map results
   			       )
   	throws SQLException,
  @@ -143,17 +173,16 @@
   
   	// 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 );
  +	    Column col = queryData.columns[i];
  +	    if ( col.isKey ) {
  +		this.setColumn( statement, currentIndex, request, col.columnConf, 
  +				getOutputName( table, col.columnConf, rowIndex ),
  +				columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ], 
  +				rowIndex);
  +		currentIndex++;
   	    }
   	}
   	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;
  
  
  
  1.2       +43 -17    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseUpdateAction.java
  
  Index: ModularDatabaseUpdateAction.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseUpdateAction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModularDatabaseUpdateAction.java	2001/11/19 16:28:33	1.1
  +++ ModularDatabaseUpdateAction.java	2001/11/23 10:50:35	1.2
  @@ -28,10 +28,10 @@
    * @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 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/23 10:50:35 $
    */
   public class ModularDatabaseUpdateAction 
  -    extends ModularDatabaseAddAction
  +    extends ModularDatabaseAction
   {
   
       /**
  @@ -41,7 +41,7 @@
        */
   
       protected String selectMode (
  -				  boolean isKeyColumns,
  +				  boolean isAutoIncrement,
   				  HashMap modes
   				  )
       {
  @@ -49,7 +49,33 @@
       }
   
   
  +    /**
  +     * determine whether autoincrement columns should be honoured by
  +     * this operation. This is usually snsible only for INSERTs.
  +     */
  +    protected boolean honourAutoIncrement() { return false; }
  +
  +
  +    /**
  +     * Fetch all values for all columns that are needed to do the
  +     * database operation.
  +     */
  +    Object[][] getColumnValues( 
  +			     Configuration tableConf,
  +			     CacheHelper queryData,
  +			     Request request
  +			     )
  +	throws ConfigurationException,
  +	       ComponentException
  +    {
  +	Object[][] columnValues = new Object[ queryData.columns.length ][];
  +	for ( int i = 0; i < queryData.columns.length; i++ ){
  +	    columnValues[i] = this.getColumnValue( tableConf, queryData.columns[i], request );
  +	}
  +	return columnValues;
  +    }
   
  +
       /**
        * Get the String representation of the PreparedStatement.  This is
        * mapped to the Configuration object itself, so if it doesn't exist,
  @@ -125,8 +151,8 @@
   			       PreparedStatement statement,
   			       Configuration table,
   			       CacheHelper queryData,
  +			       Object[][] columnValues,
   			       int rowIndex,
  -			       Object idx,
   			       Map results
   			       )
   	throws SQLException,
  @@ -138,26 +164,26 @@
   
   	// 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 );
  +	    Column col = queryData.columns[i];
  +	    if ( !col.isKey ) {
  +		this.setColumn( statement, currentIndex, request, col.columnConf, 
  +				getOutputName( table, col.columnConf, rowIndex ),
  +				columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ], 
  +				rowIndex);
   		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 );
  +	    Column col = queryData.columns[i];
  +	    if ( col.isKey ) {
  +		this.setColumn( statement, currentIndex, request, col.columnConf, 
  +				getOutputName( table, col.columnConf, rowIndex ),
  +				columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ], 
  +				rowIndex);
  +		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 );
  -	    }
  -	}
       }
   
   }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAction.java
  
  Index: ModularDatabaseAction.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.AutoIncrementHelper;
  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.
   *
   * Configuration options:
   * descriptor       file containing database description
   * throw-exception  throw an exception when an error occurs (default: false)
   *
   * 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/23 10:50:35 $
   */
  public abstract class ModularDatabaseAction 
      extends AbstractDatabaseAction 
      implements Disposable, ThreadSafe, Contextualizable
  {
  
      // ========================================================================
      // inner helper classes
      // ========================================================================
  
      protected class Column {
  	boolean isKey = false;
  	boolean isSet = false;
  	boolean isAutoIncrement = 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;
  	}
      }
  
      // ========================================================================
      // constants
      // ========================================================================
  
      private static final String LOCATION = "org.apache.cocoon.acting.ModularDatabaseAddAction";
      private static final int BYTE_ARRAY_SIZE = 1024;
  
      static final Integer MODE_AUTOINCR = 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 normal columns");
  		}
  		
  		if ( !defaultModeNames.containsKey( MODE_AUTOINCR ) && helper instanceof AutoIncrementHelper ) { 
  		    defaultModeNames.put( MODE_AUTOINCR, modeName );
  		    getLogger().debug(modeName + " default mode for autoincrement columns");
  		}
  		modeMapping.release(helper);
  	    }
  	
  	    if ( !defaultModeNames.containsKey( MODE_OTHERS ) || 
  		 !defaultModeNames.containsKey( MODE_AUTOINCR ) || 
  		 !defaultModeNames.containsKey( MODE_OUTPUT ) 
  		 ) {
  		throw new ConfigurationException("Not all default modes are configured:"
  						 + ( defaultModeNames.containsKey( MODE_AUTOINCR ) ? " 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
      // ========================================================================
  
      /**
       * 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( null, 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( null, 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);
  
  	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_AUTOINCR, set[i].getAttribute( "autoincr-mode", "autoincr" ) );
  		    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_AUTOINCR, "autoincr" );
  		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( null, 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( null, 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
  	    
  	    String throwException = (String) this.settings.get( "throw-exception", param.getParameter( "throw-exception", null ) );
  	    if ( throwException != null && 
  		 ( throwException.equalsIgnoreCase( "true" ) || throwException.equalsIgnoreCase( "yes" ) ) ) {
  		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 {
  	  CacheHelper queryData = this.getQuery( table, modeTypes, defaultModeNames );
  	  getLogger().debug("query: "+queryData.queryString);
  	  statement = conn.prepareStatement(queryData.queryString);
  
  	  Object[][] columnValues = this.getColumnValues( table, queryData, request );
  
  	  int setLength = 1;
  	  if ( queryData.isSet ) {
  	      if ( columnValues[ queryData.setMaster ] != null ) {
  		  setLength = columnValues[ queryData.setMaster ].length;
  	      } else {
  		  setLength = 0;
  	      }
  	  }
  
  	  for ( int rowIndex = 0; rowIndex < setLength; rowIndex++ ) {
  	      getLogger().debug( "====> row no. " + rowIndex );
  	      processRow( request, conn, statement, table, queryData, columnValues, rowIndex, results );
  	  }
  
        } finally {
  	  try {
  	      if (statement != null) {
  		  statement.close();
  	      }
  	  } catch (SQLException e) {}
        }
      }
  
      /**
       * 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.
       * 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");
  	    if ( modeType.equals(type) || modeType.equals(modeAll)) {
  		getLogger().debug("requested mode was \""+type+"\" returning \""+modeType+"\"");
  		modeConfig = modes[i];
  		break;
  	    };
  	}
  
  	return modeConfig;
      }
  
  
      /**
       * compose name for output a long the lines of "table.column"
       */
      String getOutputName (
  			  Configuration tableConf,
  			  Configuration columnConf
  			  )
  	throws ConfigurationException
      {
  	return getOutputName( tableConf, columnConf, -1 );
      }
  
  
      /**
       * 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("alias", tableConf.getAttribute("name") ) 
  		 + "." + columnConf.getAttribute("name") 
  		 + ( rowIndex == -1 ? "" : "[" + rowIndex + "]" ) );
      }
  
  
      /*
       * Read all values for a column from an AttributeHelper
       *
       * If the given column is an autoincrement column, an empty array
       * is returned, otherwise if it is part of a set, all available
       * values are fetched, or only the first one if it is not part of
       * a set.
       *
       */
      Object[] getColumnValue( 
  			    Configuration tableConf,
  			    Column column,
  			    Request request
  			    )
  	throws ConfigurationException,
  	       ComponentException
      {
  	if ( column.isAutoIncrement ) {
  	    return new Object[1];
  	} else {
  	    Object[] values;
  	    AttributeHelper dph = (AttributeHelper) modeMapping.select( column.mode );
  	    String cname = getOutputName( tableConf, column.columnConf );
  	    if ( column.isSet ){
  		getLogger().debug( "Trying to set column " + cname +" using getAttributeValues method");
  		values = dph.getAttributeValues( cname, column.modeConf, request );
  	    } else {
  		getLogger().debug( "Trying to set column " + cname +" using getAttribute method");
  		values = new Object[1];
  		values[0] = dph.getAttribute( cname, column.modeConf, request );
  	    }
  	    modeMapping.release( dph );
  	    if ( values != null ) {
  		for ( int i = 0; i < values.length; i++ ) {
  		    getLogger().debug( "Setting column " + cname + " [" + i + "] " + values[i] );
  		}
  	    }
  	    return values;
  	}
      }
  
  
      /**
       * 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);
  
          for ( int i = offset; i < conf.length + offset; i++ ) {
  	    getLogger().debug("i="+i);
  	    set.columns[i].columnConf =  conf[ i - offset ];
  	    set.columns[i].isSet = false;
  	    set.columns[i].isKey = isKey;
  	    set.columns[i].isAutoIncrement = false;
  	    if ( isKey & this.honourAutoIncrement() ) {
  		String autoIncrement = set.columns[i].columnConf.getAttribute("autoincrement","false");
  		if ( autoIncrement.equalsIgnoreCase("yes") || autoIncrement.equalsIgnoreCase("true") ) {
  		    set.columns[i].isAutoIncrement = true;
  		}
  	    }
  	    set.columns[i].modeConf = getMode( set.columns[i].columnConf, 
  					       selectMode( set.columns[i].isAutoIncrement, modeTypes ) );
  	    set.columns[i].mode = ( set.columns[i].modeConf != null ? 
  				    set.columns[i].modeConf.getAttribute( "name", selectMode( isKey, defaultModeNames ) ) :  selectMode( isKey, defaultModeNames ) );
  	    // 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;
  		}
  	    } 
  	}
      }
  
  
  
      /**
       * Put key values into request attributes.
       */
      void storeKeyValue( 
  		       Configuration tableConf, 
  		       Column key,
  		       int rowIndex,
  		       Connection conn, 
  		       Statement statement, 
  		       Request request, 
  		       Map results
  		       ) 
  	throws SQLException, 
  	       ConfigurationException, 
  	       ComponentException 
      {
  	AutoIncrementHelper dah = (AutoIncrementHelper) 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 for " + keyname + "as " + value );
  	    setRequestAttribute( request, keyname, value );
  	    results.put( keyname, String.valueOf( value ) );
  	}
  	modeMapping.release(dah);
      }
  
  
      /**
       * Sets the key value on the prepared statement for an autoincrement type.
       *
       * @param table the table's configuration object
       * @param column the key's configuration object
       * @param currentIndex the position of the key column
       * @param rowIndex the position in the current row set
       * @param conn the database connection
       * @param statement the insert statement
       * @param request the request object
       * @param result sitemap result object
       * @return the number of columns by which to increment the currentIndex
       */
      int setKeyAuto (
  		    Configuration table, 
  		    Column column, 
  		    int currentIndex, 
  		    int rowIndex,
  		    Connection conn, 
  		    PreparedStatement statement, 
  		    Request request, 
  		    Map results
  		    ) 
  	throws ConfigurationException, 
  	       SQLException, 
  	       ComponentException, 
  	       Exception 
      {
  
        int columnCount = 0;
  
        AutoIncrementHelper dah = (AutoIncrementHelper) modeMapping.select( column.mode );
        if ( dah.includeInQuery() ) {
  	  if ( dah.includeAsValue() ) {
  	      Object value = dah.getPreValue( table, column.columnConf, column.modeConf, conn, request );
  	      String keyname = this.getOutputName( table, column.columnConf, rowIndex );
  	      getLogger().debug( "Setting key " + keyname + " to " + value );
  	      statement.setObject( currentIndex, value );
  	      setRequestAttribute( request, keyname, value );
  	      results.put( keyname, String.valueOf( value ) );
  	      columnCount = 1;
  	  }
        } else {
  	  getLogger().debug( "Automatically setting key" );
        }  
        modeMapping.release( dah );
        return columnCount;
      }
  
      // ========================================================================
      // abstract methods
      // ========================================================================
  
  
      /**
       * set all necessary ?s and execute the query
       *
       * This method is intended to be overridden by classes that
       * implement other operations e.g. delete
       */
      protected abstract void processRow( 
  				       Request request,
  				       Connection conn,
  				       PreparedStatement statement,
  				       Configuration table,
  				       CacheHelper queryData,
  				       Object[][] columnValues,
  				       int rowIndex,
  				       Map results
  				       )
  	throws SQLException,
  	       ConfigurationException,
  	       Exception;
      
      /**
       * determine which mode to use as default mode
       *
       * This method is intended to be overridden by classes that
       * implement other operations e.g. delete
       */
      protected abstract String selectMode(
  					 boolean isAutoIncrement,
  					 HashMap modes
  					 );
  
  
      /**
       * determine whether autoincrement columns should be honoured by
       * this operation. This is usually snsible only for INSERTs.
       *
       * This method is intended to be overridden by classes that
       * implement other operations e.g. delete
       */
      protected abstract boolean honourAutoIncrement();
  
  
      /**
       * Fetch all values for all columns that are needed to do the
       * database operation.
       *
       * This method is intended to be overridden by classes that
       * implement other operations e.g. delete
       */
      abstract Object[][] getColumnValues( 
  					Configuration tableConf,
  					CacheHelper queryData,
  					Request request
  					)
  	throws ConfigurationException,
  	       ComponentException;
  
      /**
       * 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.
       *
       * This method is intended to be overridden by classes that
       * implement other operations e.g. delete
       *
       * @param table the table's configuration object
       * @return the insert query as a string
       */
      protected abstract CacheHelper getQuery(
  					    Configuration table,
  					    HashMap modeTypes,
  					    HashMap defaultModeNames
  					    ) 
  	throws ConfigurationException, 
  	       ComponentException;
  }
  
  
  
  1.2       +3 -3      xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AbstractAttributeHelper.java
  
  Index: AbstractAttributeHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AbstractAttributeHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- AbstractAttributeHelper.java	2001/11/19 16:28:33	1.1
  +++ AbstractAttributeHelper.java	2001/11/23 10:50:36	1.2
  @@ -13,15 +13,15 @@
   import org.apache.avalon.framework.configuration.ConfigurationException;
   import org.apache.avalon.framework.logger.AbstractLoggable;
   
  -import java.util.HashMap;
  +import org.apache.cocoon.util.HashMap;
   
   /**
    * AbstractDatabaseHelper gives you the infrastructure for easily
  - * deploying more database access helpers.  In order to get at the
  + * deploying more Attributehelpers.  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 $ */
  + * @version CVS $Revision: 1.2 $ $Date: 2001/11/23 10:50:36 $ */
   public abstract class AbstractAttributeHelper extends AbstractLoggable
       implements AttributeHelper, Configurable, Disposable { 
   
  
  
  
  1.2       +53 -16    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AttributeHelper.java
  
  Index: AttributeHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AttributeHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- AttributeHelper.java	2001/11/19 16:28:33	1.1
  +++ AttributeHelper.java	2001/11/23 10:50:36	1.2
  @@ -7,38 +7,75 @@
    *****************************************************************************/
   package org.apache.cocoon.acting.ModularDatabaseAccess;
   
  +import java.util.SortedSet;
  +import java.util.Enumeration;
  +
   import org.apache.cocoon.environment.Request;
  +import org.apache.avalon.framework.component.Component;
   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
  + * access to individual attributes 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 $
  + * @version CVS $Id: AttributeHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    * */
  -public interface AttributeHelper extends AccessHelper {
  +public interface AttributeHelper 
  +    extends Component 
  +{
       String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.AttributeHelper";
  -    
  +
  +    /** 
  +     * Standard access to an attribute's value. If more than one value
  +     * exists, the first is returned. If the value does not exist,
  +     * null is returned. To get all values, use {@link
  +     * getAttributeValues} or {@link getAttributeSet} and {@link
  +     * getValue} to get them one by one.
  +     * @param name a String that specifies what the caller thinks
  +     * would identify an attribute. This is mainly a fallback if no
  +     * modeConf is present.
  +     * @param modeConf column's mode configuration from resource
  +     * description. This argument is optional.
  +     * @param request the request object
  +     */
  +    public Object getAttribute( String name, 
  +				Configuration modeConf, 
  +				Request request
  +				)
  +	throws ConfigurationException;
  +
  +    /**
  +     * Returns an Enumeration of String objects containing the names
  +     * of the attributes available. If no attributes are available,
  +     * the method returns an empty Enumeration.
  +     * @param modeConf column's mode configuration from resource
  +     * description. This argument is optional.
  +     * @param request the request object
  +     */
  +    public Enumeration getAttributeNames( Configuration modeConf, 
  +					  Request request
  +					  )
  +	throws ConfigurationException;
  +
       /**
  -     * Return an object that is to be taken for insertion.
  +     * Returns an array of String objects containing all of the values
  +     * the given attribute has, or null if the attribute does not
  +     * exist. As an alternative, {@link getAttributeSet} together with
  +     * {@link getValue} can be used to get the values one by one.
        * @param name a String that specifies what the caller thinks
  -     * would identify a set of parameters. This is mainly a fallback
  +     * would identify an attributes. This is mainly a fallback
        * if no modeConf is present.
        * @param modeConf column's mode configuration from resource
  -     * description.  
  +     * description. This argument is optional.
        * @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
  -			    )
  +     */
  +    public Object[] getAttributeValues( String name, 
  +					Configuration modeConf, 
  +					Request request
  +					)
   	throws ConfigurationException;
   
   }
  
  
  
  1.2       +16 -19    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/HsqlIdentityKeyAttributeHelper.java
  
  Index: HsqlIdentityKeyAttributeHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/HsqlIdentityKeyAttributeHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- HsqlIdentityKeyAttributeHelper.java	2001/11/19 16:28:33	1.1
  +++ HsqlIdentityKeyAttributeHelper.java	2001/11/23 10:50:36	1.2
  @@ -7,7 +7,7 @@
    *****************************************************************************/
   package org.apache.cocoon.acting.ModularDatabaseAccess;
   
  -import java.lang.Long;
  +import java.lang.Integer;
   import java.util.SortedSet;
   import java.sql.CallableStatement;
   import java.sql.PreparedStatement;
  @@ -28,10 +28,10 @@
    * {@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 $
  + * @version CVS $Id: HsqlIdentityKeyAttributeHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    */
   public class HsqlIdentityKeyAttributeHelper 
  -    implements KeyAttributeHelper, ThreadSafe
  +    implements AutoIncrementHelper, ThreadSafe
   {
       
       public Object getPostValue( Configuration tableConf, 
  @@ -40,9 +40,11 @@
   				Connection conn, 
   				Statement stmt, 
   				Request request
  -				) throws SQLException, ConfigurationException 
  +				) 
  +	throws SQLException, 
  +	       ConfigurationException 
       {
  -	Long id = null;
  +	Integer id = null;
   	/*
   	CallableStatement callStmt = conn.prepareCall("? = {CALL IDENTITY()}");
   	callStmt.registerOutParameter(1, Types.INTEGER);
  @@ -53,7 +55,7 @@
   	PreparedStatement pstmt = conn.prepareStatement("CALL IDENTITY()");
   	ResultSet resultSet = pstmt.executeQuery();
   	while ( resultSet.next() ) {
  -	    id = new Long(resultSet.getInt(1));
  +	    id = new Integer(resultSet.getInt(1));
   	}
   	resultSet.close();
   
  @@ -71,25 +73,20 @@
   			       Configuration columnConf, 
   			       Configuration modeConf, 
   			       Connection conn, 
  -			       Request request,
  -			       Object idx
  -			       ) throws SQLException, ConfigurationException {
  +			       Request request
  +			       ) 
  +	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
  +			       ) 
  +	throws ConfigurationException 
       {
   	return null;
  -    }
  +    };
   }
  
  
  
  1.2       +14 -17    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/IfxSerialKeyAttributeHelper.java
  
  Index: IfxSerialKeyAttributeHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/IfxSerialKeyAttributeHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- IfxSerialKeyAttributeHelper.java	2001/11/19 16:28:33	1.1
  +++ IfxSerialKeyAttributeHelper.java	2001/11/23 10:50:36	1.2
  @@ -7,7 +7,7 @@
    *****************************************************************************/
   package org.apache.cocoon.acting.ModularDatabaseAccess;
   
  -import java.lang.Long;
  +import java.lang.Integer;
   import java.util.SortedSet;
   import java.sql.Connection;
   import java.sql.Statement;
  @@ -25,10 +25,10 @@
    * (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 $
  + * @version CVS $Id: IfxSerialKeyAttributeHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    */
   public class IfxSerialKeyAttributeHelper 
  -    implements KeyAttributeHelper, ThreadSafe
  +    implements AutoIncrementHelper, ThreadSafe
   {
       
       public Object getPostValue( Configuration tableConf, 
  @@ -37,9 +37,11 @@
   				Connection conn, 
   				Statement stmt, 
   				Request request
  -				) throws SQLException, ConfigurationException 
  +				) 
  +	throws SQLException, 
  +	       ConfigurationException 
       {
  -	return new Long(((com.informix.jdbc.IfxStatement) stmt).getSerial());
  +	return new Integer(((com.informix.jdbc.IfxStatement) stmt).getSerial());
       };
       
       public boolean includeInQuery() {
  @@ -53,25 +55,20 @@
   			       Configuration columnConf, 
   			       Configuration modeConf, 
   			       Connection conn, 
  -			       Request request,
  -			       Object idx
  -			       ) throws SQLException, ConfigurationException {
  +			       Request request
  +			       ) 
  +	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.2       +27 -37    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/ManualKeyAttributeHelper.java
  
  Index: ManualKeyAttributeHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/ManualKeyAttributeHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ManualKeyAttributeHelper.java	2001/11/19 16:28:33	1.1
  +++ ManualKeyAttributeHelper.java	2001/11/23 10:50:36	1.2
  @@ -16,7 +16,7 @@
   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.lang.Integer;
   import java.util.Map;
   import java.util.HashMap;
   import java.util.SortedSet;
  @@ -34,42 +34,43 @@
    * 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 $
  + * @version CVS $Id: ManualKeyAttributeHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    */
   public class ManualKeyAttributeHelper 
  -    extends AbstractKeyAttributeHelper 
  +    extends AbstractAutoIncrementHelper 
       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 
  +    public Object getPostValue( Configuration tableConf, 
  +				Configuration columnConf,
  +				Configuration modenConf,
  +				Connection conn, 
  +				Statement stmt, 
  +				Request request
  +				) 
  +	throws SQLException, 
  +	       ConfigurationException 
       {
   	return null;
       };
       
  -    public boolean includeInQuery ( ) {
  +    public boolean includeInQuery( ) {
   	return true;
       };
       
  -    public boolean includeAsValue ( ) {
  +    public boolean includeAsValue( ) {
   	return true;
       };
   
  -    public Object getPreValue ( Configuration tableConf, 
  -				Configuration columnConf,
  -				Configuration modeConf,
  -				Connection conn, 
  -				Request request,
  -				Object idx
  -				) 
  -	throws SQLException, ConfigurationException 
  +    public Object getPreValue( Configuration tableConf, 
  +			       Configuration columnConf,
  +			       Configuration modeConf,
  +			       Connection conn, 
  +			       Request request
  +			       ) 
  +	throws SQLException, 
  +	       ConfigurationException 
       {
           /** Set the key value using SELECT MAX(keyname)+1 **/
   	String tableName = tableConf.getAttribute("name","");
  @@ -81,28 +82,17 @@
           set.close();
           select_statement.close();
   	getLogger().debug("autoincrementValue " + (maxid+1));
  -        return new Long(maxid + 1);
  +        return new Integer(maxid + 1);
       };
   
  -    public String getSubquery ( Configuration tableConf,
  -				Configuration columnConf,
  -				Configuration modeConf 
  -				) 
  +    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
  
  
  
  1.2       +14 -5     xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/OutputHelper.java
  
  Index: OutputHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/OutputHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- OutputHelper.java	2001/11/19 16:28:33	1.1
  +++ OutputHelper.java	2001/11/23 10:50:36	1.2
  @@ -9,6 +9,7 @@
   
   import org.apache.cocoon.environment.Request;
   import org.apache.avalon.framework.component.Component;
  +import org.apache.avalon.framework.configuration.Configuration;
   
   /**
    * Communicate results to other components. This could be done via
  @@ -18,7 +19,7 @@
    * 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 $
  + * @version CVS $Id: OutputHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    * */
   public interface OutputHelper extends Component {
       String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper";
  @@ -26,13 +27,16 @@
       
       /**
        * communicate an attribute value to further processing logic. 
  +     * @param modeConf column's mode configuration from resource
  +     * description. This argument is optional.
        * @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,
  +    public void setAttribute( Configuration modeConf,
  +			      Request request,
   			      String name,
   			      Object value
   			      );
  @@ -41,13 +45,18 @@
       /**
        * retrieve an attribute from other processing steps. <em>Do we
        * really need this thingy?</em>
  +     * @param modeConf column's mode configuration from resource
  +     * description. This argument is optional.
        * @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.
  +     *
  +     * Really necessary?
        * */
  -    public Object getAttribute( Request request,
  +    public Object getAttribute( Configuration modeConf,
  +				Request request,
   				String name );
   
       /**
  @@ -59,13 +68,13 @@
        * your data straight to some beans or other entities could result
        * in data corruption!</em>
        * */
  -    public void rollback( Request request, Exception e );
  +    public void rollback( Configuration modeConf, Request request, Exception e );
   
   
       /**
        * Signal that the database transaction completed
        * successfully. See notes on @link{rollback}.
        * */
  -    public void commit( Request request );
  +    public void commit( Configuration modeConf, Request request );
   
   }
  
  
  
  1.2       +65 -102   xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestAttributeHelper.java
  
  Index: RequestAttributeHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestAttributeHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RequestAttributeHelper.java	2001/11/19 16:28:33	1.1
  +++ RequestAttributeHelper.java	2001/11/23 10:50:36	1.2
  @@ -8,11 +8,8 @@
   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 java.util.List;
  +import java.util.LinkedList;
   import org.apache.cocoon.environment.Request;
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  @@ -23,124 +20,90 @@
    * 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 $
  + * @version CVS $Id: RequestAttributeHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    */
   public class RequestAttributeHelper 
       extends AbstractAttributeHelper 
  -    implements KeyAttributeHelper, ThreadSafe
  +    implements 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
  -			    )
  +    public Object getAttribute( String name, 
  +				Configuration modeConf, 
  +				Request request
  +				)
   	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 );
   	}
  +	return request.getAttribute( pname );
       }
   
  -    
  -    // KeyAttributeHelper methods
  -    
   
  -    public Object getPostValue( Configuration tableConf, 
  -				Configuration columnConf, 
  -				Configuration modeConf, 
  -				Connection conn, 
  -				Statement stmt, 
  -				Request request
  -				) 
  -	throws SQLException, ConfigurationException
  +    public Enumeration getAttributeNames( Configuration modeConf, 
  +					  Request request
  +					  )
  +	throws ConfigurationException
       {
  -	return null;
  +	return request.getAttributeNames();
       }
   
  -    public boolean includeInQuery() 
  +    public Object[] getAttributeValues( String name, 
  +					Configuration modeConf, 
  +					Request request
  +					)
  +	throws ConfigurationException
       {
  -	return true;
  -    }
  +	String wildcard = name;
  +	if ( modeConf != null ) {
  +	    wildcard = modeConf.getAttribute( "parameter", wildcard );
  +	}
  +	int wildcardIndex = wildcard.indexOf( "*" );
  +	if ( wildcardIndex != -1 ) {
  +	    // "*" contained in attribute name => combine all
  +	    // attributes' values that match prefix, suffix
  +
  +	    // split the attribute's name so that the "*" could be
  +	    // determined by looking at the attributes' names that
  +	    // start with the prefix and end with the suffix
  +	    //
  +	    String prefix = wildcard.substring( 0, wildcardIndex );
  +	    String suffix;
  +	    if ( wildcard.length() >= wildcardIndex + 1 ) {
  +		suffix = wildcard.substring( wildcardIndex + 1 );
  +	    } else {
  +		suffix = "";
  +	    }
  +	    List values = new LinkedList();
  +	    Enumeration names = request.getAttributeNames();
  +	    
  +	    while (names.hasMoreElements()) {
  +		String pname = (String) names.nextElement();
  +		if ( pname.startsWith( prefix ) && pname.endsWith( suffix ) ) {
  +		    values.add( request.getAttribute( pname ) );
  +		}
  +	    }
   
  -    public boolean includeAsValue()
  -    {
  -	return true;
  -    }
  +	    return values.toArray();
   
  -    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 );
  -    }
  +	} else {
  +	    // no "*" in attribute name => just return all values of
  +	    // this one attribute. Make sure, it's an array.
  +	    
  +	    Object value = request.getAttribute( wildcard );
  +	    if ( value != null && !value.getClass().isArray() ) {
  +		Object[] values = new Object[1];
  +		values[0] = value;
  +		return values;
  +	    } else {
  +		return (Object[]) value;
  +	    }
   
  -    public String getSubquery( Configuration tableConf, 
  -			       Configuration columnConf, 
  -			       Configuration modeConf
  -			       ) 
  -	throws ConfigurationException
  -    {
  -	return null;
  +	}
  +	
       }
  +    
  +
   
   }
  
  
  
  1.2       +25 -11    xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestAttributeOutputHelper.java
  
  Index: RequestAttributeOutputHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestAttributeOutputHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RequestAttributeOutputHelper.java	2001/11/19 16:28:33	1.1
  +++ RequestAttributeOutputHelper.java	2001/11/23 10:50:36	1.2
  @@ -10,48 +10,60 @@
   import java.util.Enumeration;
   import org.apache.cocoon.environment.Request;
   import org.apache.avalon.framework.logger.AbstractLoggable;
  +import org.apache.avalon.framework.configuration.Configuration;
   
   /**
  - * Abstraction layer to encapsulate different DBMS behaviour for
  - * normal i.e. non key columns.
  + * Abstraction layer to encapsulate different output
  + * destinations. Configuration option &lt;key-prefix&gt; defaults to
  + * "org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper"+":"
    *
    * @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 $
  + * @version CVS $Id: RequestAttributeOutputHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    * */
   public class RequestAttributeOutputHelper 
  -    extends AbstractLoggable
  +    extends AbstractOutputHelper
       implements OutputHelper {
       
       /**
        * communicate an attribute value to further processing logic. 
  +     * @param modeConf column's mode configuration from resource
  +     * description. This argument is optional.
        * @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,
  +    public void setAttribute( Configuration modeConf,
  +			      Request request,
   			      String name,
   			      Object value
   			      )
       {
  -	request.setAttribute("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:"+name,value);
  +	request.setAttribute( ( (String) this.settings.get("key-prefix",
  +							   "org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper" ) )
  +			      + ":" + name, value);
       }
   
   
       /**
        * retrieve an attribute from other processing steps. <em>Do we
        * really need this thingy?</em>
  +     * @param modeConf column's mode configuration from resource
  +     * description. This argument is optional.
        * @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,
  +    public Object getAttribute( Configuration modeConf,
  +				Request request,
   				String name )
       {
  -	return request.getAttribute("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper:"+name);
  +	return request.getAttribute( ( (String) this.settings.get("key-prefix",
  +								  "org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper" ) )
  +			      + ":" + name );
       }
   
       /**
  @@ -63,7 +75,7 @@
        * your data straight to some beans or other entities could result
        * in data corruption!</em>
        * */
  -    public void rollback( Request request, Exception e )
  +    public void rollback( Configuration modeConf, Request request, Exception e )
       {
   	/*
   	Enumeration attributes = request.getAttributeNames();
  @@ -74,7 +86,9 @@
   	    }
   	}
   	*/
  -	request.setAttribute("org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper.Exception",e.getMessage());
  +	request.setAttribute( ( (String) this.settings.get("key-prefix",
  +							   "org.apache.cocoon.acting.ModularDatabaseAccess.OutputHelper" ) ) + ":",
  +			      e.getMessage());
       };
   
   
  @@ -82,7 +96,7 @@
        * Signal that the database transaction completed
        * successfully. See notes on @link{rollback}.
        * */
  -    public void commit( Request request )
  +    public void commit( Configuration modeConf, Request request )
       {
   	// empty method
       }
  
  
  
  1.2       +48 -120   xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestParameterHelper.java
  
  Index: RequestParameterHelper.java
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/RequestParameterHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RequestParameterHelper.java	2001/11/19 16:28:33	1.1
  +++ RequestParameterHelper.java	2001/11/23 10:50:36	1.2
  @@ -8,11 +8,8 @@
   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 java.util.List;
  +import java.util.LinkedList;
   import org.apache.cocoon.environment.Request;
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  @@ -23,38 +20,53 @@
    * 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 $
  + * @version CVS $Id: RequestParameterHelper.java,v 1.2 2001/11/23 10:50:36 haul Exp $
    */
   public class RequestParameterHelper 
       extends AbstractAttributeHelper 
  -    implements KeyAttributeHelper, ThreadSafe
  +    implements ThreadSafe
   {
  -    
  -    // AccessHelper methods
  +    public Object getAttribute( String name, 
  +				Configuration modeConf, 
  +				Request request
  +				)
  +	throws ConfigurationException
  +    {
  +	String pname = name;
  +	if ( modeConf != null ) {
  +	    pname = modeConf.getAttribute( "parameter", pname );
  +	}
  +	return request.getParameter( pname );
  +    }
   
  -    /**
  -     * 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
  -				      )
  +
  +    public Enumeration getAttributeNames( 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();
  +	return request.getParameterNames();
  +    }
   
  +    public Object[] getAttributeValues( String name, 
  +					Configuration modeConf, 
  +					Request request
  +					)
  +	throws ConfigurationException
  +    {
  +	String wildcard = name;
   	if ( modeConf != null ) {
  -	    wildcard = modeConf.getAttribute( "parameter" );
  +	    wildcard = modeConf.getAttribute( "parameter", wildcard );
   	}
   	int wildcardIndex = wildcard.indexOf( "*" );
   	if ( wildcardIndex != -1 ) {
  +	    // "*" contained in parameter name => combine all
  +	    // parameters' values that match prefix, suffix
  +
  +	    // 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 prefix = wildcard.substring( 0, wildcardIndex );
   	    String suffix;
   	    if ( wildcard.length() >= wildcardIndex + 1 ) {
  @@ -62,110 +74,26 @@
   	    } else {
   		suffix = "";
   	    }
  -	
  +	    List values = new LinkedList();
   	    Enumeration names = request.getParameterNames();
  -
  -	    while ( names.hasMoreElements() ) {
  +	    
  +	    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) );
  +		    values.add( request.getParameter( pname ) );
   		}
   	    }
  -	}
  -
  -	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 );
  -	}
  +	    return values.toArray();
   
  -	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 );
  -	    }
  +	    // no "*" in parameter name => just return all values of
  +	    // this one parameter.
  +	    
  +	    return request.getParameterValues( wildcard );
  +
   	}
  -	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;
  -    }
  -
   }
  
  
  
  1.1                  xml-cocoon2/scratchpad/src/org/apache/cocoon/acting/ModularDatabaseAccess/AbstractAutoIncrementHelper.java
  
  Index: AbstractAutoIncrementHelper.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 org.apache.cocoon.util.HashMap;
  
  /**
   * AbstractDatabaseHelper gives you the infrastructure for easily
   * deploying more AutoIncrementHelpers.  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/23 10:50:36 $ */
  public abstract class AbstractAutoIncrementHelper extends AbstractLoggable
      implements AutoIncrementHelper, 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/AbstractOutputHelper.java
  
  Index: AbstractOutputHelper.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 org.apache.cocoon.util.HashMap;
  
  /**
   * AbstractOutputHelper gives you the infrastructure for easily
   * deploying more output 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/23 10:50:36 $ */
  public abstract class AbstractOutputHelper extends AbstractLoggable
      implements OutputHelper, 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/AutoIncrementHelper.java
  
  Index: AutoIncrementHelper.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.component.Component;
  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: AutoIncrementHelper.java,v 1.1 2001/11/23 10:50:36 haul Exp $ 
   * */
  public interface AutoIncrementHelper 
      extends Component 
  {
      String ROLE = "org.apache.cocoon.acting.ModularDatabaseAccess.AutoIncrementHelper";
      
      /**
       * 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
  			     ) 
  	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;
  
  }
  
  
  

----------------------------------------------------------------------
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