merged with 1.1
[mir.git] / source / mir / storage / Database.java
index 6b9cd35..468c06b 100755 (executable)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (C) 2001, 2002  The Mir-coders group\r
+ * Copyright (C) 2001, 2002 The Mir-coders group\r
  *\r
  * This file is part of Mir.\r
  *\r
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
  *\r
  * In addition, as a special exception, The Mir-coders gives permission to link\r
- * the code of this program with the com.oreilly.servlet library, any library\r
- * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
- * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
- * the above that use the same license as the above), and distribute linked\r
- * combinations including the two.  You must obey the GNU General Public\r
- * License in all respects for all of the code used other than the above\r
- * mentioned libraries.  If you modify this file, you may extend this exception\r
- * to your version of the file, but you are not obligated to do so.  If you do\r
- * not wish to do so, delete this exception statement from your version.\r
+ * the code of this program with  any library licensed under the Apache Software License,\r
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
+ * (or with modified versions of the above that use the same license as the above),\r
+ * and distribute linked combinations including the two.  You must obey the\r
+ * GNU General Public License in all respects for all of the code used other than\r
+ * the above mentioned libraries.  If you modify this file, you may extend this\r
+ * exception to your version of the file, but you are not obligated to do so.\r
+ * If you do not wish to do so, delete this exception statement from your version.\r
  */\r
 package mir.storage;\r
 \r
 import java.io.IOException;\r
 import java.io.InputStreamReader;\r
-\r
 import java.sql.Connection;\r
 import java.sql.PreparedStatement;\r
 import java.sql.ResultSet;\r
@@ -40,40 +38,31 @@ import java.sql.ResultSetMetaData;
 import java.sql.SQLException;\r
 import java.sql.Statement;\r
 import java.sql.Timestamp;\r
-\r
 import java.text.ParseException;\r
 import java.text.SimpleDateFormat;\r
-\r
 import java.util.ArrayList;\r
-import java.util.List;\r
 import java.util.Calendar;\r
 import java.util.GregorianCalendar;\r
 import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.TimeZone;\r
+import java.util.Vector;\r
 \r
 import com.codestudio.util.SQLManager;\r
-\r
-import freemarker.template.SimpleHash;\r
-import freemarker.template.SimpleList;\r
-\r
 import mir.config.MirPropertiesConfiguration;\r
-\r
 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;\r
-\r
 import mir.entity.Entity;\r
 import mir.entity.EntityList;\r
 import mir.entity.StorableObjectEntity;\r
-\r
 import mir.log.LoggerWrapper;\r
-\r
-import mir.misc.HTMLTemplateProcessor;\r
 import mir.misc.StringUtil;\r
-\r
 import mir.storage.store.ObjectStore;\r
 import mir.storage.store.StorableObject;\r
 import mir.storage.store.StoreContainerType;\r
 import mir.storage.store.StoreIdentifier;\r
 import mir.storage.store.StoreUtil;\r
-\r
 import mir.util.JDBCStringRoutines;\r
 \r
 \r
@@ -85,7 +74,7 @@ import mir.util.JDBCStringRoutines;
  * Treiber, Host, User und Passwort, ueber den der Zugriff auf die\r
  * Datenbank erfolgt.\r
  *\r
- * @version $Id: Database.java,v 1.33 2003/02/20 16:05:33 zapata Exp $\r
+ * @version $Id: Database.java,v 1.46 2003/09/03 18:29:03 zapata Exp $\r
  * @author rk\r
  *\r
  */\r
@@ -94,7 +83,7 @@ public class Database implements StorageObject {
   private static Class STORABLE_OBJECT_ENTITY_CLASS = mir.entity.StorableObjectEntity.class;\r
 \r
 \r
-  private static SimpleHash POPUP_EMPTYLINE = new SimpleHash();\r
+  private static Map POPUP_EMPTYLINE = new HashMap();\r
   protected static final ObjectStore o_store = ObjectStore.getInstance();\r
   private static final int _millisPerHour = 60 * 60 * 1000;\r
   private static final int _millisPerMinute = 60 * 1000;\r
@@ -118,20 +107,23 @@ public class Database implements StorageObject {
   protected ArrayList metadataNotNullFields;\r
   protected int[] metadataTypes;\r
   protected Class theEntityClass;\r
-  protected StorageObject myselfDatabase;\r
-  protected SimpleList popupCache = null;\r
+  protected List popupCache = null;\r
   protected boolean hasPopupCache = false;\r
-  protected SimpleHash hashCache = null;\r
+  protected Map hashCache = null;\r
   protected boolean hasTimestamp = true;\r
   private String database_driver;\r
   private String database_url;\r
   private int defaultLimit;\r
-  protected DatabaseAdaptor theAdaptor;\r
-  private SimpleDateFormat _dateFormatterOut =\r
-    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");\r
-  private SimpleDateFormat _dateFormatterIn =\r
-    new SimpleDateFormat("yyyy-MM-dd HH:mm");\r
-  private Calendar _cal = new GregorianCalendar();\r
+\r
+  TimeZone timezone;\r
+  SimpleDateFormat internalDateFormat;\r
+  SimpleDateFormat userInputDateFormat;\r
+/*\r
+  private SimpleDateFormat _dateFormatterOut;\r
+  private SimpleDateFormat _dateFormatterIn;\r
+  _dateFormatterOut = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");\r
+  _dateFormatterIn = new SimpleDateFormat("yyyy-MM-dd HH:mm");\r
+*/\r
 \r
   /**\r
    * Kontruktor bekommt den Filenamen des Konfigurationsfiles ?bergeben.\r
@@ -151,13 +143,19 @@ public class Database implements StorageObject {
       throw new StorageObjectFailure(e);\r
     }\r
     logger = new LoggerWrapper("Database");\r
+    timezone = TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone"));\r
+    internalDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");\r
+    internalDateFormat.setTimeZone(timezone);\r
+\r
+    userInputDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");\r
+    userInputDateFormat.setTimeZone(timezone);\r
+\r
 \r
     String theAdaptorName = configuration.getString("Database.Adaptor");\r
     defaultLimit = Integer.parseInt(configuration.getString("Database.Limit"));\r
 \r
     try {\r
       theEntityClass = GENERIC_ENTITY_CLASS;\r
-      theAdaptor = (DatabaseAdaptor) Class.forName(theAdaptorName).newInstance();\r
     }\r
     catch (Throwable e) {\r
       logger.error("Error in Database() constructor with " + theAdaptorName + " -- " + e.getMessage());\r
@@ -214,7 +212,8 @@ public class Database implements StorageObject {
   public String getCoreTable() {\r
     if (theCoreTable != null) {\r
       return theCoreTable;\r
-    } else {\r
+    }\r
+    else {\r
       return theTable;\r
     }\r
   }\r
@@ -272,107 +271,113 @@ public class Database implements StorageObject {
     if (rs != null) {\r
       try {\r
         switch (aType) {\r
-        case java.sql.Types.BIT:\r
-          outValue = (rs.getBoolean(valueIndex) == true) ? "1" : "0";\r
+          case java.sql.Types.BIT:\r
+            outValue = (rs.getBoolean(valueIndex) == true) ? "1" : "0";\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.INTEGER:\r
-        case java.sql.Types.SMALLINT:\r
-        case java.sql.Types.TINYINT:\r
-        case java.sql.Types.BIGINT:\r
+          case java.sql.Types.INTEGER:\r
+          case java.sql.Types.SMALLINT:\r
+          case java.sql.Types.TINYINT:\r
+          case java.sql.Types.BIGINT:\r
 \r
-          int out = rs.getInt(valueIndex);\r
+            int out = rs.getInt(valueIndex);\r
 \r
-          if (!rs.wasNull()) {\r
-            outValue = new Integer(out).toString();\r
-          }\r
+            if (!rs.wasNull()) {\r
+              outValue = new Integer(out).toString();\r
+            }\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.NUMERIC:\r
+          case java.sql.Types.NUMERIC:\r
 \r
-          /** @todo Numeric can be float or double depending upon\r
-           *  metadata.getScale() / especially with oracle */\r
-          long outl = rs.getLong(valueIndex);\r
+            /** @todo Numeric can be float or double depending upon\r
+             *  metadata.getScale() / especially with oracle */\r
+            long outl = rs.getLong(valueIndex);\r
 \r
-          if (!rs.wasNull()) {\r
-            outValue = new Long(outl).toString();\r
-          }\r
+            if (!rs.wasNull()) {\r
+              outValue = new Long(outl).toString();\r
+            }\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.REAL:\r
+          case java.sql.Types.REAL:\r
 \r
-          float tempf = rs.getFloat(valueIndex);\r
+            float tempf = rs.getFloat(valueIndex);\r
 \r
-          if (!rs.wasNull()) {\r
-            tempf *= 10;\r
-            tempf += 0.5;\r
+            if (!rs.wasNull()) {\r
+              tempf *= 10;\r
+              tempf += 0.5;\r
 \r
-            int tempf_int = (int) tempf;\r
-            tempf = (float) tempf_int;\r
-            tempf /= 10;\r
-            outValue = "" + tempf;\r
-            outValue = outValue.replace('.', ',');\r
-          }\r
+              int tempf_int = (int) tempf;\r
+              tempf = (float) tempf_int;\r
+              tempf /= 10;\r
+              outValue = "" + tempf;\r
+              outValue = outValue.replace('.', ',');\r
+            }\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.DOUBLE:\r
+          case java.sql.Types.DOUBLE:\r
 \r
-          double tempd = rs.getDouble(valueIndex);\r
+            double tempd = rs.getDouble(valueIndex);\r
 \r
-          if (!rs.wasNull()) {\r
-            tempd *= 10;\r
-            tempd += 0.5;\r
+            if (!rs.wasNull()) {\r
+              tempd *= 10;\r
+              tempd += 0.5;\r
 \r
-            int tempd_int = (int) tempd;\r
-            tempd = (double) tempd_int;\r
-            tempd /= 10;\r
-            outValue = "" + tempd;\r
-            outValue = outValue.replace('.', ',');\r
-          }\r
+              int tempd_int = (int) tempd;\r
+              tempd = (double) tempd_int;\r
+              tempd /= 10;\r
+              outValue = "" + tempd;\r
+              outValue = outValue.replace('.', ',');\r
+            }\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.CHAR:\r
-        case java.sql.Types.VARCHAR:\r
-        case java.sql.Types.LONGVARCHAR:\r
-          outValue = rs.getString(valueIndex);\r
+          case java.sql.Types.CHAR:\r
+          case java.sql.Types.VARCHAR:\r
+          case java.sql.Types.LONGVARCHAR:\r
+            outValue = rs.getString(valueIndex);\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.LONGVARBINARY:\r
-          outValue = rs.getString(valueIndex);\r
+          case java.sql.Types.LONGVARBINARY:\r
+            outValue = rs.getString(valueIndex);\r
 \r
-          break;\r
+            break;\r
 \r
-        case java.sql.Types.TIMESTAMP:\r
+          case java.sql.Types.TIMESTAMP:\r
 \r
-          // it's important to use Timestamp here as getting it\r
-          // as a string is undefined and is only there for debugging\r
-          // according to the API. we can make it a string through formatting.\r
-          // -mh\r
-          Timestamp timestamp = (rs.getTimestamp(valueIndex));\r
+            // it's important to use Timestamp here as getting it\r
+            // as a string is undefined and is only there for debugging\r
+            // according to the API. we can make it a string through formatting.\r
+            // -mh\r
+            Timestamp timestamp = (rs.getTimestamp(valueIndex));\r
 \r
-          if (!rs.wasNull()) {\r
-            java.util.Date date = new java.util.Date(timestamp.getTime());\r
-            outValue = _dateFormatterOut.format(date);\r
-            _cal.setTime(date);\r
+            if (!rs.wasNull()) {\r
+              java.util.Date date = new java.util.Date(timestamp.getTime());\r
 \r
-            int offset =\r
-              _cal.get(Calendar.ZONE_OFFSET) + _cal.get(Calendar.DST_OFFSET);\r
-            String tzOffset =\r
-              StringUtil.zeroPaddingNumber(offset / _millisPerHour, 2, 2);\r
-            outValue = outValue + "+" + tzOffset;\r
-          }\r
+              Calendar calendar = new GregorianCalendar();\r
+              calendar.setTime(date);\r
+              calendar.setTimeZone(timezone);\r
+              outValue = internalDateFormat.format(date);\r
 \r
-          break;\r
+              int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);\r
+              String tzOffset = StringUtil.zeroPaddingNumber(Math.abs(offset) / _millisPerHour, 2, 2);\r
+\r
+              if (offset<0)\r
+                outValue = outValue + "-";\r
+              else\r
+                outValue = outValue + "+";\r
+              outValue = outValue + tzOffset;\r
+            }\r
 \r
-        default:\r
-          outValue = "<unsupported value>";\r
-          logger.warn( "Unsupported Datatype: at " + valueIndex + " (" + aType + ")");\r
+            break;\r
+\r
+          default:\r
+            outValue = "<unsupported value>";\r
+            logger.warn("Unsupported Datatype: at " + valueIndex + " (" + aType + ")");\r
         }\r
       } catch (SQLException e) {\r
         throw new StorageObjectFailure("Could not get Value out of Resultset -- ",\r
@@ -485,8 +490,7 @@ public class Database implements StorageObject {
    * @return EntityList mit den gematchten Entities\r
    * @exception StorageObjectException\r
    */\r
-  public EntityList selectByWhereClause(String where)\r
-    throws StorageObjectFailure {\r
+  public EntityList selectByWhereClause(String where) throws StorageObjectFailure {\r
     return selectByWhereClause(where, 0);\r
   }\r
 \r
@@ -499,8 +503,7 @@ public class Database implements StorageObject {
    * @return EntityList mit den gematchten Entities\r
    * @exception StorageObjectException\r
    */\r
-  public EntityList selectByWhereClause(String whereClause, int offset)\r
-    throws StorageObjectFailure {\r
+  public EntityList selectByWhereClause(String whereClause, int offset) throws StorageObjectFailure {\r
     return selectByWhereClause(whereClause, null, offset);\r
   }\r
 \r
@@ -514,8 +517,7 @@ public class Database implements StorageObject {
    * @return EntityList mit den gematchten Entities\r
    * @exception StorageObjectException\r
    */\r
-  public EntityList selectByWhereClause(String where, String order)\r
-    throws StorageObjectFailure {\r
+  public EntityList selectByWhereClause(String where, String order) throws StorageObjectFailure {\r
     return selectByWhereClause(where, order, 0);\r
   }\r
 \r
@@ -529,29 +531,28 @@ public class Database implements StorageObject {
    * @return EntityList mit den gematchten Entities\r
    * @exception StorageObjectException\r
    */\r
-  public EntityList selectByWhereClause(String whereClause, String orderBy,\r
-    int offset) throws StorageObjectFailure {\r
+  public EntityList selectByWhereClause(String whereClause, String orderBy, int offset) throws StorageObjectFailure {\r
     return selectByWhereClause(whereClause, orderBy, offset, defaultLimit);\r
   }\r
 \r
   /**\r
    * select-Operator liefert eine EntityListe mit den gematchten Datens?tzen zur?ck.\r
-   * @param wc where-Clause\r
-   * @param ob orderBy-Clause\r
+   * @param aWhereClause where-Clause\r
+   * @param anOrderByClause orderBy-Clause\r
    * @param offset ab welchem Datensatz\r
    * @param limit wieviele Datens?tze\r
    * @return EntityList mit den gematchten Entities\r
    * @exception StorageObjectException\r
    */\r
-  public EntityList selectByWhereClause(String wc, String ob, int offset,\r
-    int limit) throws StorageObjectFailure {\r
+  public EntityList selectByWhereClause(String aWhereClause, String anOrderByClause,\r
+            int offset, int limit) throws StorageObjectFailure {\r
+\r
     // check o_store for entitylist\r
     if (StoreUtil.implementsStorableObject(theEntityClass)) {\r
       StoreIdentifier search_sid =\r
-        new StoreIdentifier(theEntityClass,\r
-          StoreContainerType.STOC_TYPE_ENTITYLIST,\r
-          StoreUtil.getEntityListUniqueIdentifierFor(theTable, wc, ob, offset,\r
-            limit));\r
+          new StoreIdentifier(\r
+            theEntityClass, StoreContainerType.STOC_TYPE_ENTITYLIST,\r
+            StoreUtil.getEntityListUniqueIdentifierFor(theTable, aWhereClause, anOrderByClause, offset, limit));\r
       EntityList hit = (EntityList) o_store.use(search_sid);\r
 \r
       if (hit != null) {\r
@@ -573,8 +574,8 @@ public class Database implements StorageObject {
 \r
     /** @todo count sql string should only be assembled if we really count\r
      *  see below at the end of method //rk */\r
-    if ((wc != null) && (wc.length() == 0)) {\r
-      wc = null;\r
+    if ((aWhereClause != null) && (aWhereClause.trim().length() == 0)) {\r
+      aWhereClause = null;\r
     }\r
 \r
     StringBuffer countSql =\r
@@ -582,19 +583,17 @@ public class Database implements StorageObject {
     StringBuffer selectSql =\r
       new StringBuffer("select * from ").append(theTable);\r
 \r
-    if (wc != null) {\r
-      selectSql.append(" where ").append(wc);\r
-      countSql.append(" where ").append(wc);\r
+    if (aWhereClause != null) {\r
+      selectSql.append(" where ").append(aWhereClause);\r
+      countSql.append(" where ").append(aWhereClause);\r
     }\r
 \r
-    if ((ob != null) && !(ob.length() == 0)) {\r
-      selectSql.append(" order by ").append(ob);\r
+    if ((anOrderByClause != null) && !(anOrderByClause.trim().length() == 0)) {\r
+      selectSql.append(" order by ").append(anOrderByClause);\r
     }\r
 \r
-    if (theAdaptor.hasLimit()) {\r
-      if ((limit > -1) && (offset > -1)) {\r
-        selectSql.append(" LIMIT ").append(limit).append(" OFFSET ").append(offset);\r
-      }\r
+    if ((limit > -1) && (offset > -1)) {\r
+      selectSql.append(" LIMIT ").append(limit).append(" OFFSET ").append(offset);\r
     }\r
 \r
     // execute sql\r
@@ -624,9 +623,7 @@ public class Database implements StorageObject {
       }\r
 \r
       // making entitylist infos\r
-      if (!(theAdaptor.hasLimit())) {\r
-        count = offsetCount;\r
-      }\r
+      count = offsetCount;\r
 \r
       if (theReturnList != null) {\r
         // now we decide if we have to know an overall count...\r
@@ -653,8 +650,8 @@ public class Database implements StorageObject {
 \r
         theReturnList.setCount(count);\r
         theReturnList.setOffset(offset);\r
-        theReturnList.setWhere(wc);\r
-        theReturnList.setOrder(ob);\r
+        theReturnList.setWhere(aWhereClause);\r
+        theReturnList.setOrder(anOrderByClause);\r
         theReturnList.setStorage(this);\r
         theReturnList.setLimit(limit);\r
 \r
@@ -672,9 +669,11 @@ public class Database implements StorageObject {
           o_store.add(sid);\r
         }\r
       }\r
-    } catch (SQLException sqe) {\r
+    }\r
+    catch (SQLException sqe) {\r
       throwSQLException(sqe, "selectByWhereClause");\r
-    } finally {\r
+    }\r
+    finally {\r
       try {\r
         if (con != null) {\r
           freeConnection(con, stmt);\r
@@ -695,7 +694,7 @@ public class Database implements StorageObject {
   private Entity makeEntityFromResultSet(ResultSet rs)\r
     throws StorageObjectFailure {\r
     /** @todo OS: get Pkey from ResultSet and consult ObjectStore */\r
-    HashMap theResultHash = new HashMap();\r
+    Map theResultHash = new HashMap();\r
     String theResult = null;\r
     int theType;\r
     Entity returnEntity = null;\r
@@ -736,8 +735,8 @@ public class Database implements StorageObject {
 \r
       if (theEntityClass != null) {\r
         returnEntity = (Entity) theEntityClass.newInstance();\r
+        returnEntity.setStorage(this);\r
         returnEntity.setValues(theResultHash);\r
-        returnEntity.setStorage(myselfDatabase);\r
 \r
         if (returnEntity instanceof StorableObject) {\r
           logger.debug("CACHE: ( in) " + returnEntity.getId() + " :" + theTable);\r
@@ -746,13 +745,17 @@ public class Database implements StorageObject {
       } else {\r
         throwStorageObjectException("Internal Error: theEntityClass not set!");\r
       }\r
-    } catch (IllegalAccessException e) {\r
+    }\r
+    catch (IllegalAccessException e) {\r
       throwStorageObjectException("No access! -- " + e.getMessage());\r
-    } catch (IOException e) {\r
+    }\r
+    catch (IOException e) {\r
       throwStorageObjectException("IOException! -- " + e.getMessage());\r
-    } catch (InstantiationException e) {\r
+    }\r
+    catch (InstantiationException e) {\r
       throwStorageObjectException("No Instatiation! -- " + e.getMessage());\r
-    } catch (SQLException sqe) {\r
+    }\r
+    catch (SQLException sqe) {\r
       throwSQLException(sqe, "makeEntityFromResultSet");\r
 \r
       return null;\r
@@ -762,8 +765,7 @@ public class Database implements StorageObject {
   }\r
 \r
   /**\r
-   * insert-Operator: f?gt eine Entity in die Tabelle ein. Eine Spalte WEBDB_CREATE\r
-   * wird automatisch mit dem aktuellen Datum gefuellt.\r
+   * Inserts an entity into the database.\r
    *\r
    * @param theEntity\r
    * @return der Wert des Primary-keys der eingef?gten Entity\r
@@ -800,18 +802,20 @@ public class Database implements StorageObject {
           aValue = null;\r
 \r
           // exceptions\r
-          if (aField.equals("webdb_create") ||\r
-              aField.equals("webdb_lastchange")) {\r
+          if (!theEntity.hasValueForField(aField) && (\r
+              aField.equals("webdb_create") ||\r
+              aField.equals("webdb_lastchange"))) {\r
             aValue = "NOW()";\r
-          } else {\r
+          }\r
+          else {\r
             if ((streamedInput != null) && streamedInput.contains(aField)) {\r
               aValue = "?";\r
-            } else {\r
+            }\r
+            else {\r
               if (theEntity.hasValueForField(aField)) {\r
                 aValue =\r
                   "'" +\r
-                  JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(\r
-                      aField)) + "'";\r
+                   JDBCStringRoutines.escapeStringLiteral((String) theEntity.getValue(aField)) + "'";\r
               }\r
             }\r
           }\r
@@ -839,7 +843,7 @@ public class Database implements StorageObject {
                                         .append(") values (").append(v).append(")");\r
       String sql = sqlBuf.toString();\r
 \r
-      //theLog.printInfo("INSERT: " + sql);\r
+      logger.debug("INSERT: " + sql);\r
       con = getPooledCon();\r
       con.setAutoCommit(false);\r
       pstmt = con.prepareStatement(sql);\r
@@ -859,9 +863,7 @@ public class Database implements StorageObject {
         return null;\r
       }\r
 \r
-      pstmt =\r
-        con.prepareStatement(theAdaptor.getLastInsertSQL(\r
-            (Database) myselfDatabase));\r
+      pstmt = con.prepareStatement("select currval('" + getCoreTable() + "_id_seq')");\r
 \r
       ResultSet rs = pstmt.executeQuery();\r
       rs.next();\r
@@ -886,8 +888,7 @@ public class Database implements StorageObject {
   }\r
 \r
   /**\r
-   * update-Operator: aktualisiert eine Entity. Eine Spalte WEBDB_LASTCHANGE\r
-   * wird automatisch mit dem aktuellen Datum gefuellt.\r
+   * Updates an entity in the database\r
    *\r
    * @param theEntity\r
    */\r
@@ -967,10 +968,11 @@ public class Database implements StorageObject {
 \r
         // TimeStamp stuff\r
         try {\r
-          java.util.Date d = _dateFormatterIn.parse(dateString);\r
-          Timestamp tStamp = new Timestamp(d.getTime());\r
-          sql.append(",webdb_create='" + tStamp.toString() + "'");\r
-        } catch (ParseException e) {\r
+          java.util.Date d = userInputDateFormat.parse(dateString);\r
+//          Timestamp tStamp = new Timestamp(d.getTime());\r
+          sql.append(",webdb_create='" + JDBCStringRoutines.formatDate(d) + "'");\r
+        }\r
+        catch (ParseException e) {\r
           throw new StorageObjectFailure(e);\r
         }\r
       }\r
@@ -983,9 +985,8 @@ public class Database implements StorageObject {
     }\r
 \r
     sql.append(" where id=").append(id);\r
+    logger.debug("UPDATE: " + sql);\r
 \r
-    //theLog.printInfo("UPDATE: " + sql);\r
-    // execute sql\r
     try {\r
       con = getPooledCon();\r
       con.setAutoCommit(false);\r
@@ -1000,12 +1001,15 @@ public class Database implements StorageObject {
       }\r
 \r
       pstmt.executeUpdate();\r
-    } catch (SQLException sqe) {\r
+    }\r
+    catch (SQLException sqe) {\r
       throwSQLException(sqe, "update");\r
-    } finally {\r
+    }\r
+    finally {\r
       try {\r
         con.setAutoCommit(true);\r
-      } catch (Exception e) {\r
+      }\r
+      catch (Exception e) {\r
         ;\r
       }\r
 \r
@@ -1058,153 +1062,49 @@ public class Database implements StorageObject {
     return (res > 0) ? true : false;\r
   }\r
 \r
-  /* noch nicht implementiert.\r
-  * @return immer false\r
-   */\r
-  public boolean delete(EntityList theEntityList) {\r
-    invalidatePopupCache();\r
-\r
-    return false;\r
-  }\r
-\r
-  /**\r
-   * Diese Methode sollte ueberschrieben werden, wenn fuer die abgeleitete Database-Klasse\r
-   * eine SimpleList mit Standard-Popupdaten erzeugt werden koennen soll.\r
-   * @return null\r
-   */\r
-  public SimpleList getPopupData() throws StorageObjectFailure {\r
-    return null;\r
-  }\r
-\r
-  /**\r
-   *  Holt Daten fuer Popups.\r
-   *  @param name  Name des Feldes.\r
-   *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.\r
-   *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.\r
-   */\r
-  public SimpleList getPopupData(String name, boolean hasNullValue)\r
-    throws StorageObjectFailure {\r
-    return getPopupData(name, hasNullValue, null);\r
-  }\r
-\r
-  /**\r
-   *  Holt Daten fuer Popups.\r
-   *  @param name  Name des Feldes.\r
-   *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.\r
-   *  @param where  Schraenkt die Selektion der Datensaetze ein.\r
-   *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.\r
-   */\r
-  public SimpleList getPopupData(String name, boolean hasNullValue, String where)\r
-    throws StorageObjectFailure {\r
-    return getPopupData(name, hasNullValue, where, null);\r
-  }\r
-\r
   /**\r
-   *  Holt Daten fuer Popups.\r
-   *  @param name  Name des Feldes.\r
-   *  @param hasNullValue  Wenn true wird eine leerer  Eintrag fuer die Popups erzeugt.\r
-   *  @param where  Schraenkt die Selektion der Datensaetze ein.\r
-   *  @param order  Gibt ein Feld als Sortierkriterium an.\r
-   *  @return SimpleList Gibt freemarker.template.SimpleList zurueck.\r
+   * Deletes entities based on a where clause\r
+   *\r
+   * @param aWhereClause\r
+   * @return\r
+   * @throws StorageObjectFailure\r
    */\r
-  public SimpleList getPopupData(String name, boolean hasNullValue,\r
-    String where, String order) throws StorageObjectFailure {\r
-    // caching\r
-    if (hasPopupCache && (popupCache != null)) {\r
-      return popupCache;\r
+  public int deleteByWhereClause(String aWhereClause) throws StorageObjectFailure {\r
+    invalidatePopupCache();\r
+    if (StoreUtil.implementsStorableObject(theEntityClass)) {\r
+      StoreContainerType stoc_type = StoreContainerType.valueOf(theEntityClass, StoreContainerType.STOC_TYPE_ENTITYLIST);\r
+      o_store.invalidate(stoc_type);\r
     }\r
 \r
-    SimpleList simpleList = null;\r
-    Connection con = null;\r
     Statement stmt = null;\r
+    Connection con = null;\r
+    int res = 0;\r
+    String sql =\r
+      "delete from " + theTable + " where " + aWhereClause;\r
 \r
-    // build sql\r
-    StringBuffer sql =\r
-      new StringBuffer("select ").append(thePKeyName).append(",").append(name)\r
-                                 .append(" from ").append(theTable);\r
-\r
-    if ((where != null) && !(where.length() == 0)) {\r
-      sql.append(" where ").append(where);\r
-    }\r
-\r
-    sql.append(" order by ");\r
-\r
-    if ((order != null) && !(order.length() == 0)) {\r
-      sql.append(order);\r
-    } else {\r
-      sql.append(name);\r
-    }\r
-\r
-    // execute sql\r
+    //theLog.printInfo("DELETE " + sql);\r
     try {\r
       con = getPooledCon();\r
-    } catch (Exception e) {\r
-      throw new StorageObjectFailure(e);\r
-    }\r
-\r
-    try {\r
       stmt = con.createStatement();\r
-\r
-      ResultSet rs = executeSql(stmt, sql.toString());\r
-\r
-      if (rs != null) {\r
-        if (!evaluatedMetaData) {\r
-          get_meta_data();\r
-        }\r
-\r
-        simpleList = new SimpleList();\r
-\r
-        // if popup has null-selector\r
-        if (hasNullValue) {\r
-          simpleList.add(POPUP_EMPTYLINE);\r
-        }\r
-\r
-        SimpleHash popupDict;\r
-\r
-        while (rs.next()) {\r
-          popupDict = new SimpleHash();\r
-          popupDict.put("key", getValueAsString(rs, 1, thePKeyType));\r
-          popupDict.put("value", rs.getString(2));\r
-          simpleList.add(popupDict);\r
-        }\r
-\r
-        rs.close();\r
-      }\r
+      res = stmt.executeUpdate(sql);\r
     }\r
-    catch (Exception e) {\r
-      logger.error("getPopupData: " + e.getMessage());\r
-      throw new StorageObjectFailure(e);\r
-    } finally {\r
-      freeConnection(con, stmt);\r
+    catch (SQLException sqe) {\r
+      throwSQLException(sqe, "delete");\r
     }\r
-\r
-    if (hasPopupCache) {\r
-      popupCache = simpleList;\r
+    finally {\r
+      freeConnection(con, stmt);\r
     }\r
 \r
-    return simpleList;\r
+    return res;\r
   }\r
 \r
-  /**\r
-   * Liefert alle Daten der Tabelle als SimpleHash zurueck. Dies wird verwandt,\r
-   * wenn in den Templates ein Lookup-Table benoetigt wird. Sollte nur bei kleinen\r
-   * Tabellen Verwendung finden.\r
-   * @return SimpleHash mit den Tabellezeilen.\r
+  /* noch nicht implementiert.\r
+  * @return immer false\r
    */\r
-  public SimpleHash getHashData() {\r
-    /** @todo dangerous! this should have a flag to be enabled, otherwise\r
-     *  very big Hashes could be returned */\r
-    if (hashCache == null) {\r
-      try {\r
-        hashCache =\r
-          HTMLTemplateProcessor.makeSimpleHash(selectByWhereClause("", -1));\r
-      }\r
-      catch (StorageObjectFailure e) {\r
-        logger.debug(e.getMessage());\r
-      }\r
-    }\r
+  public boolean delete(EntityList theEntityList) {\r
+    invalidatePopupCache();\r
 \r
-    return hashCache;\r
+    return false;\r
   }\r
 \r
   /* invalidates the popupCache\r
@@ -1239,6 +1139,107 @@ public class Database implements StorageObject {
 \r
     return rs;\r
   }\r
+/*\r
+  public ResultSet executeSql(String sql) throws StorageObjectFailure, SQLException {\r
+    long startTime = System.currentTimeMillis();\r
+    Connection connection = null;\r
+    Statement statement = null;\r
+\r
+    try {\r
+      connection = getPooledCon();\r
+      statement = connection.createStatement();\r
+      ResultSet result;\r
+\r
+      result = statement.executeQuery(sql);\r
+\r
+      logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
+      return result;\r
+    }\r
+    catch (Throwable e) {\r
+      logger.error(e.getMessage() +"\n" + (System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
+      throw new StorageObjectFailure(e);\r
+    }\r
+    finally {\r
+      if (connection!=null) {\r
+        freeConnection(connection, statement);\r
+      }\r
+    }\r
+  }\r
+*/\r
+  private Map processRow(ResultSet aResultSet) throws StorageObjectFailure, StorageObjectExc {\r
+    try {\r
+      Map result = new HashMap();\r
+      ResultSetMetaData metaData = aResultSet.getMetaData();\r
+      int nrColumns = metaData.getColumnCount();\r
+      for (int i=0; i<nrColumns; i++) {\r
+        result.put(metaData.getColumnName(i+1), getValueAsString(aResultSet, i+1, metaData.getColumnType(i+1)));\r
+      }\r
+\r
+      return result;\r
+    }\r
+    catch (Throwable e) {\r
+      throw new StorageObjectFailure(e);\r
+    }\r
+  }\r
+\r
+  public List executeFreeSql(String sql, int aLimit) throws StorageObjectFailure, StorageObjectExc {\r
+    Connection connection = null;\r
+    Statement statement = null;\r
+    try {\r
+      List result = new Vector();\r
+      connection = getPooledCon();\r
+      statement = connection.createStatement();\r
+      ResultSet resultset = executeSql(statement, sql);\r
+      try {\r
+        while (resultset.next() && result.size() < aLimit) {\r
+          result.add(processRow(resultset));\r
+        }\r
+      }\r
+      finally {\r
+        resultset.close();\r
+      }\r
+\r
+      return result;\r
+    }\r
+    catch (Throwable e) {\r
+      throw new StorageObjectFailure(e);\r
+    }\r
+    finally {\r
+      if (connection!=null) {\r
+        freeConnection(connection, statement);\r
+      }\r
+    }\r
+  };\r
+\r
+  public Map executeFreeSingleRowSql(String anSqlStatement) throws StorageObjectFailure, StorageObjectExc {\r
+    try {\r
+      List resultList = executeFreeSql(anSqlStatement, 1);\r
+      try {\r
+        if (resultList.size()>0)\r
+          return (Map) resultList.get(0);\r
+        else\r
+          return null;\r
+      }\r
+      finally {\r
+      }\r
+    }\r
+    catch (Throwable t) {\r
+      throw new StorageObjectFailure(t);\r
+    }\r
+  };\r
+\r
+  public String executeFreeSingleValueSql(String sql) throws StorageObjectFailure, StorageObjectExc {\r
+    Map row = executeFreeSingleRowSql(sql);\r
+\r
+    if (row==null)\r
+      return null;\r
+\r
+    Iterator i = row.values().iterator();\r
+    if (i.hasNext())\r
+      return (String) i.next();\r
+    else\r
+      return null;\r
+  };\r
 \r
   /**\r
    * returns the number of rows in the table\r
@@ -1247,7 +1248,7 @@ public class Database implements StorageObject {
     long startTime = System.currentTimeMillis();\r
     String sql = "SELECT Count(*) FROM " + theTable;\r
 \r
-    if ((where != null) && !(where.length() == 0)) {\r
+    if ((where != null) && (where.length() != 0)) {\r
       sql = sql + " where " + where;\r
     }\r
 \r
@@ -1289,7 +1290,7 @@ public class Database implements StorageObject {
       logger.debug((System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
     }\r
     catch (SQLException e) {\r
-      logger.debug("Failed: " + (System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
+      logger.error("Failed: " + (System.currentTimeMillis() - startTime) + "ms. for: " + sql);\r
       throw e;\r
     }\r
 \r
@@ -1394,7 +1395,7 @@ public class Database implements StorageObject {
     }\r
     catch (SQLException e) {\r
       logger.error("could not connect to the database " + e.getMessage());\r
-      System.err.println("could not connect to the database " + e.getMessage());\r
+\r
       throw new StorageObjectFailure("Could not connect to the database", e);\r
     }\r
 \r
@@ -1413,8 +1414,7 @@ public class Database implements StorageObject {
    * @param wo Funktonsname, in der die SQLException geworfen wurde\r
    * @exception StorageObjectException\r
    */\r
-  protected void throwSQLException(SQLException sqe, String aFunction)\r
-    throws StorageObjectFailure {\r
+  protected void throwSQLException(SQLException sqe, String aFunction) throws StorageObjectFailure {\r
     String state = "";\r
     String message = "";\r
     int vendor = 0;\r
@@ -1456,4 +1456,4 @@ public class Database implements StorageObject {
     logger.error(aMessage);\r
     throw new StorageObjectFailure(aMessage, null);\r
   }\r
-}\r
+}