01: package com.opensymphony.webwork.views.freemarker;
02:
03: import freemarker.core.CollectionAndSequence;
04: import freemarker.ext.beans.BeansWrapper;
05: import freemarker.ext.beans.MapModel;
06: import freemarker.ext.util.ModelFactory;
07: import freemarker.template.*;
08:
09: import java.util.Map;
10: import java.util.Set;
11:
12: /**
13: * <!-- START SNIPPET: javadoc -->
14: *
15: * The WebWorkBeanWrapper extends the default FreeMarker BeansWrapper and provides almost no change in functionality,
16: * <b>except</b> for how it handles maps. Normally, FreeMarker has two modes of operation: either support for friendly
17: * map built-ins (?keys, ?values, etc) but only support for String keys; OR no special built-in support (ie: ?keys
18: * returns the methods on the map instead of the keys) but support for String and non-String keys alike. WebWork
19: * provides an alternative implementation that gives us the best of both worlds.
20: *
21: * <p/> It is possible that this special behavior may be confusing or can cause problems. Therefore, you can set the
22: * <b>webwork.freemarker.wrapper.altMap</b> property in webwork.properties to false, allowing the normal BeansWrapper
23: * logic to take place instead.
24: *
25: * <!-- END SNIPPET: javadoc -->
26: */
27: public class WebWorkBeanWrapper extends BeansWrapper {
28: private static final boolean altMapWrapper = "true"
29: .equals(com.opensymphony.webwork.config.Configuration
30: .get("webwork.freemarker.wrapper.altMap"));
31:
32: public TemplateModel wrap(Object object)
33: throws TemplateModelException {
34: if (object instanceof TemplateBooleanModel) {
35: return super .wrap(object);
36: }
37:
38: // attempt to get the best of both the SimpleMapModel and the MapModel of FM.
39: if (altMapWrapper && object instanceof Map) {
40: return getInstance(object, FriendlyMapModel.FACTORY);
41: }
42:
43: return super .wrap(object);
44: }
45:
46: /**
47: * Attempting to get the best of both worlds of FM's MapModel and SimpleMapModel, by reimplementing the isEmpty(),
48: * keySet() and values() methods. ?keys and ?values built-ins are thus available, just as well as plain Map
49: * methods.
50: */
51: private final static class FriendlyMapModel extends MapModel
52: implements TemplateHashModelEx {
53: static final ModelFactory FACTORY = new ModelFactory() {
54: public TemplateModel create(Object object,
55: ObjectWrapper wrapper) {
56: return new FriendlyMapModel((Map) object,
57: (BeansWrapper) wrapper);
58: }
59: };
60:
61: public FriendlyMapModel(Map map, BeansWrapper wrapper) {
62: super (map, wrapper);
63: }
64:
65: public boolean isEmpty() {
66: return ((Map) object).isEmpty();
67: }
68:
69: protected Set keySet() {
70: return ((Map) object).keySet();
71: }
72:
73: public TemplateCollectionModel values() {
74: return new CollectionAndSequence(new SimpleSequence(
75: ((Map) object).values(), wrapper));
76: }
77: }
78: }
|