Anonymous avatar Anonymous committed 1f00714

WW-1069: better map support (best of both worlds)

git-svn-id: http://svn.opensymphony.com/svn/webwork/trunk@1929 573baa09-0c28-0410-bef9-dab3c582ae83

Comments (0)

Files changed (3)

src/java/com/opensymphony/webwork/default.properties

 ###(can be overridden by a webwork.properties file in the root of the classpath)
 ###
 
-
-
 ### Specifies the Configuration used to configure webwork
 ### one could extend com.opensymphony.webwork.config.Configuration
 ### to build one's customize way of getting the configurations parameters into webwork
 # webwork.configuration=com.opensymphony.webwork.config.DefaultConfiguration
 
-
-
 ### This can be used to set your default locale and encoding scheme
 # webwork.locale=en_US
 webwork.i18n.encoding=UTF-8
 
-
-
 ### if specified, the default object factory can be overridden here
 ### Note: short-hand notation is supported in some cases, such as "spring"
 ###       Alternatively, you can provide a com.opensymphony.xwork.ObjectFactory subclass name here  
 # webwork.objectFactory = spring
 
-
-
 ### specifies the autoWiring logic when using the SpringObjectFactory.
 ### valid values are: name, type, auto, and constructor (name is the default)
 webwork.objectFactory.spring.autoWire = name
 
-
-
 ### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data
 # webwork.multipart.parser=cos
 # webwork.multipart.parser=pell
 webwork.multipart.saveDir=
 webwork.multipart.maxSize=2097152
 
-
-
 ### Load custom property files (does not override webwork.properties!)
 # webwork.custom.properties=application,com/webwork/extension/custom
 
-
-
 ### How request URLs are mapped to and from actions
 webwork.mapper.class=com.opensymphony.webwork.dispatcher.mapper.DefaultActionMapper
 
-
-
 ### Used by the DefaultActionMapper
 webwork.action.extension=action
 
-
-
 ### use alternative syntax that requires %{} in most places
 ### to evaluate expressions for String attributes for tags
 webwork.tag.altSyntax=true
 
-
-
 ### when set to true, WebWork will act much more friendly for developers. This
 ### includes:
 ### - webwork.i18n.reload = true
 ###                them right away.
 webwork.devMode = false
 
-
-
 ### when set to true, resource bundles will be reloaded on _every_ request.
 ### this is good during development, but should never be used in production
 webwork.i18n.reload=false
 
-
-
 ### Standard UI theme
 ### Change this to reflect which path should be used for JSP control tag templates by default
 webwork.ui.theme=xhtml
 #sets the default template type. Either ftl, vm, or jsp
 webwork.ui.templateSuffix=ftl
 
-
-
 ### Configuration reloading
 ### This will cause the configuration to reload xwork.xml when it is changed
 webwork.configuration.xml.reload=false
 
-
-
 ### Location of velocity.properties file.  defaults to velocity.properties
 # webwork.velocity.configfile = velocity.properties
 
-
-
 ### Comma separated list of VelocityContext classnames to chain to the WebWorkVelocityContext
 # webwork.velocity.contexts =
 
-
-
 ### used to build URLs, such as the UrlTag
 webwork.url.http.port = 80
 webwork.url.https.port = 443
 
-
-
 ### Load custom default resource bundles
 # webwork.custom.i18n.resources=testmessages,testmessages2
 
-
-
 ### workaround for some app servers that don't handle HttpServletRequest.getParameterMap()
 ### often used for WebLogic, Orion, and OC4J
 webwork.dispatcher.parametersWorkaround = false
 
-
-
 ### configure the Freemarker Manager class to be used
 ### Allows user to plug-in customised Freemarker Manager if necessary
 ### MUST extends off com.opensymphony.webwork.views.freemarker.FreemarkerManager
 #webwork.freemarker.manager.classname=com.opensymphony.webwork.views.freemarker.FreemarkerManager
 
+### See the WebWorkBeanWrapper javadocs for more information
+webwork.freemarker.wrapper.altMap=true
+
 ### END SNIPPET: complete_file

src/java/com/opensymphony/webwork/views/freemarker/FreemarkerManager.java

     }
 
     protected BeansWrapper getObjectWrapper() {
-        BeansWrapper beansWrapper = new WebWorkBeanWrapper();
-        beansWrapper.setSimpleMapWrapper(false);
-
-
-        return beansWrapper;
+        return new WebWorkBeanWrapper();
     }
 
     /**

src/java/com/opensymphony/webwork/views/freemarker/WebWorkBeanWrapper.java

 package com.opensymphony.webwork.views.freemarker;
 
+import freemarker.core.CollectionAndSequence;
 import freemarker.ext.beans.BeansWrapper;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-import freemarker.template.TemplateBooleanModel;
+import freemarker.ext.beans.MapModel;
+import freemarker.ext.util.ModelFactory;
+import freemarker.template.*;
+
+import java.util.Map;
+import java.util.Set;
 
 /**
- * @author Patrick Lightbody (plightbo at gmail dot com)
+ * <!-- START SNIPPET: javadoc -->
+ *
+ * The WebWorkBeanWrapper extends the default FreeMarker BeansWrapper and provides almost no change in functionality,
+ * <b>except</b> for how it handles maps. Normally, FreeMarker has two modes of operation: either support for friendly
+ * map built-ins (?keys, ?values, etc) but only support for String keys; OR no special built-in support (ie: ?keys
+ * returns the methods on the map instead of the keys) but support for String and non-String keys alike. WebWork
+ * provides an alternative implementation that gives us the best of both worlds.
+ *
+ * <p/> It is possible that this special behavior may be confusing or can cause problems. Therefore, you can set the
+ * <b>webwork.freemarker.wrapper.altMap</b> property in webwork.properties to false, allowing the normal BeansWrapper
+ * logic to take place instead.
+ *
+ * <!-- END SNIPPET: javadoc -->
  */
 public class WebWorkBeanWrapper extends BeansWrapper {
+    private static final boolean altMapWrapper
+            = "true".equals(com.opensymphony.webwork.config.Configuration.get("webwork.freemarker.wrapper.altMap"));
+
     public TemplateModel wrap(Object object) throws TemplateModelException {
         if (object instanceof TemplateBooleanModel) {
-            TemplateModel tm = super.wrap(object);
-            return tm;
+            return super.wrap(object);
+        }
+
+        // attempt to get the best of both the SimpleMapModel and the MapModel of FM.
+        if (altMapWrapper && object instanceof Map) {
+            return getInstance(object, FriendlyMapModel.FACTORY);
         }
 
         return super.wrap(object);
     }
 
-    class BooleanTemplateModel implements TemplateBooleanModel {
-        public boolean getAsBoolean() throws TemplateModelException {
-            return false;
+    /**
+     * Attempting to get the best of both worlds of FM's MapModel and SimpleMapModel, by reimplementing the isEmpty(),
+     * keySet() and values() methods. ?keys and ?values built-ins are thus available, just as well as plain Map
+     * methods.
+     */
+    private final static class FriendlyMapModel extends MapModel implements TemplateHashModelEx {
+        static final ModelFactory FACTORY = new ModelFactory() {
+            public TemplateModel create(Object object, ObjectWrapper wrapper) {
+                return new FriendlyMapModel((Map) object, (BeansWrapper) wrapper);
+            }
+        };
+
+        public FriendlyMapModel(Map map, BeansWrapper wrapper) {
+            super(map, wrapper);
+        }
+
+        public boolean isEmpty() {
+            return ((Map) object).isEmpty();
+        }
+
+        protected Set keySet() {
+            return ((Map) object).keySet();
+        }
+
+        public TemplateCollectionModel values() {
+            return new CollectionAndSequence(new SimpleSequence(((Map) object).values(), wrapper));
         }
     }
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.