--- /dev/null
+// 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