View Javadoc

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