001 /*
002 * TemplateBinding.java
003 */
004
005 package net.sf.tacos.binding;
006
007 import org.apache.commons.lang.StringUtils;
008 import org.apache.hivemind.Location;
009 import org.apache.tapestry.IBinding;
010 import org.apache.tapestry.IComponent;
011 import org.apache.tapestry.binding.AbstractBinding;
012 import org.apache.tapestry.binding.BindingFactory;
013 import org.apache.tapestry.coerce.ValueConverter;
014
015 import java.util.HashMap;
016 import java.util.Iterator;
017 import java.util.Map;
018
019 /**
020 * An implementation of Tapestry {@link IBinding} that provides a binding
021 * similar to {@link org.apache.tapestry.binding.LiteralBinding} with the
022 * additional benefit that it can also include variables.
023 *
024 * The variables should be put into <code>${}</code> .<br/>
025 *
026 * Here's an example:
027 * <code>
028 * <binding name="effects" value="template:some id is ${currNote.id}."/>
029 * </code>
030 * <p/>
031 * <p/>
032 * The binding can also get its content from resource bundles. It will still
033 * go on and process it - looking for variables to substitute.<br/>
034 * Example:
035 * <code>
036 * <binding name="effects" value="template:%message-code"/>
037 * </code>
038 *
039 */
040 public class TemplateBinding extends AbstractBinding
041 {
042 /**
043 * The root object against which the nested property name is evaluated.
044 */
045 private final IComponent root;
046 /**
047 * The expression that have variables, as a string.
048 */
049 private String expression;
050 /**
051 * cache the variable expression and it's compiled binding.
052 */
053 private Map variableBindings;
054 /**
055 * the BindingFactory for creating the inside variable IBinding.
056 */
057 private BindingFactory nestedBindingFactory;
058
059 /**
060 * constructor
061 *
062 * @param description
063 * @param location
064 * @param valueConverter
065 * @param root The root object against which the nested property name is evaluated.
066 * @param expression plain string or message code in page's specification file.
067 * @param nestedBindingFactory Internal used BindingFactory
068 */
069 public TemplateBinding(String description, Location location, ValueConverter valueConverter,
070 IComponent root, String expression, BindingFactory nestedBindingFactory)
071 {
072 super(description, valueConverter, location);
073 this.root = root;
074 this.expression = extractExpression(expression);
075 this.nestedBindingFactory = nestedBindingFactory;
076 buildingVariableBindings();
077 }
078
079 /**
080 * build variable binding map.
081 */
082 private void buildingVariableBindings()
083 {
084 if (variableBindings == null) {
085 variableBindings = new HashMap();
086 }
087 String least = expression;
088 int start;
089 while ((start = least.indexOf("${")) != -1) {
090 least = least.substring(start);
091 int length = least.indexOf("}");
092 String innerExp = least.substring(2, length);
093 IBinding expBinding = nestedBindingFactory.createBinding(root, getDescription(), innerExp, getLocation());
094 variableBindings.put("${" + innerExp + "}", expBinding);
095 least = least.substring(length + 1);
096 }
097 }
098
099 /**
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 }