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 }