View Javadoc

1   /*
2    * TemplateBinding.java
3    */ 
4   
5   package net.sf.tacos.binding;
6   
7   import org.apache.commons.lang.StringUtils;
8   import org.apache.hivemind.Location;
9   import org.apache.tapestry.IBinding;
10  import org.apache.tapestry.IComponent;
11  import org.apache.tapestry.binding.AbstractBinding;
12  import org.apache.tapestry.binding.BindingFactory;
13  import org.apache.tapestry.coerce.ValueConverter;
14  
15  import java.util.HashMap;
16  import java.util.Iterator;
17  import java.util.Map;
18  
19  /**
20   * An implementation of Tapestry {@link IBinding} that provides a binding
21   * similar to {@link org.apache.tapestry.binding.LiteralBinding} with the 
22   * additional benefit that it can also include variables.
23   *
24   * The variables should be put into <code>${}</code> .<br/>
25   *
26   * Here's an example:
27   * <code>
28   * &lt;binding name="effects" value="template:some id is ${currNote.id}."/&gt;
29   * </code>
30   * <p/>
31   * <p/>
32   * The binding can also get its content from resource bundles. It will still
33   * go on and process it - looking for variables to substitute.<br/>
34   * Example:
35   * <code>
36   * &lt;binding name="effects" value="template:%message-code"/&gt;
37   * </code>
38   *
39   */
40  public class TemplateBinding extends AbstractBinding
41  {
42      /**
43       * The root object against which the nested property name is evaluated.
44       */
45      private final IComponent root;
46      /**
47       * The expression that have variables, as a string.
48       */
49      private String expression;
50      /**
51       * cache the variable expression and it's compiled binding.
52       */
53      private Map variableBindings;
54      /**
55       * the BindingFactory for creating the inside variable IBinding.
56       */
57      private BindingFactory nestedBindingFactory;
58  
59      /**
60       * constructor
61       *
62       * @param description
63       * @param location
64       * @param valueConverter
65       * @param root                 The root object against which the nested property name is evaluated.
66       * @param expression           plain string or message code in page's specification file.
67       * @param nestedBindingFactory Internal used BindingFactory
68       */
69      public TemplateBinding(String description, Location location, ValueConverter valueConverter,
70                             IComponent root, String expression, BindingFactory nestedBindingFactory)
71      {
72          super(description, valueConverter, location);
73          this.root = root;
74          this.expression = extractExpression(expression);
75          this.nestedBindingFactory = nestedBindingFactory;
76          buildingVariableBindings();
77      }
78  
79      /**
80       * build variable binding map.
81       */
82      private void buildingVariableBindings()
83      {
84          if (variableBindings == null) {
85              variableBindings = new HashMap();
86          }
87          String least = expression;
88          int start;
89          while ((start = least.indexOf("${")) != -1) {
90              least = least.substring(start);
91              int length = least.indexOf("}");
92              String innerExp = least.substring(2, length);
93              IBinding expBinding = nestedBindingFactory.createBinding(root, getDescription(), innerExp, getLocation());
94              variableBindings.put("${" + innerExp + "}", expBinding);
95              least = least.substring(length + 1);
96          }
97      }
98  
99      /**
100      * Extract expression if it is indicated as message code.
101      *
102      * @param expr
103      * @return real evaluated expression.
104      */
105     private String extractExpression(String expr)
106     {
107         if (expr.startsWith("%")) {
108             String key = expr.substring(1);
109             return root.getMessages().getMessage(key);
110         }
111         return expr;
112     }
113 
114     /**
115      * Evaluate the value with inside variables.
116      *
117      * @return binding value
118      */
119     public Object getObject()
120     {
121         String value = expression;
122         Iterator it = variableBindings.entrySet().iterator();
123         while (it.hasNext()) {
124             Map.Entry entry = (Map.Entry) it.next();
125             String key = (String) entry.getKey();
126             IBinding exp = (IBinding) entry.getValue();
127             String replacement = "";
128             Object object = exp.getObject();
129             if (object != null) {
130                 replacement = object.toString();
131             }
132             value = StringUtils.replace(value, key, replacement);
133         }
134         return value;
135     }
136 
137     /**
138      * Should be variant because of inside ognl expression.
139      *
140      * @return indicate it is variant.
141      * @see org.apache.tapestry.IBinding#isInvariant()
142      */
143     public boolean isInvariant()
144     {
145         return false;
146     }
147 }