Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / tools / jdbc / JdbcBeanSerializer.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/tools/jdbc/JdbcBeanSerializer.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/tools/jdbc/JdbcBeanSerializer.java
new file mode 100644 (file)
index 0000000..656b608
--- /dev/null
@@ -0,0 +1,1380 @@
+// JdbcBeanSerializer.java\r
+// $Id: JdbcBeanSerializer.java,v 1.1 2010/06/15 12:27:29 smhuang Exp $\r
+// (c) COPYRIGHT MIT, INRIA and Keio, 2000.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+package org.w3c.tools.jdbc;\r
+\r
+import java.beans.BeanInfo;\r
+import java.beans.Beans;\r
+import java.beans.IntrospectionException;\r
+import java.beans.Introspector;\r
+import java.beans.PropertyChangeListener;\r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyDescriptor;\r
+\r
+import java.io.IOException;\r
+\r
+import java.lang.reflect.Method;\r
+import java.lang.reflect.InvocationTargetException;\r
+\r
+import java.sql.ResultSet;\r
+import java.sql.ResultSetMetaData;\r
+import java.sql.SQLException;\r
+\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+import java.util.Properties;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * Read <a href="http://www.w3.org/Jigsaw/Doc/Programmer/jspdb.html">http://www.w3.org/Jigsaw/Doc/Programmer/jspdb.html</a> \r
+ * to know how to use this class.\r
+ * @version $Revision: 1.1 $\r
+ * @author  Benoît Mahé (bmahe@w3.org)\r
+ */\r
+public class JdbcBeanSerializer implements PropertyChangeListener {\r
+\r
+    /**\r
+     * Bean modified?\r
+     */\r
+    private boolean modified = false;\r
+\r
+    /**\r
+     * Our bean.\r
+     */\r
+    protected JdbcBeanInterface bean = null;\r
+\r
+    /**\r
+     * The associated JdbcBean\r
+     */\r
+    private JdbcBeanInterface beans[] = null;\r
+\r
+    /**\r
+     * INTERSECT, UNION, EXCEPT.\r
+     */\r
+    protected final static int NOTHING   = -1;\r
+    protected final static int INTERSECT = 10;\r
+    protected final static int UNION     = 20;\r
+    protected final static int EXCEPT    = 30;\r
+\r
+    protected int priority[] = { NOTHING, NOTHING, NOTHING };\r
+\r
+    protected JdbcBeanSerializer intersect_serializer = null;\r
+    protected JdbcBeanSerializer union_serializer     = null;\r
+    protected JdbcBeanSerializer except_serializer    = null;\r
+\r
+    /**\r
+     * The ResultSet\r
+     */\r
+    protected ResultSet result = null;\r
+\r
+    /**\r
+     * The tables/bean used to generate the SQL request (in the correct\r
+     * order)\r
+     */\r
+    private Vector beantables = null;\r
+\r
+    /**\r
+     * The Foreign keys <(class,class), String[]>\r
+     */\r
+    private static Hashtable foreignKeys = new Hashtable();\r
+\r
+    private void registerForeignKeys(Class beanclass1, \r
+                                    Class beanclass2)\r
+    {\r
+       Integer key = new Integer(beanclass1.hashCode() & \r
+                                 beanclass2.hashCode());\r
+       if (! foreignKeys.containsKey(key)) {\r
+           foreignKeys.put(key, computeForeignKeys(beanclass1, beanclass2));\r
+       }\r
+    }\r
+\r
+    private String[] getForeignKeys(Class beanclass1,\r
+                                   Class beanclass2)\r
+    {\r
+       Integer key = new Integer(beanclass1.hashCode() & \r
+                                 beanclass2.hashCode());\r
+       String keys[] = (String[]) foreignKeys.get(key);\r
+       if (keys == null) {\r
+           keys = computeForeignKeys(beanclass1, beanclass2);\r
+           foreignKeys.put(key, keys);\r
+       }\r
+       return keys;\r
+    }\r
+\r
+    private String[] computeForeignKeys(Class beanclass1,\r
+                                       Class beanclass2) \r
+    {\r
+       try {\r
+           BeanInfo           bi1    = Introspector.getBeanInfo(beanclass1);\r
+           PropertyDescriptor pds1[] = bi1.getPropertyDescriptors();\r
+\r
+           BeanInfo           bi2    = Introspector.getBeanInfo(beanclass2);\r
+           PropertyDescriptor pds2[] = bi2.getPropertyDescriptors();\r
+       \r
+           Vector foreign = new Vector();\r
+           \r
+           for(int cpt1 = 0 ; cpt1 < pds1.length ; cpt1++) {\r
+               PropertyDescriptor pd1 = pds1[cpt1];\r
+               for (int cpt2 = 0 ; cpt2 < pds2.length ; cpt2++) {\r
+                   PropertyDescriptor pd2 = pds2[cpt2];\r
+                   if ((! pd2.isHidden()) \r
+                       && (! pd1.isHidden())\r
+                       && (equalsForeignKeys(pd1.getName(),pd2.getName()))) {\r
+                       foreign.addElement(pd1.getName());\r
+                   }\r
+               }\r
+           }\r
+           String keys[] = new String[foreign.size()];\r
+           foreign.copyInto(keys);\r
+           return keys;\r
+       } catch (IntrospectionException ex) {\r
+           return new String[0];\r
+       }\r
+    }\r
+\r
+    /**\r
+     * toto_username == username\r
+     */\r
+    private static boolean equalsForeignKeys(String key1, String key2) {\r
+       int idx1 = key1.lastIndexOf("_");\r
+       if (idx1 != -1) {\r
+           key1 = key1.substring(idx1);\r
+       }\r
+       int idx2 = key2.lastIndexOf("_");\r
+       if (idx2 != -1) {\r
+           key2 = key2.substring(idx2);\r
+       }\r
+       return key1.equals(key2);\r
+    }\r
+\r
+    protected void markModified(boolean modified) {\r
+       this.modified = modified;\r
+    }\r
+\r
+    protected boolean isModified() {\r
+       return modified;\r
+    }\r
+\r
+    /**\r
+     * PropertyChangeListener implementation: This method gets called when\r
+     * a bound property is changed.\r
+     * @param evt A PropertyChangeEvent object describing the event source \r
+     * and the property that has changed.\r
+     */\r
+    public void propertyChange(PropertyChangeEvent evt) {\r
+       Object source = evt.getSource();\r
+       String name   = evt.getPropertyName();\r
+       Object value  = evt.getNewValue();\r
+       if (source == bean) {\r
+           if (value == null) {\r
+               PropertyDescriptor pd = getPropertyDescriptor(name);\r
+               if (JdbcBeanUtil.isJdbcBean(pd.getPropertyType())) {\r
+                   // delete cached bean descriptors\r
+                   this.beans = null;\r
+                   // update listeners\r
+                   JdbcBeanInterface oldbean = \r
+                       (JdbcBeanInterface)evt.getOldValue();\r
+                   if (oldbean != null) {\r
+                       PropertyCache.removeProperties(oldbean);\r
+                       oldbean.removePropertyChangeListener(this);\r
+                   }\r
+               }\r
+           } else if (value instanceof JdbcBeanInterface) {\r
+               registerForeignKeys(bean.getClass(), value.getClass());\r
+               // delete cached bean descriptors\r
+               this.beans = null;\r
+               // update listeners\r
+               JdbcBeanInterface oldbean = \r
+                   (JdbcBeanInterface)evt.getOldValue();\r
+               JdbcBeanInterface newbean = (JdbcBeanInterface)value;\r
+               if (oldbean != null) {\r
+                   PropertyCache.removeProperties(oldbean);\r
+                   oldbean.removePropertyChangeListener(this);\r
+               }\r
+               newbean.addPropertyChangeListener(this);\r
+           } else {\r
+               JdbcBeanInterface bean = (JdbcBeanInterface)source;\r
+               PropertyCache.addProperty(bean, name, evt.getNewValue());\r
+               markModified(true);\r
+           }\r
+       } else {\r
+           markModified(true);\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Get the raw value of the given property, split the operator and\r
+     * the value and convert the raw value into a SQL value.<p>\r
+     * ie "~A.*" will become { " ~ " , "'A.*'" }\r
+     * @param bean the property holder\r
+     * @param pd the property descriptor\r
+     */\r
+    private String[] getSQLOperatorNValue(JdbcBeanInterface bean, \r
+                                         PropertyDescriptor pd) \r
+    {\r
+       Class  type = pd.getPropertyType();\r
+       Method m    = pd.getReadMethod(); \r
+       if (m != null) { // are we authozired to read it?\r
+           // use the cache\r
+           Object value = PropertyCache.getProperty(bean, pd); \r
+           if (value == null) {\r
+               return null;\r
+           }\r
+           return SQL.getSQLOperator(value);\r
+       }\r
+       return null;\r
+    }\r
+\r
+    /**\r
+     * Get the SQL value of the given property.\r
+     * @param bean the property holder\r
+     * @param pd the property descriptor\r
+     */\r
+    private String getSQLValue(JdbcBeanInterface bean, \r
+                              PropertyDescriptor pd) \r
+    {\r
+       Class  type = pd.getPropertyType();\r
+       Method m    = pd.getReadMethod(); \r
+       if (m != null) { // are we authozired to read it?\r
+           // use the cache\r
+           Object value = PropertyCache.getProperty(bean, pd); \r
+           if (value == null) {\r
+               return null;\r
+           }\r
+            return SQL.getSQLValue(value);\r
+       }\r
+       return null;\r
+    }\r
+\r
+    /**\r
+     * @param name\r
+     * @param value\r
+     * @param buf\r
+     */\r
+    private void append(String name, \r
+                       String operator,\r
+                       String value, \r
+                       String separator,\r
+                       StringBuffer buf)\r
+    {\r
+       if (buf.length() > 0) {\r
+           buf.append(separator).append(" ");\r
+       }\r
+       buf.append(name).append(operator).append(value).append(" ");\r
+    }\r
+\r
+    /**\r
+     * @param name\r
+     * @param value\r
+     * @param namesbuffer\r
+     * @param valuesbuffer\r
+     */\r
+    private void appendInsert(String name, \r
+                             String value, \r
+                             StringBuffer namesbuffer,\r
+                             StringBuffer valuesbuffer)\r
+    {\r
+       if (namesbuffer.length() > 0) {\r
+           namesbuffer.append(", ").append(name);\r
+           valuesbuffer.append(", ").append(value);\r
+       } else {\r
+           namesbuffer.append("(").append(name);\r
+           valuesbuffer.append("(").append(value);\r
+       }\r
+    }\r
+\r
+    private JdbcBeanInterface[] getJdbcBeans() {\r
+       if (beans != null) {\r
+           return beans;\r
+       }\r
+       try {\r
+           BeanInfo info = Introspector.getBeanInfo(bean.getClass());\r
+           Vector   vb   = new Vector();\r
+           PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if ((! pd.isHidden()) &&  \r
+                   (JdbcBeanUtil.isJdbcBean(pd.getPropertyType()))) {\r
+                   Method m = pd.getReadMethod();\r
+                   if (m != null) {\r
+                           Object value = m.invoke(bean, (Object [])null);\r
+                       if (value != null) {\r
+                           vb.addElement(value);\r
+                       }\r
+                   }\r
+               }\r
+           }\r
+           beans = new JdbcBeanInterface[vb.size()];\r
+           vb.copyInto(beans);\r
+           return beans;\r
+       } catch (IntrospectionException ex) {\r
+           return null;\r
+       } catch (IllegalAccessException ex) {\r
+           return null;\r
+       } catch (InvocationTargetException ex) {\r
+           return null;\r
+       }\r
+    }\r
+\r
+    private void appendForeignKeys(Vector tables,\r
+                                  StringBuffer buffer,\r
+                                  String properties[]) \r
+       throws IntrospectionException\r
+    {\r
+       BeanInfo     info   = Introspector.getBeanInfo(bean.getClass());\r
+       PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+       appendForeignKeys(tables, buffer, pds, properties);\r
+    }\r
+\r
+    private void appendForeignKeys(Vector tables,\r
+                                  StringBuffer buffer,\r
+                                  PropertyDescriptor pds[],\r
+                                  String properties[])\r
+       throws IntrospectionException\r
+    {\r
+       JdbcBeanInterface jbeans[] = getJdbcBeans();\r
+       if (jbeans != null) { // FOREIGN KEYs\r
+           for (int i = 0 ; i < jbeans.length ; i++) {\r
+               JdbcBeanInterface jbean = jbeans[i];\r
+               // foreign keys\r
+               String keys[] = getForeignKeys(jbean.getClass(),\r
+                                              bean.getClass());\r
+               for (int f = 0 ; f < keys.length ; f++) {\r
+                   String key = keys[f];\r
+                   if ((properties == null) ||\r
+                       (JdbcBeanUtil.isIn(key, properties))) {\r
+                       append(jbean.getJdbcTable()+"."+key,\r
+                              " = ",\r
+                              bean.getJdbcTable()+"."+key,\r
+                              "AND",\r
+                              buffer);\r
+                   }\r
+               }\r
+               BeanInfo           bi     = null;\r
+               PropertyDescriptor jpds[] = null;\r
+               // value now\r
+               bi   = Introspector.getBeanInfo(jbean.getClass());\r
+               jpds = bi.getPropertyDescriptors();\r
+               for (int jpd_cpt = 0 ; jpd_cpt < jpds.length ; jpd_cpt++) {\r
+                   PropertyDescriptor jpd = jpds[jpd_cpt];\r
+                   if (jpd.isHidden()) {\r
+                       continue;\r
+                   }\r
+                   String jname = jpd.getName();\r
+                   if ((properties == null) ||\r
+                       (JdbcBeanUtil.isIn(jname, properties))) {\r
+                       String split[] = getSQLOperatorNValue(jbean, jpd);\r
+                       if (split != null) {\r
+                           append(jbean.getJdbcTable()+"."+jname,\r
+                                  split[0],\r
+                                  split[1],\r
+                                  "AND",\r
+                                  buffer);\r
+                       }\r
+                   }\r
+               }\r
+               tables.addElement(jbean);\r
+               // test FIXME recursive stuff\r
+               jbean.getSerializer().appendForeignKeys(tables, \r
+                                                       buffer, \r
+                                                       properties);\r
+           }\r
+       }\r
+    }\r
+\r
+    protected String computeSQLCount(boolean all, \r
+                                    boolean distinct, \r
+                                    String properties[]) \r
+    {\r
+       String count = (distinct) ? "DISTINCT count(*)" : "count(*)";\r
+       return computeSQLSelect(all, count, properties);\r
+    }\r
+\r
+    private String computeSQLSelect(String orderby[], \r
+                                   boolean asc[],\r
+                                   boolean all)\r
+    {\r
+       return computeSQLSelect(orderby, asc, all, "*", null);\r
+    }\r
+\r
+    private String computeSQLSelect(String orderby[], \r
+                                   boolean asc[],\r
+                                   boolean all,\r
+                                   String select)\r
+    {\r
+       return computeSQLSelect(orderby, asc, all, select, null);\r
+    }\r
+\r
+    protected String computeSQLSelect(String orderby[], \r
+                                     boolean asc[],\r
+                                     boolean all,\r
+                                     String select,\r
+                                     String properties[])\r
+    {\r
+       String sql = computeSQLSelect(all, select, properties);\r
+       StringBuffer buffer = new StringBuffer(sql);\r
+       if (orderby != null) {\r
+           buffer.append(" ORDER BY ");\r
+           for (int j = 0 ; j < orderby.length ; j++) {\r
+               if (j != 0) {\r
+                   buffer.append(", ");\r
+               }\r
+               buffer.append(orderby[j]);\r
+               if (! asc[j]) {\r
+                   buffer.append(" DESC");\r
+               }\r
+           }\r
+       }\r
+       return buffer.toString();\r
+       \r
+    }\r
+\r
+    private String computeSQLSelect(boolean all,\r
+                                   String select,\r
+                                   String properties[])\r
+    {\r
+       try {\r
+           BeanInfo     info   = Introspector.getBeanInfo(bean.getClass());\r
+           StringBuffer buffer = new StringBuffer();\r
+           String       table  = bean.getJdbcTable();\r
+           this.beantables     = new Vector();\r
+           beantables.addElement(bean);\r
+           \r
+           PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+           if (all) {\r
+               // FOREIGN KEYs\r
+               appendForeignKeys(beantables, buffer, pds, properties);\r
+           }\r
+           // known values\r
+           for (int i = 0 ; i < pds.length ; i++) { \r
+               PropertyDescriptor pd = pds[i];\r
+               if (! pd.isHidden()) {\r
+                   String jname = pd.getName();\r
+                   if ((properties == null) ||\r
+                       (JdbcBeanUtil.isIn(jname, properties))) {\r
+                       String split[] = getSQLOperatorNValue(bean, pd);\r
+                       if (split != null) {\r
+                           append(table+"."+jname, \r
+                                  split[0],\r
+                                  split[1],\r
+                                  "AND",\r
+                                  buffer);\r
+                       }\r
+                   }\r
+               }\r
+           }\r
+           // build SQL request\r
+           if (buffer.length() > 0) {\r
+               StringBuffer tables = new StringBuffer();\r
+               for (int i = 0 ; i < beantables.size() ; i++) {\r
+                   JdbcBeanInterface jbean = \r
+                       (JdbcBeanInterface)beantables.elementAt(i);\r
+                   if (i != 0) {\r
+                       tables.append(", ");\r
+                   }\r
+                   tables.append(jbean.getJdbcTable());\r
+               }\r
+               tables.append(" WHERE ");\r
+               tables.insert(0, "SELECT "+select+" FROM ");\r
+               tables.append(buffer.toString());\r
+               buffer = tables;\r
+           } else {\r
+               buffer = new StringBuffer("SELECT "+select+" FROM ");\r
+               buffer.append(table);\r
+           }\r
+           // union? intersect? except?\r
+           for (int i = 0 ; i < priority.length ; i++) {\r
+               int p = priority[i];\r
+               if (p == NOTHING) {\r
+                   break;\r
+               }\r
+               switch (p) \r
+                   {\r
+                   case INTERSECT:\r
+                       if (intersect_serializer != null) {\r
+                           String intersect = \r
+                               intersect_serializer.computeSQLSelect(all,\r
+                                                                     select,\r
+                                                                 properties);\r
+                           buffer.append(" INTERSECT (").append(intersect);\r
+                           buffer.append(")");\r
+                       }\r
+                       break;\r
+                   case UNION:\r
+                       if (union_serializer != null) {\r
+                           String union = \r
+                               union_serializer.computeSQLSelect(all,\r
+                                                                 select,\r
+                                                                 properties);\r
+                           buffer.append(" UNION (").append(union);\r
+                           buffer.append(")");\r
+                       }\r
+                       break;\r
+                   case EXCEPT:\r
+                       if (except_serializer != null) {\r
+                           String except =\r
+                               except_serializer.computeSQLSelect(all,\r
+                                                                  select,\r
+                                                                  properties);\r
+                           buffer.append(" EXCEPT (").append(except);\r
+                           buffer.append(")");\r
+                       }\r
+                       break;\r
+                   default:\r
+                       // unreached (I hope)\r
+                   }\r
+           }\r
+           return buffer.toString();\r
+       } catch (IntrospectionException ex) {\r
+           return null;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Compute the SQL request necessary to update the Database.\r
+     * @return a String\r
+     */\r
+    protected String computeSQLInsert() {\r
+       try {\r
+           BeanInfo info = Introspector.getBeanInfo(bean.getClass());\r
+           PropertyDescriptor pds[]  = info.getPropertyDescriptors();\r
+           StringBuffer namesbuffer  = new StringBuffer();\r
+           StringBuffer valuesbuffer = new StringBuffer();\r
+           for (int i = 0 ; i < pds.length ; i++) { \r
+               PropertyDescriptor pd = pds[i];\r
+               if (! pd.isHidden()) {  \r
+                   String value = getSQLValue(bean, pd);\r
+                   if (value != null) {\r
+                       appendInsert(pd.getName(), \r
+                                    value, \r
+                                    namesbuffer, \r
+                                    valuesbuffer);\r
+                   }\r
+               }\r
+           }\r
+           if (namesbuffer.length() > 0) {\r
+               StringBuffer request = new StringBuffer("INSERT INTO ");\r
+               request.append(bean.getJdbcTable()).append(" ");\r
+               request.append(namesbuffer).append(") ");\r
+               request.append("VALUES ").append(valuesbuffer).append(")");\r
+               return request.toString();\r
+           } else {\r
+               return null;\r
+           }\r
+       } catch (IntrospectionException ex) {\r
+           return null;\r
+       }\r
+    }\r
+\r
+    protected String computeSQLDelete() {\r
+       try {\r
+           BeanInfo     info   = Introspector.getBeanInfo(bean.getClass());\r
+           StringBuffer buffer = new StringBuffer();\r
+           StringBuffer table = new StringBuffer(bean.getJdbcTable());\r
+           PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+           // known values\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if (! pd.isHidden()) {\r
+                   String split[] = getSQLOperatorNValue(bean, pd);\r
+                   if (split != null) {\r
+                       append(pd.getName(), split[0], split[1],"AND", buffer);\r
+                   }\r
+               }\r
+           }\r
+           // build SQL request\r
+           if (buffer.length() > 0) {\r
+               table.append(" WHERE ");\r
+               table.insert(0, "DELETE FROM ");\r
+               table.append(buffer.toString());\r
+               buffer = table;\r
+           } else {\r
+               return null;\r
+           }\r
+           return buffer.toString();\r
+       } catch (IntrospectionException ex) {\r
+           return null;\r
+       }\r
+    }\r
+\r
+    protected String computeSQLUpdate(String primarykeys[]) {\r
+       try {\r
+           BeanInfo     info     = Introspector.getBeanInfo(bean.getClass());\r
+           StringBuffer buffer   = new StringBuffer();\r
+           StringBuffer pkbuffer = new StringBuffer();\r
+           StringBuffer table    = new StringBuffer(bean.getJdbcTable());\r
+           PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+           // known values\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if (! pd.isHidden()) {\r
+                   String name = pd.getName();\r
+                   String split[] = getSQLOperatorNValue(bean, pd);\r
+                   if (split != null) {\r
+                       if (JdbcBeanUtil.isIn(name, primarykeys)) {\r
+                           append(name, split[0], split[1], "AND", pkbuffer);\r
+                       } else {\r
+                           append(name, split[0], split[1], ",", buffer);\r
+                       }\r
+                   }\r
+               }\r
+           }\r
+           // build SQL request\r
+           if (buffer.length() > 0) {\r
+               table.append(" SET ");\r
+               table.insert(0, "UPDATE ");\r
+               table.append(buffer.toString());\r
+               table.append(" WHERE ");\r
+               table.append(pkbuffer.toString());\r
+               buffer = table;\r
+           } else {\r
+               return null;\r
+           }\r
+           return buffer.toString();\r
+       } catch (IntrospectionException ex) {\r
+           return null;\r
+       }\r
+    }\r
+\r
+    protected JdbcServer getJdbcServer() {\r
+       Properties props = new Properties();\r
+       Jdbc.setMaxConn(props, bean.getMaxConn());\r
+       return JdbcServer.getServer(bean.getJdbcURI(), \r
+                                   bean.getJdbcUser(), \r
+                                   bean.getJdbcPassword(), \r
+                                   bean.getJdbcDriver(), \r
+                                   props);\r
+    }\r
+\r
+    private void executeSQLQuery(String sqlrequest) \r
+       throws SQLException\r
+    {\r
+       result = getJdbcServer().runQuery(sqlrequest, false);\r
+    }\r
+\r
+    private int executeSQLUpdate(String sqlrequest) \r
+       throws SQLException\r
+    {\r
+       return getJdbcServer().runUpdate(sqlrequest, false);\r
+    }\r
+\r
+    /**\r
+     * Count the number or row with columns matching the value of the\r
+     * bean properties.\r
+     * @return an int\r
+     */\r
+    public int count() {\r
+       return count(true, false, null);\r
+    }\r
+\r
+    /**\r
+     * Count the number or row with columns matching the value of the\r
+     * given properties.\r
+     * @param properties The property names\r
+     * @return an int\r
+     */\r
+    public int count(String properties[]) {\r
+       return count(true, false, properties);\r
+    }\r
+\r
+    /**\r
+     * Count the number or row with columns matching the value of the\r
+     * bean properties.\r
+     * @param all (join with associated beans?)\r
+     * @return an int\r
+     */\r
+    public int count(boolean all) {\r
+       return count(all, false, null);\r
+    }\r
+\r
+    /**\r
+     * Count the number or row with columns matching the value of the\r
+     * bean properties\r
+     * @param all (join with associated beans?)\r
+     * @param distinct (SELECT DISTINCT?)\r
+     * @return an int\r
+     */\r
+    public int count(boolean all, boolean distinct) {\r
+       return count(all, distinct, null);\r
+    }\r
+\r
+    /**\r
+     * Count the number or row with columns matching the value of the\r
+     * given properties.\r
+     * @param all (join with associated beans?)\r
+     * @param distinct (SELECT DISTINCT?)\r
+     * @param properties The property names\r
+     * @return an int\r
+     */\r
+    public int count(boolean all, boolean distinct, String properties[]) {\r
+       String sql = computeSQLCount(all, distinct, properties);\r
+       try {\r
+           executeSQLQuery(sql);\r
+           if (result.first()) {\r
+               return result.getInt(1);\r
+           } else {\r
+               return 0;\r
+           }\r
+       } catch (SQLException ex) {\r
+           System.out.println("SQL STATE: "+ex.getSQLState());\r
+           ex.printStackTrace();\r
+           return 0;\r
+       } finally {\r
+           result     = null;\r
+           beantables = null;\r
+       }\r
+       \r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     */\r
+    public void select() {\r
+       boolean array[] = { true };\r
+       select((String[])null, array, true, false);\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param all join with attached beans? (default is true)\r
+     */\r
+    public void select(boolean all) {\r
+       boolean array[] = { true };\r
+       select((String[])null, array, all, false);\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param orderby orderby rule\r
+     */\r
+    public void select(String orderby) {\r
+       String array[] = { orderby };\r
+       boolean arrayb[] = { true };\r
+       select(array, arrayb, true, false);\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param orderby orderby rule\r
+     * @param asc boolean if true orderby is ASC if false it it\r
+     * DESC (relative to the orderby[] parameter)\r
+     * @param all join with attached beans? (default is true)\r
+     */\r
+    public void select(String orderby, \r
+                      boolean asc, \r
+                      boolean all)\r
+    {\r
+       String array[] = { orderby };\r
+       boolean arrayb[] = { asc };\r
+       select(array, arrayb, all, false);\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param orderby orderby rule\r
+     * @param asc boolean if true orderby is ASC if false it it\r
+     * DESC (relative to the orderby[] parameter)\r
+     * @param all join with attached beans? (default is true)\r
+     * @param distinct if true, result won't have duplicate row (default is \r
+     * false)\r
+     */\r
+    public void select(String orderby, \r
+                      boolean asc, \r
+                      boolean all, \r
+                      boolean distinct) \r
+    {\r
+       String array[] = { orderby };\r
+       boolean arrayb[] = { asc };\r
+       select(array, arrayb, all, distinct);\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param orderby array of orderby rules (ASC by default)\r
+     */\r
+    public void select(String orderby[]) {\r
+       boolean array[] = { true };\r
+       select(orderby, array, true, false);\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param orderby array of orderby rules\r
+     * @param asc array of boolean if true orderby is ASC if false it it\r
+     * DESC (relative to the orderby[] parameter)\r
+     * @param all join with attached beans? (default is true)\r
+     * @param distinct if true, result won't have duplicate row (default is \r
+     * false)\r
+     */\r
+    public void select(String orderby[],\r
+                      boolean asc[],\r
+                      boolean all,\r
+                      boolean distinct) \r
+    {\r
+       String select = (distinct) ? "DISTINCT *" : "*";\r
+       String sql = computeSQLSelect(orderby, asc, all, select);\r
+       try {\r
+           executeSQLQuery(sql);\r
+       } catch (SQLException ex) {\r
+           System.out.println("SQL STATE: "+ex.getSQLState());\r
+           ex.printStackTrace();\r
+           result = null;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update the beans properties.\r
+     * @param orderby array of orderby rules\r
+     * @param asc array of boolean if true orderby is ASC if false it it\r
+     * DESC (relative to the orderby[] parameter)\r
+     * @param all join with attached beans? (default is true)\r
+     * @param distinct if true, result won't have duplicate row (default is \r
+     * @param toselect array of columns name to select\r
+     * false)\r
+     */\r
+    public void select(String orderby[],\r
+                      boolean asc[],\r
+                      boolean all,\r
+                      boolean distinct,\r
+                      String toselect[]) \r
+    {\r
+       String query = null;\r
+       if (toselect != null) {\r
+           StringBuffer buffer = new StringBuffer();\r
+           for (int i = 0 ; i < toselect.length ; i++) { \r
+               if (i != 0) {\r
+                   buffer.append(", ");\r
+               }\r
+               buffer.append(toselect[i]).append(" ");\r
+           }\r
+           query = buffer.toString();\r
+       } else {\r
+           query = "*";\r
+       }\r
+       String select = (distinct) ? "DISTINCT "+query : query;\r
+       String sql = computeSQLSelect(orderby, asc, all, select);\r
+       try {\r
+           executeSQLQuery(sql);\r
+       } catch (SQLException ex) {\r
+           System.out.println("SQL STATE: "+ex.getSQLState());\r
+           ex.printStackTrace();\r
+           result = null;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Perform a sql select to update only the given columns. (distinct flag is\r
+     * set as true.\r
+     * @param column the bean property to update\r
+     */\r
+    public void selectDistinct(String column) {\r
+       boolean array[] = { true };\r
+        String order[] = { column };\r
+       String sql = computeSQLSelect(order, array, false, "DISTINCT "+column);\r
+       try {\r
+           executeSQLQuery(sql);\r
+       } catch (SQLException ex) {\r
+           System.out.println("SQL STATE: "+ex.getSQLState());\r
+           ex.printStackTrace();\r
+           result = null;\r
+       }\r
+    }\r
+\r
+    private void setPriority(int p) {\r
+       int idx = 0;\r
+       while ((idx < priority.length) && (priority[idx] != NOTHING)) {\r
+           if (priority[idx] == p) { // already set\r
+               return;\r
+           }\r
+           idx++;\r
+       }\r
+       priority[idx] = p;\r
+    }\r
+\r
+    /**\r
+     * USE THIS METHOD ONLY BEFORE SELECT QUERIES.\r
+     * This will produce a select query with an INTERSECT statement in it\r
+     * using the values of the given bean.\r
+     * @param ibean the intersect bean\r
+     */\r
+    public JdbcBeanSerializer intersect(JdbcBeanInterface ibean) {\r
+       setPriority(INTERSECT);\r
+       intersect_serializer = ibean.getSerializer();\r
+       return intersect_serializer;\r
+    }\r
+\r
+    /**\r
+     * USE THIS METHOD ONLY BEFORE QUERIES.\r
+     * This will produce a select query with an UNION statement in it\r
+     * using the values of the given bean.\r
+     * @param ibean the intersect bean\r
+     */\r
+    public JdbcBeanSerializer union(JdbcBeanInterface ubean) {\r
+       setPriority(UNION);\r
+       union_serializer = ubean.getSerializer();\r
+       return union_serializer;\r
+    }\r
+\r
+    /**\r
+     * USE THIS METHOD ONLY BEFORE SELECT QUERIES.\r
+     * This will produce a select query with an EXCEPT statement in it\r
+     * using the values of the given bean.\r
+     * @param ibean the intersect bean\r
+     */\r
+    public JdbcBeanSerializer except(JdbcBeanInterface ebean) {\r
+       setPriority(EXCEPT);\r
+       except_serializer = ebean.getSerializer();\r
+       return except_serializer;\r
+    }\r
+\r
+    /**\r
+     * Remove the intersect bean\r
+     */\r
+    public void removeIntersectBean() {\r
+       intersect_serializer = null;\r
+       \r
+    }\r
+\r
+    /**\r
+     * Remove the union bean\r
+     */\r
+    public void removeUnionBean() {\r
+       union_serializer = null;\r
+       \r
+    }\r
+\r
+    /**\r
+     * Remove the except bean\r
+     */\r
+    public void removeExceptBean() {\r
+       except_serializer = null;\r
+    }\r
+\r
+    /**\r
+     * Insert the current bean values in the associated table.\r
+     * @return false if the INSERT request failed.\r
+     */\r
+    public boolean insert() {\r
+       if (! isModified()) { // nothing new to insert\r
+           return false;\r
+       }\r
+       JdbcBeanInterface beans[] = getJdbcBeans();\r
+       for (int i = 0 ; i < beans.length ; i++) { \r
+           JdbcBeanInterface jbean = beans[i];\r
+           JdbcBeanSerializer ser  = jbean.getSerializer();\r
+           if (ser.isModified()) {\r
+               // insert associated bean\r
+               ser.insert();\r
+               // update our foreign key\r
+               updateForeignKeys(jbean); \r
+           }\r
+       }\r
+       if (! bean.getReadOnly()) {\r
+           // ok insert ourself now\r
+           String request = computeSQLInsert();\r
+           try {\r
+               // insert (could fail without being critical)\r
+               // ie: when the row is already in the table\r
+               executeSQLUpdate(request);\r
+           } catch (SQLException ex) {\r
+               System.err.println(ex.getMessage());\r
+               return false;\r
+           }\r
+       }\r
+       // update value automatically generated by the DB (index, ...)\r
+       select(false);\r
+       try {\r
+           if (result == null) {\r
+               return false;\r
+           }\r
+           if (result.first()) {\r
+               return updateProperties(false);\r
+           } else {\r
+               return false;\r
+           }\r
+       } catch (SQLException ex) {\r
+           ex.printStackTrace();\r
+           return false;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Update the row relative to our bean.\r
+     * @param primarykey The primary key of the SQL table\r
+     * @return false if the UPDATE request failed.\r
+     */\r
+    public boolean update(String primarykey) {\r
+       String array[] = { primarykey };\r
+       return update(array);\r
+    }\r
+\r
+    /**\r
+     * Update the row relative to our bean.\r
+     * @param primarykey The primary key of the SQL table\r
+     * @return false if the UPDATE request failed.\r
+     */\r
+    public boolean update(String primarykeys[]) {\r
+       if (! isModified()) { // noting to update\r
+           return false;\r
+       }\r
+       String sql = computeSQLUpdate(primarykeys);\r
+       try {\r
+           int nb = executeSQLUpdate(sql);\r
+           return (nb > 0);\r
+       } catch (SQLException ex) {\r
+           ex.printStackTrace();\r
+       }\r
+       return false;\r
+    }\r
+\r
+    /**\r
+     * Delete the row relative to the current bean.\r
+     * @return false if the DELETE request failed.\r
+     */\r
+    public boolean delete() {\r
+       if (bean.getReadOnly()) {\r
+           return false;\r
+       }\r
+       String sql = computeSQLDelete();\r
+       try {\r
+           int nb = executeSQLUpdate(sql);\r
+           return (nb > 0);\r
+       } catch (SQLException ex) {\r
+           System.out.println("SQL STATE: "+ex.getSQLState());\r
+           ex.printStackTrace();\r
+           result = null;\r
+           return false; // FIXME VERIFY\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Go to the first row\r
+     * @return false if there is no first row\r
+     */\r
+    public boolean first() {\r
+       try {\r
+           if (result == null) {\r
+               return false;\r
+           }\r
+           if (result.first()) {\r
+               return updateProperties();\r
+           }\r
+       } catch (SQLException ex) { }\r
+       return false;\r
+    }\r
+\r
+    /**\r
+     * Update our bean with the value of the next row\r
+     * @return false if there is no more row\r
+     */\r
+    public boolean next() {\r
+       try {\r
+           if (result == null) {\r
+               return false;\r
+           }\r
+           if (result.next()) {\r
+               return updateProperties();\r
+           }\r
+       } catch (SQLException ex) { }\r
+       return false;\r
+    }\r
+\r
+    /**\r
+     * Did we reached the last row?\r
+     * @return true if the last row is reached\r
+     */\r
+    public boolean isLast() {\r
+       try {\r
+           if (result == null) {\r
+               return true;\r
+           }\r
+           return result.isLast();\r
+       } catch (SQLException ex) { }\r
+       return true;\r
+    }\r
+\r
+    /**\r
+     * Clean cached properties (relative to our bean)\r
+     */\r
+    public void clean() {\r
+       result = null;\r
+       PropertyCache.removeProperties(bean);\r
+        markModified(false);\r
+    }\r
+\r
+    /**\r
+     * Restore default value except for JdbcBean properties.\r
+     */\r
+    public void initBean() {\r
+       try {\r
+           BeanInfo info = Introspector.getBeanInfo(bean.getClass());\r
+           PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if ((! pd.isHidden()) && \r
+                   (! JdbcBeanUtil.isJdbcBean(pd.getPropertyType()))) {\r
+                   Method getter = pd.getReadMethod();\r
+                   Method setter = pd.getWriteMethod();\r
+                   Object value  = null;\r
+                   if ((getter != null) && (setter != null)) {\r
+                       try {\r
+                           value = getter.invoke(bean.getDefault(), \r
+                                                 (Object [])null);\r
+                           Object array[] = { value };\r
+                           setter.invoke(bean, array);\r
+                       } catch (IllegalAccessException ex) {\r
+                           ex.printStackTrace();\r
+                           // nothing to do\r
+                       } catch (InvocationTargetException ex) {\r
+                           ex.printStackTrace();\r
+                           // still nothing to do\r
+                       } catch (IllegalArgumentException ex) {\r
+                           ex.printStackTrace();\r
+                           // nothing to do\r
+                       }\r
+                   }  \r
+               }\r
+           }\r
+           clean();\r
+       } catch (IntrospectionException ex) {\r
+       }\r
+    }\r
+\r
+    private int findColumn(Vector tables, ResultSet result, String colname) \r
+       throws SQLException\r
+    {\r
+       String            tablename = bean.getJdbcTable();\r
+       ResultSetMetaData metadata  = result.getMetaData();\r
+       int cpt = 0;\r
+       if (metadata.getTableName(1).length() > 0) { // applicable\r
+           for (int i = 0 ; i < metadata.getColumnCount() ; i++) {\r
+               String coltable = metadata.getTableName(i);\r
+               if ((metadata.getTableName(i).equalsIgnoreCase(tablename)) &&\r
+                   (metadata.getColumnName(i).equalsIgnoreCase(colname))) {\r
+                   return i;\r
+               }\r
+           }\r
+       } else { // not applicable\r
+           // search all columns matching the given name\r
+           Vector indexes = new Vector();\r
+           try {\r
+               for (int i = 1 ; i <= metadata.getColumnCount() ; i++) {\r
+                   if (metadata.getColumnName(i).equals(colname)) {\r
+                       indexes.addElement(new Integer(i));\r
+                   }\r
+               }\r
+           } catch (Exception ex) {\r
+               ex.printStackTrace();\r
+           }\r
+           // find the good one\r
+           if (indexes.size() == 0) {\r
+               return -1;\r
+           } else if (indexes.size() == 1) {\r
+               return ((Integer)indexes.elementAt(0)).intValue();\r
+           } else {\r
+               int idxidx = 0;\r
+               for (int i = 0 ; i < tables.size() ; i++) {\r
+                   JdbcBeanInterface jbean = \r
+                       (JdbcBeanInterface)tables.elementAt(i);\r
+                   if (jbean == bean) {\r
+                       return ((Integer)indexes.elementAt(idxidx)).intValue();\r
+                   }\r
+                   if (jbean.getSerializer().getPropertyDescriptor(colname) !=\r
+                       null) {\r
+                       // exists in this table\r
+                       idxidx++;\r
+                   }\r
+               }\r
+           }\r
+       }\r
+       return -1;\r
+    }\r
+\r
+    private boolean updateProperties() {\r
+       return updateProperties(this.beantables, this.result, true);\r
+    }\r
+\r
+    private boolean updateProperties(boolean all) {\r
+       return updateProperties(this.beantables, this.result, all);\r
+    }\r
+\r
+    private boolean updateProperties(Vector tables,\r
+                                    ResultSet result) \r
+    {\r
+       return updateProperties(tables, result, true);\r
+    }\r
+\r
+    private boolean updateProperties(Vector tables,\r
+                                    ResultSet result, \r
+                                    boolean all) \r
+    {\r
+       try {\r
+           BeanInfo info = Introspector.getBeanInfo(bean.getClass());\r
+           PropertyDescriptor pds[] = info.getPropertyDescriptors();\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if (! pd.isHidden()) {\r
+                   try {\r
+                       int idx = findColumn(tables, result, pd.getName());\r
+                       if (idx != -1) {\r
+                           Object value  = result.getObject(idx);\r
+                           Class propertyclass = pd.getPropertyType();\r
+                           value = SQL.getMatchingValue(propertyclass, value);\r
+                           if (value != null) {\r
+                               Object values[] = { value };\r
+                               Method setter = pd.getWriteMethod();\r
+                               if (setter != null) {\r
+                                   try {\r
+                                       setter.invoke(bean, values);\r
+                                   } catch (IllegalAccessException ex) {\r
+                                       ex.printStackTrace();\r
+                                       // nothing to do\r
+                                   } catch (InvocationTargetException ex) {\r
+                                       ex.printStackTrace();\r
+                                       // still nothing to do\r
+                                   } catch (IllegalArgumentException ex) {\r
+                                       ex.printStackTrace();\r
+                                       // nothing to do\r
+                                   }\r
+                               }\r
+                           } else {\r
+                               // default value\r
+                               Method getter = pd.getReadMethod();\r
+                               Method setter = pd.getWriteMethod();\r
+                               if ((getter != null) && (setter != null)) {\r
+                                   try {\r
+                                       value = \r
+                                           getter.invoke(bean.getDefault(),\r
+                                                         (Object [])null);\r
+                                       Object array[] = { value };\r
+                                       setter.invoke(bean, array);\r
+                                   } catch (IllegalAccessException ex) {\r
+                                       ex.printStackTrace();\r
+                                       // nothing to do\r
+                                   } catch (InvocationTargetException ex) {\r
+                                       ex.printStackTrace();\r
+                                       // still nothing to do\r
+                                   } catch (IllegalArgumentException ex) {\r
+                                       ex.printStackTrace();\r
+                                       // nothing to do\r
+                                   }\r
+                               }\r
+                           }\r
+                       }\r
+                   } catch (SQLException ex) { // not found\r
+                       // nothing to do\r
+                   }\r
+               }\r
+           }\r
+           if (all) {\r
+               // update the associated beans\r
+               JdbcBeanInterface beans[] = getJdbcBeans();\r
+               for (int i = 0 ; i < beans.length ; i++) {\r
+                   beans[i].getSerializer().updateProperties(tables, result);\r
+               }\r
+           }\r
+           markModified(false);\r
+           return true;\r
+       } catch (IntrospectionException ex) {\r
+           return false;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Update our bean property with the given bean property \r
+     * (must be an instance of the same class).\r
+     * @param ubean the bean to get new properties\r
+     */\r
+    public void updateProperties(JdbcBeanInterface ubean) {\r
+       if (ubean.getClass() != bean.getClass()) {\r
+           return;\r
+       }\r
+       try {\r
+           BeanInfo bi = Introspector.getBeanInfo(bean.getClass());\r
+           PropertyDescriptor pds[] = bi.getPropertyDescriptors();\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if (! pd.isHidden()) {\r
+                   try {\r
+                       Method reader = pd.getReadMethod();\r
+                       Method writer = pd.getWriteMethod();\r
+                       Object value  = reader.invoke(ubean, \r
+                                                     (Object [])null);\r
+                       if (value != null) {\r
+                           Object array[] = { value };\r
+                           writer.invoke(bean, array);\r
+                       }\r
+                   } catch (IllegalAccessException ex) {\r
+                       ex.printStackTrace();\r
+                   } catch (InvocationTargetException ex) {\r
+                       ex.printStackTrace();\r
+                   }\r
+               }\r
+           }\r
+       } catch (IntrospectionException ex) {\r
+           // nothing to do\r
+       }\r
+    }\r
+\r
+    private PropertyDescriptor getPropertyDescriptor(String property) {\r
+       try {\r
+           BeanInfo bi = Introspector.getBeanInfo(bean.getClass());\r
+           PropertyDescriptor pds[] = bi.getPropertyDescriptors();\r
+           for (int i = 0 ; i < pds.length ; i++) {\r
+               PropertyDescriptor pd = pds[i];\r
+               if (pd.getName().equals(property)) {\r
+                   return pd;\r
+               }\r
+           }\r
+           return null;\r
+       } catch (IntrospectionException ex) {\r
+           return null;\r
+       }\r
+    }\r
+\r
+    private void updateForeignKeys(JdbcBeanInterface jbean) {\r
+       String keys[] = getForeignKeys(jbean.getClass(), bean.getClass());\r
+       JdbcBeanSerializer ser = jbean.getSerializer();\r
+       for (int i = 0 ; i < keys.length ; i++) { \r
+           try {\r
+               String             key    = keys[i];\r
+               PropertyDescriptor wkeypd = getPropertyDescriptor(key);\r
+               PropertyDescriptor rkeypd = ser.getPropertyDescriptor(key);\r
+               Method             reader = rkeypd.getReadMethod();\r
+               Method             writer = wkeypd.getWriteMethod();\r
+               Object value = reader.invoke(jbean, (Object [])null);\r
+               if (value != null) {\r
+                   Object array[] = { value };\r
+                   writer.invoke(bean, array);\r
+               }\r
+           } catch (IllegalAccessException ex) {\r
+               ex.printStackTrace();\r
+           } catch (InvocationTargetException ex) {\r
+               ex.printStackTrace();\r
+           }\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Called by the Garbage Collector.\r
+     */\r
+    protected void finalize() \r
+       throws Throwable\r
+    {\r
+       // cleanup (static) cached properties\r
+       PropertyCache.removeProperties(this.bean);\r
+    }\r
+\r
+    /**\r
+     * Constructor\r
+     * @param bean the JdbcBean to serialize\r
+     */ \r
+    public JdbcBeanSerializer(JdbcBeanInterface bean) {\r
+       this.bean        = bean;\r
+       bean.addPropertyChangeListener(this);\r
+    }\r
+\r
+}\r
+\r
+\r