EMPIREDB-310
Overcome ParamterMap limitations (see Jira)
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/FacesUtils.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/FacesUtils.java
index d26bd72..50dad0e 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/FacesUtils.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/FacesUtils.java
@@ -133,7 +133,7 @@
         return context.getExternalContext().getRequestParameterMap().get(param);

     }

 

-    public static Object getRequestParam(final String key)

+    public static String getRequestParam(final String key)

     {

         FacesContext fc = getContext();

         return getRequestParam(fc, key);

@@ -212,17 +212,54 @@
     }

 

     /* Parameter-map */

-    public static final String PARAMETER_MAP_ATTRIBUTE = ParameterMap.class.getSimpleName();

+    public static final String PARAMETER_MAP_ATTRIBUTE = "PARAMETER_MAP";

+

+    public static final String PARAMETER_MAP_CLASS_ATTRIBUTE = "PARAMETER_MAP_CLASS";

+

+    public static void setParameterMapClass(final FacesContext fc, Class<? extends ParameterMap>clazz)

+    {

+        Map<String, Object> am = fc.getExternalContext().getApplicationMap();

+        am.put(PARAMETER_MAP_CLASS_ATTRIBUTE, clazz);

+    }

     

     public static ParameterMap getParameterMap(final FacesContext fc)

     {

         Map<String, Object> sm = fc.getExternalContext().getSessionMap();

-        Object pm = sm.get(PARAMETER_MAP_ATTRIBUTE);

+        ParameterMap pm = (ParameterMap)sm.get(PARAMETER_MAP_ATTRIBUTE);

         if (pm==null)

-        {   pm = new ParameterMap();

+        {   try

+            {   // Create Paramter Map

+                Map<String, Object> am = fc.getExternalContext().getApplicationMap();

+                Object pmClass = am.get(PARAMETER_MAP_CLASS_ATTRIBUTE);

+                if (pmClass instanceof Class<?>) {

+                    // ParamterMapClass provided as Object

+                    pm = (ParameterMap)((Class<?>)pmClass).newInstance(); 

+                } 

+                else if (pmClass instanceof String) {

+                    // ParamterMapClass provided as String

+                    pm = (ParameterMap)Class.forName((String)pmClass).newInstance(); 

+                }

+                else {

+                    // not provided, use default

+                    pm = new ParameterMap();

+                }

+            }

+            catch (ClassNotFoundException e)

+            {

+                throw new InternalException(e);

+            }

+            catch (InstantiationException e)

+            {

+                throw new InternalException(e);

+            }

+            catch (IllegalAccessException e)

+            {

+                throw new InternalException(e);

+            }

+            // put on session

             sm.put(PARAMETER_MAP_ATTRIBUTE, pm);

         }

-        return (ParameterMap)pm;

+        return pm;

     }

     

     public static ParameterMap getParameterMap()

diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pageelements/BeanListPageElement.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pageelements/BeanListPageElement.java
index 893fadc..8da9047 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pageelements/BeanListPageElement.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pageelements/BeanListPageElement.java
@@ -545,7 +545,7 @@
         Set<Object[]> items = new HashSet<Object[]>(selectedItems.size());

         for (String idParam : selectedItems)

         {

-            Object[] key = getParameterMap().get(rowset, idParam);

+            Object[] key = getParameterMap().getKey(rowset, idParam);

             if (key == null)

             {

                 log.warn("Object does not exist in ParameterMap!");

diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java
index 25af54b..47a4bd4 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/pages/Page.java
@@ -397,13 +397,13 @@
     public Object[] getKeyFromParam(DBRowSet rowset, String idParam)

     {

         FacesContext fc = FacesUtils.getContext();

-        return FacesUtils.getParameterMap(fc).get(rowset, idParam);

+        return FacesUtils.getParameterMap(fc).getKey(rowset, idParam);

     }

     

     public Object[] getKeyFromParam(PageDefinition page, DBRowSet rowset, String idParam)

     {

         FacesContext fc = FacesUtils.getContext();

-        return FacesUtils.getParameterMap(fc).get(page, rowset, idParam);

+        return FacesUtils.getParameterMap(fc).getKey(page, rowset, idParam);

     }

     

     public String getIdParamForKey(DBRowSet rowset, Object[] key)

diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterMap.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterMap.java
index 88b4024..31aace7 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterMap.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterMap.java
@@ -62,6 +62,10 @@
     }

     

     private final byte[] salt;

+

+    protected Hashtable<String, String> codeMap = new Hashtable<String, String>();

+

+    protected final Hashtable<String, Hashtable<String, Object>> typeMap = new Hashtable<String, Hashtable<String, Object>>();

     

     public ParameterMap()

     {

@@ -100,8 +104,6 @@
         return hash.toString();

     }

 

-    private Hashtable<String, String> codeMap = new Hashtable<String, String>();

-

     public String encodeStringWithCache(String valueAsString)

     {

         String code = codeMap.get(valueAsString);

@@ -110,61 +112,104 @@
             code = encodeString(valueAsString);

             codeMap.put(valueAsString, code);

         }

-        /*

-        else

-        {   // Trace

-            if (log.isTraceEnabled())

-                log.trace("Using already generated code {} for value {}.", code, valueAsString);

-        }

-        */

         return code;

     }

-    

-    private final Hashtable<String, Hashtable<String, Object>> typeMap = new Hashtable<String, Hashtable<String, Object>>();

-    

-    private void putValue(String typeName, String key, Object value)

+

+    /**

+     * gets a unique name for a given rowset  

+     * @param rowset

+     * @return a unique name for the given rowset

+     */

+    protected String getRowSetTypeName(DBRowSet rowset)

     {

+        /*

+         * alternatively use:

+         *     rowset.getName();

+         * or

+         *     rowset.getFullName();

+         */

+        return rowset.getClass().getName();

+    }

+

+    /**

+     * puts an object into the parameter map

+     * @param typeName

+     * @param id

+     * @param value

+     */

+    protected void putValue(String typeName, String encodedId, Object item)

+    {   // put in Table

+        if (encodedId==null)

+        {

+            throw new InvalidArgumentException("encodedId", encodedId);

+        }

         Hashtable<String, Object> map = typeMap.get(typeName);

         if (map==null)

         {   map = new Hashtable<String, Object>(1);

             typeMap.put(typeName, map);

         }

-        if (key==null || value==null)

-            log.warn("Key or value is null.");

-        map.put(key, value);

+        map.put(encodedId, item);

+    }

+    

+    /**

+     * encodes the objectKey and stores the item in the parameter map

+     * @param typeName

+     * @param id

+     * @param value

+     */

+    protected String encodeAndStore(String typeName, String objectKey, Object item, boolean useCache)

+    {   // Generate the id

+        String encodedId = (useCache ? encodeStringWithCache(objectKey) : encodeString(objectKey));

+        // store

+        putValue(typeName, encodedId, item);

+        // return id

+        return encodedId;

     }

 

     public String put(String type, String key, boolean useCache)

     {

         // Generate id and put in map

-        String id = (useCache ? encodeStringWithCache(key) : encodeString(key));

-        putValue(type, id, key);

-        return id;

+        return encodeAndStore(type, key, key, useCache);

     }

 

-    public String put(Class<? extends Object> c, Object[] key)

+    /**

+     * Puts an object into the paramter map that implements the ParameterObject interface  

+     * @param paramObject

+     * @return

+     */

+    public String put(ParameterObject paramObject)

     {

+        String objectKey;

+        // check param

+        if (paramObject==null || StringUtils.isEmpty((objectKey=paramObject.getObjectKey())))

+            throw new InvalidArgumentException("paramObject", paramObject);

         // Generate id and put in map

-        String ref = StringUtils.valueOf(key);

-        String id = encodeString(ref);

-        String type = c.getSimpleName();

-        putValue(type, id, key);

-        return id;

+        String type = paramObject.getClass().getName();

+        return encodeAndStore(type, objectKey, paramObject, false);

     }

 

     public String put(DBRowSet rowset, Object[] key)

     {

         // Generate id and put in map

-        String ref = StringUtils.valueOf(key);

-        String id = encodeString(ref);

-        String type = rowset.getClass().getSimpleName();

-        putValue(type, id, key);

-        return id;

+        String rowKey = StringUtils.valueOf(key);

+        String type = getRowSetTypeName(rowset);

+        return encodeAndStore(type, rowKey, key, false);

     }

+

+    /*

+     * do we really need this?

+     * 

+    public String put(Class<? extends Object> c, Object[] key)

+    {

+        // Generate id and put in map

+        String ref = StringUtils.valueOf(key);

+        String type = c.getName();

+        return encodeAndStore(type, ref, key, false);

+    }

+    */

     

     /**

      * Generates an idParam which is only valid for the given page.

-     * 

      * @param targetPage

      * @param rowset

      * @param key

@@ -172,54 +217,85 @@
      */

     public String put(PageDefinition targetPage, DBRowSet rowset, Object[] key) {

         // Generate id and put in map

-        String ref = targetPage.getPageBeanName() + "/" + StringUtils.valueOf(key);

-        String idParam = encodeString(ref);

-        String type = targetPage.getPageBeanName() + "$" + rowset.getClass().getSimpleName();

-        putValue(type, idParam, key);

-        return idParam;

+        String ref  = StringUtils.valueOf(key);

+        String type = targetPage.getPageBeanName() + "$" + getRowSetTypeName(rowset);

+        return encodeAndStore(type, ref, key, false);

     }

 

+    /**

+     * Gets an object from the parameter map for a given type and id

+     * @param type the object type (typically the class name)

+     * @param id the encoded idParam

+     * @return the object

+     */

     public Object get(String type, String id)

     {

         Hashtable<String, Object> map = typeMap.get(type);

         return (map!=null ? map.get(id) : null);

     }

 

-    public Object[] get(Class<? extends Object> c, String id)

+    public void clear(String type)

     {

-        String type = c.getSimpleName();

-        Hashtable<String, Object> map = typeMap.get(type);

-        return (map!=null ? ((Object[])map.get(id)) : null);

-    }

-

-    public Object[] get(DBRowSet rowset, String id)

-    {

-        String type = rowset.getClass().getSimpleName();

-        Hashtable<String, Object> map = typeMap.get(type);

-        return (map!=null ? ((Object[])map.get(id)) : null);

-    }

-    

-    public Object[] get(PageDefinition page, DBRowSet rowset, String id)

-    {

-    	String type = page.getPageBeanName() + "$" + rowset.getClass().getSimpleName();

-        Hashtable<String, Object> map = typeMap.get(type);

-        return (map!=null ? ((Object[])map.get(id)) : null);

-    }

-

-    public void clear(Class<? extends Object> c)

-    {

-        String type = c.getSimpleName();

         Hashtable<String, Object> map = typeMap.get(type);

         if (map!=null)

             map.clear();

     }

 

+    /**

+     * Puts an object into the paramter map that implements the ParameterObject interface  

+     * @param paramType

+     * @param id

+     * @return

+     */

+    @SuppressWarnings("unchecked")

+    public <T extends ParameterObject> T get(Class<T> paramType, String id)

+    {

+        String type = paramType.getName();

+        Hashtable<String, Object> map = typeMap.get(type);

+        return (T)(map!=null ? map.get(id) : null);

+    }

+

+    public void clear(Class<? extends ParameterObject> paramType)

+    {

+        String type = paramType.getName();

+        clear(type);

+    }

+

+    /*

+     * do we really need this?

+     * 

+    public Object[] getKey(Class<? extends Object> c, String id)

+    {

+        String type = c.getName();

+        Hashtable<String, Object> map = typeMap.get(type);

+        return (map!=null ? ((Object[])map.get(id)) : null);

+    }

+    */

+

+    public Object[] getKey(DBRowSet rowset, String id)

+    {

+        String type = getRowSetTypeName(rowset);

+        Hashtable<String, Object> map = typeMap.get(type);

+        return (map!=null ? ((Object[])map.get(id)) : null);

+    }

+

     public void clear(DBRowSet rowset)

     {

-        String type = rowset.getClass().getSimpleName();

-        Hashtable<String, Object> map = typeMap.get(type);

-        if (map!=null)

-            map.clear();

+        String type = getRowSetTypeName(rowset);

+        clear(type);

     }

 

+    /**

+     * returns an record key for a given page

+     * @param page

+     * @param rowset

+     * @param id

+     * @return

+     */

+    public Object[] getKey(PageDefinition page, DBRowSet rowset, String id)

+    {

+        String type = page.getPageBeanName() + "$" + getRowSetTypeName(rowset);

+        Hashtable<String, Object> map = typeMap.get(type);

+        return (map!=null ? ((Object[])map.get(id)) : null);

+    }

 }

diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterObject.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterObject.java
new file mode 100644
index 0000000..8108ef5
--- /dev/null
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/utils/ParameterObject.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jsf2.utils;
+
+/**
+ * ParameterObject
+ * This interface is used to store objects in the session parameter map
+ * The object must provide a unique instance key as a String
+ * @author doebele
+ */
+public interface ParameterObject
+{
+    /**
+     * provides the unique instance key for this object
+     * @return the unique key
+     */
+    String getObjectKey();
+}