<?xml version="1.0" encoding="ISO-8859-1"?>


<!--
  'W2DBML'

  'W2DBML' stands for "Web to Database Markup Language".

   It exists to facilitate the validation and adjustment
   of data by software logic to fit a particular database schema.  

   A typical use would be a Java servlet:
    a) Employing the JDOM API to read a 'W2DBML' XML document 
    b) Using the values obtained from the 'W2DBML' XML document 
       to validate and adjust a data set based on an HTML form submission.
    c) Writing the data to the database via JDBC.

   Data that does not exist in an in-coming HTML form submission can be set by the Java to
   its default values as defined by the XML, and autogeneratedColumn elements are marked
   with an XML 'AUTOGENERATEDCOLUMN' element and are  
   automatically set by the database itself.

 * <br><br>
 *   This file is part of The web2database package.
 * <br><br>
 *    The web2database package is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 * <br><br> 
 *    The web2database package is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 * <br><br>
 *    You should have received a copy of the GNU General Public License
 *    along with The web2database package; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * <br>
 * @copyright &copy; <a href="mailto:web2database@treelogic-swe.com>TreeLogic Software Engineering</a>, Palo Alto, California, U.S.A. 94301-2437
 * @author <a href="mailto:christophermbalz@stanfordalumni.org">Christopher M. Balz</a>
-->

<!-- 
  Be sure to re-validate this file after changing it, to assure 
  functionality.  

  This is my favorite validator (very non-psychotic):
     http://www.stg.brown.edu/service/xmlvalid/
  Here is another very good validator:
     http://www-106.ibm.com/developerworks/xml/library/xmlvalidatorform.html?dwzone=xml 
-->

<!DOCTYPE W2DBML  [

<!-- Element Definitions -->

<!--
  'database', 'tableWriteOrder', and 'tableName' serve to define what tables in what databases
  get written to and in what order.  This allows the Java logic to follow a given database's referential integrity,
  and even to make writes across disparate databases.      

  'tableRow' serves to define what to write to the tables.
-->

<!ELEMENT W2DBML ( ((database+) | (tableRow+))+ ) >

<!ELEMENT database ( tableWriteOrder ) >

<!ELEMENT tableWriteOrder ( (tableName+) ) >

<!ELEMENT tableName ( #PCDATA ) >

<!ELEMENT tableRow ( ( (columnSet*) | (externalInputColumn*) | (autogeneratedColumn*) | (preexistingInAnotherTable*) )+ ) >

<!--  
      'autogeneratedColumn' elements are not presented to the user
      on the user agent.  Instead of being directly entered 
      by the user, their values are computed 
      by the database and written to the database along
      with each form submission by the user.  This DTD enforces
      and the DynaForm.java code expects that autogeneratedColumn tags do _not_
      have default attributes.  Although the Java logic does not compute the value of 
      auto-generated columns, it must know about them in order to correctly perform database
      write operations.
-->

<!ELEMENT autogeneratedColumn EMPTY >


<!--
     'preexistingInAnotherTable' elements represent database
      columns such as foreign keys and the primary keys of 
      database "weak-entity" tables,
     whose values must be pulled from another database table.  The value of their 'databaseElementName' attribute must
     necessarily duplicate another value of 'databaseElementName' in the W2DBML file.  Therefore the 'databaseElementName'
     attribute is not specified as type 'ID'.
-->

<!ELEMENT preexistingInAnotherTable EMPTY >


<!-- 
     'columnSet' elements define groups of elements as they
     are represented on the client.  'columnSet' elements
     are ignored by the Java code that handles data bound for
     the database.  They exist for readability, so that the 
     server's understanding of the data may be more easily checked 
     by a developer against the client's understanding of the 
     data.  They must match.
 -->

<!ELEMENT columnSet  (  (columnSet*) | (externalInputColumn*)  ) >

<!--
     externalInputColumn elements define fields that correspond to form fields 
     on the client.  If changing the set of attributes defined in 
     this DTD, synchronize ColumnElement.java appropriately.
-->

<!ELEMENT externalInputColumn ( regularExpression* ) >

<!-- Note that because element 'regularExpression' is PCDATA, it is parsed
     for entity values (such as &copy; for the copyright sign).  
     ** Be sure to escape any ampersands with a backslash
        in order to keep them from signifying the start of an entity value to the 
        XML parser. **
-->

<!ELEMENT regularExpression ( ( (pattern), (replaceWith?) ) ) > 
<!ELEMENT pattern     ( #PCDATA ) >
<!ELEMENT replaceWith ( #PCDATA ) >

<!-- Attribute Lists -->

<!ATTLIST W2DBML  
          version     ( 1.0 )                     #REQUIRED
>

<!-- Note: The 'W2DBML' tag must come before any tableRow elements. -->

<!ATTLIST database
          name        ID                       	  #REQUIRED
          driver      CDATA                       #IMPLIED                    
          user        CDATA                       #IMPLIED
          password    CDATA                       #IMPLIED
          url         CDATA                       #IMPLIED          
>

<!-- Note: 'tableRow' names _must_ equal the contents of 
     the corresponding 'tableName' tag.  The Java in the 'web2database.datamodel' package checks and validates this.  
     It is perfectly fine and intended that a given table definition may in fact
     apply to more than one database definition (as 
     defined by the child elements of a 'database' tag).

     Necessary: If a database table is a 'weak entity' (in database terminology), be sure to mark it as such via the 'type' attribute.

     Necessary: For a table that records the submission of dynamically-generated input-sets (on the client), its table type 
                must be 'perInputSet'.  For example, in this test, the database table 'phrase_contribution_events' is one such table.
                It carries no data besides autogenerated data (autonumbered index column and autogenerated timestamp) that is not in other 
                tables, yet it is written to to record at least the attempted submission of a phrase, even if all other tables are not
                written to since the submitted data is a duplicate of row sets already in the database.
-->

<!ATTLIST tableRow
          name        ID                       	  #REQUIRED
          type       ( regular | weakEntity |
                       perInputSet             )  #REQUIRED
>


<!ATTLIST columnSet 
          name        ID                       	 #REQUIRED          
>



<!ATTLIST autogeneratedColumn 
          databaseElementName     ID                                                 #REQUIRED          
          kind                  ( k_AUTONUMBER | k_TIMESTAMP | k_TIME | 
                                  k_DATE | k_DATETIME )                              #REQUIRED          
          datatype              ( INTEGER | DATE | DATETIME | TIMESTAMP )            #REQUIRED
          cacheForReuseInOtherTables        ( y | n )           		             #REQUIRED
>


<!-- 
    Necessary: Currently 'preexistingInAntherTable' elements must have a "maxValue" and "minValue" 
    if they are set to 'onlyWriteRowIfThisValueIsUnique'.  Otherwise, the 'Transformed
    class will not be able to properly validate their data.

    Note that currently, setting 'preexistingInAntherTable' elements to a 'onlyWriteRowIfThisValueIsUnique'
    non-false setting is the only way to avoid writing to weak-entity tables when 'Web2Database' is iterating
    through input sets that have been dynamically generated on the client.
-->

<!ATTLIST preexistingInAnotherTable 
          databaseElementName        CDATA                                     #REQUIRED    
          sourceTable                CDATA                     				   #REQUIRED 
          sourceColumn               CDATA                     				   #REQUIRED
          datatype    	  		   ( BOOLEAN | VARCHAR | 
                      	  		     LONGVARCHAR | INTEGER |
	                                 BIGINT | FLOAT |  
                      	  		     DECIMAL | TEXT | BLOB )   				   #REQUIRED
          onlyWriteRowIfThisValueIsUnique  
                                            ( TRUE |
                                              TRUE_EXCEPT_FOR_EMPTY_STRING | 
                                              TRUE_EXCEPT_FOR_NULL | 
                                              FALSE )                          #REQUIRED
          default     	             CDATA                     		   		   #IMPLIED
          maxValue    	                    CDATA              		   		   #IMPLIED
          minValue    	                    CDATA              		   		   #IMPLIED  
>


<!--
   'externalInputColumn': How to Use

   Introduction:
   - - - - - - 
   The 'externalInputColumn' element is ready to effortlessly handle HTML form elements whose i.d.s have been dynamically
   generated by the client.  It handles these HTML form elements when appropriately switched by several of its attributes. 
   In the future, it will use its children "pattern" and "replaceWith" to do the work.

   To handle dynamically-generated HTML form i.d.s, this element's attributes and children must be 
   configured properly, and the HTML form i.d. must follow a specific format.  Detail below.


   How-To:
   - - - - 

   The format required for processing of HTML form elements with dynamically generated form element i.d.s
   is that the form elements must have a fixed, constant name that is generically substitutable: 
   for example, "phraseBoxSet_number_d_0", where the "_d" will be substituted for a dynamically generated number.
   The form element name in the HttpServletRequest
   would thus be for example "phraseBoxSet_number_0_0".  The only number that currently will be substituted for is 
   the set number, the second number in the sequence of two numbers separated by an underbar.  The final number is permanent.

   If there are two sequences, as in the example "_0_1", the first number must indicate the set to which 
   the form element belongs, and the second number merely adds a unique identifier with respect to the given set.
   A given set of values
   is of course written in one loop through all the database tables, so that each member of the set
   goes in the correct row in the table that it belongs to.

   If there is just one sequence (i.e., "phraseBox_1"), then the sequence will be interpreted as a mere unique identifier, and grouping
   of the element by set will not happen.

   If 'web2database' Java logic finds column elements ending with the pattern "_\d+_\d+" (where '\d+' stands for one or more digits),
   currently it will assume that they are dynamically generated and will automatically find all members of the set.
   
   The 'regularExpression' element is provided in W2DBML to provide for greater future power and flexibility in selecting and 
   transforming dynamically-generated HTML form element i.d. names.


   Max and Min Values
   - - - - - - - - - -
     The Java performs bounds-checking on values of data items 
     only insofar as the                                        
     optional 'maxValue' and 'minValue' are present as    
     attributes of those data items.   
     In cases where 'maxValue' and 'minValue' apply to a string, 
     they are taken as indicating the bounds for the length of 
     the string.                          

-->

<!ATTLIST externalInputColumn 
            
          databaseElementName               ID                  		   	  	#REQUIRED
          htmlFormElementName               CDATA                          	  	#REQUIRED
          datatype    	                    ( BOOLEAN | VARCHAR | 
                      	                      LONGVARCHAR | INTEGER 	   	  	 
                      	                      | DECIMAL | TEXT | BLOB )    	  	#REQUIRED
          default     	                    CDATA                    	   	  	#REQUIRED
          onlyWriteRowIfThisValueIsUnique  
                                            ( TRUE |
                                              TRUE_EXCEPT_FOR_EMPTY_STRING | 
                                              TRUE_EXCEPT_FOR_NULL | 
                                              FALSE )                         	#REQUIRED
          cacheForReuseInOtherTables        ( y | n )           		   		#IMPLIED
          howToFind                         ( MAX | 0 | 1 | 2 | 3 | 4 |
                                              5   | 6 | 7 | 8 | 9       )  		#IMPLIED
          nonHtmlFormElementName            CDATA               		   		#IMPLIED
          nonHtmlFormElementSource          CDATA               		   		#IMPLIED
          translateFromBlobType             ( String | JPG | PNG )         		#IMPLIED      
          mandatory   	                    ( M | O )                      		#IMPLIED
          maxValue    	                    CDATA                    	   		#IMPLIED
          minValue    	                    CDATA                          		#IMPLIED  
>



<!ATTLIST regularExpression
          name         ID                                                                                #REQUIRED
          description  CDATA                                                                             #IMPLIED
          action     ( SUBSTITUTE_IN_SET_NUMBER | VALIDATE_VALUE | VALIDATE_KEY )                        #REQUIRED
          applyTo    ( NAME | VALUE )                                                                    #REQUIRED
          flavor     ( PERL5 | JAVA_1.4 | GNU_EMACS | MODERN_GREP | PYTHON | TCL | MODERN_SED | 
                       MODERN_AWK | LEX )                                                                #IMPLIED
>


<!ATTLIST pattern
          name        ID                       	  #IMPLIED
>

<!ATTLIST replaceWith
          name        ID                       	  #IMPLIED
>


]>


<W2DBML version="1.0" >

 <database  name="phraseologic"  driver="org.gjt.mm.mysql.Driver"  user="www"  
            password="freekfarmzqau3KVI"  url="jdbc:mysql://localhost:3306/phraseologic?autoReconnect=true" >
  <tableWriteOrder>
   <tableName>
     Users
   </tableName>
   <tableName>
     Phrases
   </tableName>
   <tableName>
     Phrase_Attributees
   </tableName>
   <tableName>
     Source_Titles
   </tableName>
   <tableName>
     Source_Pages
   </tableName>
   <tableName>
     Phrase_Contribution_Events
   </tableName>
   <tableName>
     Contact_Permissions
   </tableName>
   <tableName>
     Phrase_Comments_By_Users
   </tableName>
  </tableWriteOrder>
 </database>


 <tableRow  name="Phrase_Contribution_Events"  type="perInputSet" >
  <autogeneratedColumn  databaseElementName="phrase_contribution_event_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="n" />
  <autogeneratedColumn  databaseElementName="time_of_contribution_event"    kind="k_TIMESTAMP"   datatype="INTEGER" 
                        cacheForReuseInOtherTables="n" />
  <preexistingInAnotherTable  databaseElementName="phrase_id"  sourceTable="Phrases"  sourceColumn="phrase_id" 
                              datatype="INTEGER" onlyWriteRowIfThisValueIsUnique="FALSE" />
  <preexistingInAnotherTable  databaseElementName="user_id"  sourceTable="Users"  sourceColumn="user_id" 
                              datatype="INTEGER" onlyWriteRowIfThisValueIsUnique="FALSE" />
  <preexistingInAnotherTable  databaseElementName="phrase_attributee_id"  sourceTable="Phrase_Attributees"  
                              sourceColumn="phrase_attributee_id" 
                              datatype="INTEGER" onlyWriteRowIfThisValueIsUnique="FALSE" />
  <preexistingInAnotherTable  databaseElementName="page_source_id"  sourceTable="Source_Pages"  sourceColumn="page_source_id" 
                              datatype="INTEGER" onlyWriteRowIfThisValueIsUnique="FALSE" />
 </tableRow>


 <tableRow  name = "Phrases"  type="regular">
  <autogeneratedColumn  databaseElementName="phrase_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="y" />
  <autogeneratedColumn  databaseElementName="date_first_contributed" kind="k_DATE"  datatype="DATE" 
                        cacheForReuseInOtherTables="n" />
  <externalInputColumn  databaseElementName="phrase_text"  htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_0"  
                        datatype="BLOB"  
                        translateFromBlobType="String" minValue="0"  maxValue="10000"   default=""                          
                        onlyWriteRowIfThisValueIsUnique="TRUE">
  </externalInputColumn>
 </tableRow>


 <tableRow  name="Users"  type="regular">
  <autogeneratedColumn  databaseElementName="user_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="y" />
  <externalInputColumn  databaseElementName="user_user_name"  htmlFormElementName="phraseologic.users.user_user_name"  
                        datatype="VARCHAR"  minValue="0"  maxValue="25"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="TRUE">
  </externalInputColumn>
  <externalInputColumn  databaseElementName="user_first_name"  htmlFormElementName="phraseologic.users.user_first_name"  
                        datatype="VARCHAR"  minValue="0"  maxValue="25"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
  <externalInputColumn  databaseElementName="user_last_name"  htmlFormElementName="phraseologic.users.user_last_name"  
                        datatype="VARCHAR"  minValue="0"  maxValue="25"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
  <externalInputColumn  databaseElementName="user_email_address"  htmlFormElementName="phraseologic.users.user_email_address"  
                        datatype="VARCHAR"  minValue="0"  maxValue="40"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
 </tableRow>


 <tableRow  name="Contact_Permissions"  type="weakEntity">
  <preexistingInAnotherTable  databaseElementName="user_id"  sourceTable="Users"  sourceColumn="user_id" 
                              datatype="INTEGER" onlyWriteRowIfThisValueIsUnique="TRUE" 
                              minValue="0"       maxValue="99999999" />
  <externalInputColumn  databaseElementName="ok_contact_by_phraseologic"  htmlFormElementName="phraseologic.users.ok_contact_by_phraseologic"  
                        datatype="BOOLEAN"  default=""  onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
  <externalInputColumn  databaseElementName="ok_contact_by_phraseologic_associates"  
                        htmlFormElementName="phraseologic.users.ok_contact_by_phraseologic_associates"  
                        datatype="BOOLEAN"  default=""  onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
 </tableRow>


<tableRow  name="Phrase_Comments_By_Users"  type="regular">
  <autogeneratedColumn  databaseElementName="last_edited" kind="k_TIMESTAMP"  datatype="TIMESTAMP" 
                        cacheForReuseInOtherTables="n" />
  <autogeneratedColumn  databaseElementName="phrase_comment_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="n" />
  <externalInputColumn  databaseElementName="comment_on_phrase"  
                        htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_4"  
                        datatype="BLOB"  
                        translateFromBlobType="String"  
                        minValue="0"  maxValue="10000"  default=""  onlyWriteRowIfThisValueIsUnique="TRUE">
  </externalInputColumn>
  <preexistingInAnotherTable  databaseElementName="phrase_id"  sourceTable="Phrases"  sourceColumn="phrase_id" 
                              datatype="INTEGER"  onlyWriteRowIfThisValueIsUnique="FALSE" />
  <preexistingInAnotherTable  databaseElementName="user_id"  sourceTable="Users"  sourceColumn="user_id" 
                              datatype="INTEGER"  onlyWriteRowIfThisValueIsUnique="FALSE" />
 </tableRow>


 <tableRow  name="Source_Titles"  type="regular">
  <autogeneratedColumn  databaseElementName="source_title_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="y" />
  <externalInputColumn  databaseElementName="title"  htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_1"  
                        datatype="VARCHAR"  minValue="0"  maxValue="200"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="TRUE">
  </externalInputColumn>
  <externalInputColumn  databaseElementName="year_of_publication"  
                        htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_5"  
                        datatype="INTEGER"  minValue="-10000"  maxValue="2023"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
  <!--  We don't require an absolutely unique value for ISBN because many contributors will leave the field blank, 
        this automatically assigning the ISBN field the "empty string" value. -->
  <externalInputColumn  databaseElementName="ISBN"  
                        htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_6"  
                        datatype="VARCHAR"  minValue="0"  maxValue="13"        default=""                          
                        onlyWriteRowIfThisValueIsUnique="TRUE_EXCEPT_FOR_EMPTY_STRING"> 
  </externalInputColumn>
 </tableRow>


 <tableRow  name="Source_Pages"  type="regular">
  <autogeneratedColumn  databaseElementName="page_source_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="y" />
  <externalInputColumn  databaseElementName="page_number"  htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_2"  
                        datatype="VARCHAR"  minValue="0"  maxValue="5" default="" onlyWriteRowIfThisValueIsUnique="FALSE">
  </externalInputColumn>
  <preexistingInAnotherTable  databaseElementName="source_title_id"  sourceTable="Source_Titles"  
                              sourceColumn="source_title_id"  datatype="INTEGER"  onlyWriteRowIfThisValueIsUnique="FALSE" />
 </tableRow>



 <tableRow  name="Phrase_Attributees"  type="regular">
  <autogeneratedColumn  databaseElementName="phrase_attributee_id"  kind="k_AUTONUMBER"  datatype="INTEGER" 
                        cacheForReuseInOtherTables="y" />
  <externalInputColumn  databaseElementName="last_name_first_name_middle_name"  
                        htmlFormElementName="phraseologic.dependentOnNumericSequence.phraseBoxSet_number_d_3"  
                        datatype="VARCHAR"  minValue="0"  maxValue="55"  default=""  onlyWriteRowIfThisValueIsUnique="TRUE">
  </externalInputColumn>
 </tableRow>

  

</W2DBML>
 