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     * &lt;binding name="effects" value="template:some id is ${currNote.id}."/&gt;
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     * &lt;binding name="effects" value="template:%message-code"/&gt;
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    }