View Javadoc

1   /*
2    * $Id: StrutsBeanWrapper.java 439747 2006-09-03 09:22:46Z mrdon $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts2.views.freemarker;
19  
20  import java.util.Map;
21  import java.util.Set;
22  
23  import freemarker.core.CollectionAndSequence;
24  import freemarker.ext.beans.BeansWrapper;
25  import freemarker.ext.beans.MapModel;
26  import freemarker.ext.util.ModelFactory;
27  import freemarker.template.ObjectWrapper;
28  import freemarker.template.SimpleSequence;
29  import freemarker.template.TemplateBooleanModel;
30  import freemarker.template.TemplateCollectionModel;
31  import freemarker.template.TemplateHashModelEx;
32  import freemarker.template.TemplateModel;
33  import freemarker.template.TemplateModelException;
34  
35  /***
36   * <!-- START SNIPPET: javadoc -->
37   *
38   * The StrutsBeanWrapper extends the default FreeMarker BeansWrapper and provides almost no change in functionality,
39   * <b>except</b> for how it handles maps. Normally, FreeMarker has two modes of operation: either support for friendly
40   * map built-ins (?keys, ?values, etc) but only support for String keys; OR no special built-in support (ie: ?keys
41   * returns the methods on the map instead of the keys) but support for String and non-String keys alike. Struts
42   * provides an alternative implementation that gives us the best of both worlds.
43   *
44   * <p/> It is possible that this special behavior may be confusing or can cause problems. Therefore, you can set the
45   * <b>struts.freemarker.wrapper.altMap</b> property in struts.properties to false, allowing the normal BeansWrapper
46   * logic to take place instead.
47   *
48   * <!-- END SNIPPET: javadoc -->
49   */
50  public class StrutsBeanWrapper extends BeansWrapper {
51      private static final boolean altMapWrapper
52              = "true".equals(org.apache.struts2.config.Settings.get("struts.freemarker.wrapper.altMap"));
53  
54      public TemplateModel wrap(Object object) throws TemplateModelException {
55          if (object instanceof TemplateBooleanModel) {
56              return super.wrap(object);
57          }
58  
59          // attempt to get the best of both the SimpleMapModel and the MapModel of FM.
60          if (altMapWrapper && object instanceof Map) {
61              return getInstance(object, FriendlyMapModel.FACTORY);
62          }
63  
64          return super.wrap(object);
65      }
66  
67      /***
68       * Attempting to get the best of both worlds of FM's MapModel and SimpleMapModel, by reimplementing the isEmpty(),
69       * keySet() and values() methods. ?keys and ?values built-ins are thus available, just as well as plain Map
70       * methods.
71       */
72      private final static class FriendlyMapModel extends MapModel implements TemplateHashModelEx {
73          static final ModelFactory FACTORY = new ModelFactory() {
74              public TemplateModel create(Object object, ObjectWrapper wrapper) {
75                  return new FriendlyMapModel((Map) object, (BeansWrapper) wrapper);
76              }
77          };
78  
79          public FriendlyMapModel(Map map, BeansWrapper wrapper) {
80              super(map, wrapper);
81          }
82  
83          public boolean isEmpty() {
84              return ((Map) object).isEmpty();
85          }
86  
87          protected Set keySet() {
88              return ((Map) object).keySet();
89          }
90  
91          public TemplateCollectionModel values() {
92              return new CollectionAndSequence(new SimpleSequence(((Map) object).values(), wrapper));
93          }
94      }
95  }