1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.jameleon.function;
20
21 import net.sf.jameleon.JameleonTagSupport;
22 import net.sf.jameleon.bean.Attribute;
23 import net.sf.jameleon.exception.JameleonException;
24
25 import org.apache.commons.jelly.JellyContext;
26 import org.apache.commons.jelly.MissingAttributeException;
27
28 import java.io.File;
29 import java.util.Iterator;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.List;
33 import java.lang.reflect.Field;
34
35 /***
36 * The <code>AttributeBroker</code> class is used to copy values from
37 * a <code>JellyContext</code> or the script directory to instance variables
38 * in an <code>Attributable</code>.
39 */
40 public class AttributeBroker {
41 /***
42 * The instance of the Attributable to which the values will be transfered to
43 */
44 protected Attributable consumer;
45 /***
46 * A List of attributes or instance variables of the consumer
47 */
48 protected Map attributes = new HashMap();
49
50 public static final boolean REQUIRED = true;
51 public static final boolean NOT_REQUIRED = false;
52 public static final boolean OPTIONAL = false;
53
54 /***
55 * An Attributable is required to instaniate this class.
56 */
57 public AttributeBroker(Attributable consumer) {
58 this.consumer = consumer;
59 }
60
61 /***
62 * Gets the attributes registered for this Attributable
63 */
64 public Map getAttributes(){
65 return attributes;
66 }
67
68 /***
69 * Add an attribute to the list of instance variables supported by the <code>consumer</code>
70 */
71 public void registerAttribute(Attribute attr) {
72 if (attributes.get(attr.getName()) == null) {
73 attributes.put(attr.getName(), attr);
74 }
75 }
76
77 /***
78 * Calls the <code>Attributable.describeAttributes()</code> method.
79 * This needs to be called before anything else is called
80 */
81 public void setUp() {
82 consumer.describeAttributes(this);
83 }
84
85 /***
86 * Copy all variables from the context (JellyContext) to instances variables registered in the
87 * <code>Attributable</code> instance.
88 * @param context - The context that stores the <code>Attributable</code>-independent key/value
89 * pairs which will be used to set the instance variables of the <code>Attributable</code>
90 * to.
91 */
92 public void transferAttributes(JellyContext context) {
93 if (attributes == null || attributes.size() == 0) return;
94 Iterator it = attributes.keySet().iterator();
95 Attribute attr = null;
96 while (it.hasNext()) {
97 attr = (Attribute)attributes.get(it.next());
98 String name = attr.getName();
99 if ( name != null && !attr.isValueSet() ) {
100 if (attr.isInstanceVariable() && (attr.isContextVariable() || attr.getDefaultValue() != null)) {
101 Object value = getValueFromContext(context, attr.getContextName(), attr.getDefaultValue());
102 setConsumerAttribute(attr, value);
103 }else if (!attr.isInstanceVariable() && attr.getValue() == null) {
104
105
106
107
108 attr.setValue(ContextHelper.getVariable(context, name));
109 }
110 }
111 }
112 }
113
114 /***
115 * Validates that all context variables marked as <code>required</code> are set.
116 * @param context The JellyContext of the tag.
117 * @throws MissingAttributeException if any required attributes were not set.
118 */
119 public void validate(JellyContext context) throws MissingAttributeException {
120 Iterator it = attributes.keySet().iterator();
121 StringBuffer errors = new StringBuffer();
122 Attribute attr = null;
123 while (it.hasNext()) {
124 attr = (Attribute)attributes.get(it.next());
125 if (attr.isRequired() && !attr.isValueSet()) {
126 if (getAttributeValue(attr, context) == null) {
127 if (errors.length() > 0) {
128 errors.append(",");
129 }
130 errors.append(attr);
131 }
132 }
133 }
134 if (errors.length() > 0) {
135 throw new MissingAttributeException(errors.toString());
136 }
137 }
138
139
140
141 /***
142 * Attempts to get the value for the given key (<code>contextName</code>) from the context.
143 * If a value is not found from the context, then a defaultValue is used.
144 * @param context - A JellyContext of key/value pairs.
145 * @param contextName - The key from the context
146 * @param defaultValue - A value to return only if the variable is not set in the context.
147 * @return The value for the given context variable.
148 */
149 protected Object getValueFromContext(JellyContext context, String contextName, String defaultValue) {
150 Object value = ContextHelper.getVariable(context, contextName);
151
152 if (value == null) {
153 value = defaultValue;
154 }
155 return value;
156 }
157
158 /***
159 * Gets the real-time value of <code>attr</code> for the consumer
160 * @param attr - The attr that represents the property's value to be returned.
161 * @return The real-time value of the attribute.
162 */
163 public Object getAttributeValue(Attribute attr, JellyContext context){
164 Object value = null;
165 if (attr.isInstanceVariable()) {
166 value = getAttributeValueFromInstance(attr);
167 }else if (attr.getContextName() != null && attr.getContextName().length() > 0) {
168 value = ContextHelper.getVariable(context, attr.getContextName());
169 }else{
170
171
172
173 value = attr.getValue();
174 }
175 if (value == null) {
176 value = attr.getDefaultValue();
177 }
178 return value;
179 }
180
181 public Field getConsumerField(Attribute attr){
182 Field field = null;
183 String name = attr.getName();
184 Class c = consumer.getClass();
185
186
187 while ( c != null &&
188 ! c.equals(JameleonTagSupport.class) ) {
189 try{
190 field = c.getDeclaredField(name);
191 field.setAccessible(true);
192 break;
193 }catch(NoSuchFieldException nsfe){
194 c = c.getSuperclass();
195 }
196 }
197 return field;
198 }
199
200 protected Object getAttributeValueFromInstance(Attribute attr){
201 Object value = null;
202 if (attr.isInstanceVariable()) {
203 Field field = getConsumerField(attr);
204 if (field != null) {
205 try{
206 Class type = field.getType();
207 value = field.get(consumer);
208 if (type.isPrimitive()) {
209 boolean nullValue = false;
210 if (value instanceof Number && ((Number)value).byteValue() == 0) {
211 nullValue = true;
212 }else if (value instanceof Boolean && !((Boolean)value).booleanValue() ) {
213 nullValue = true;
214 }else if (value instanceof Character && ((Character)value).charValue() == 0) {
215 nullValue = true;
216 }
217 if (nullValue) {
218 value = null;
219 }
220 }
221 }catch (IllegalAccessException iae){
222 throw new JameleonException("Please report this problem along with as much info as you can give to the Jameleon bugtracker.", iae);
223 }
224 }
225 }
226 return value;
227 }
228
229 /***
230 * Sets the instance variable of the <code>Attributable</code> to the value with the correct type.
231 * @param attr - The Attribute representing the variable to set.
232 * @param objValue - The value to the instance variable to.
233 */
234 public void setConsumerAttribute(Attribute attr, Object objValue) {
235 String name = attr.getName();
236 Class cOrig = consumer.getClass();
237 Field f = getConsumerField(attr);
238 boolean valueSet = true;
239 if (f != null){
240
241 Class type = f.getType();
242 if (type.isPrimitive()) {
243
244 if (objValue != null) {
245 setConsumerAttributeAsPrimitive(f, objValue);
246 }else{
247 valueSet = false;
248 }
249 }else if ( objValue != null) {
250
251 setConsumerAttributeAsObject(f, objValue);
252 }else{
253 valueSet = false;
254 }
255 }else{
256 throw new JameleonException("Instance variable " + name + " does not exist in " + cOrig);
257 }
258 if (valueSet) {
259 attr.setValue(objValue);
260 }
261 }
262
263 public void setConsumerAttributeAsPrimitive(Field f, Object objValue){
264 if (f != null) {
265 try{
266 Class type = f.getType();
267 Object o = f.get(consumer);
268 String value = null;
269 if (type.isPrimitive()) {
270 if (objValue != null) {
271 value = (String)objValue;
272 }else{
273 value = "0";
274 }
275 if (o instanceof Byte) {
276 f.setByte(consumer,Byte.parseByte(value));
277 }else if (o instanceof Integer) {
278 f.setInt(consumer, Integer.parseInt(value));
279 }else if (o instanceof Long) {
280 f.setLong(consumer,Long.parseLong(value));
281 }else if (o instanceof Short) {
282 f.setShort(consumer, Short.parseShort(value));
283 }else if (o instanceof Double) {
284 f.setDouble(consumer, Double.parseDouble(value));
285 }else if (o instanceof Float) {
286 f.setFloat(consumer,Float.parseFloat(value));
287 }else if (o instanceof Boolean) {
288 if ("true".equals(value) || "yes".equals(value)) {
289 f.setBoolean(consumer, true);
290 }else{
291 f.setBoolean(consumer, false);
292 }
293 }else if (o instanceof Character) {
294 char cValue = '\u0000';
295 if (objValue != null) {
296 cValue = value.charAt(0);
297 }
298 f.setChar(consumer, cValue);
299 }else{
300
301 throw new JameleonException(f.getName() + " of type " + f.getType() +
302 ", not a supported type");
303 }
304 }
305 } catch (IllegalAccessException e) {
306 throw new JameleonException("Instance variable " + f.getName() + " is not settable (may be final) in " + consumer.getClass(), e);
307 }
308 }else{
309 throw new JameleonException("Cannot set a null field!");
310 }
311
312 }
313
314 public void setConsumerAttributeAsObject(Field f, Object objValue){
315 if (f != null) {
316 try{
317 Class type = f.getType();
318 if ( objValue != null) {
319
320 if (objValue instanceof String) {
321 if (type.getName().equals(List.class.getName())) {
322
323 f.set(consumer,ContextHelper.makeList(objValue));
324 }else if (type.getName().equals(Boolean.class.getName())) {
325 f.set(consumer, Boolean.valueOf((String)objValue));
326 }else if (type.getName().equals(Byte.class.getName())){
327 f.set(consumer, Byte.valueOf((String)objValue));
328 }else if (type.getName().equals(Short.class.getName())){
329 f.set(consumer, Short.valueOf((String)objValue));
330 }else if (type.getName().equals(Character.class.getName())){
331 f.set(consumer, new Character(((String)objValue).charAt(0)));
332 }else if (type.getName().equals(Integer.class.getName())){
333 f.set(consumer, Integer.valueOf((String)objValue));
334 }else if (type.getName().equals(Long.class.getName())){
335 f.set(consumer, Long.valueOf((String)objValue));
336 }else if (type.getName().equals(Float.class.getName())){
337 f.set(consumer, Float.valueOf((String)objValue));
338 }else if (type.getName().equals(Double.class.getName())){
339 f.set(consumer, Double.valueOf((String)objValue));
340 }else if (type.getName().equals(File.class.getName())){
341 f.set(consumer, new File((String)objValue));
342 }else{
343 f.set(consumer, objValue);
344 }
345 }else{
346 f.set(consumer, objValue);
347 }
348 }else{
349 f.set(consumer, objValue);
350 }
351 } catch (IllegalAccessException e) {
352 throw new JameleonException("Instance variable " + f.getName() + " is not settable (may be final) in " + consumer.getClass(), e);
353 }
354 }else{
355 throw new JameleonException("Cannot set a null field!");
356 }
357 }
358
359 }