View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.facelets;
21  
22  import org.apache.commons.beanutils.PropertyUtils;
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.myfaces.tobago.component.Attributes;
25  import org.apache.myfaces.tobago.component.SupportsMarkup;
26  import org.apache.myfaces.tobago.component.SupportsRenderedPartially;
27  import org.apache.myfaces.tobago.context.Markup;
28  import org.apache.myfaces.tobago.el.ConstantMethodBinding;
29  import org.apache.myfaces.tobago.util.ComponentUtils;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import javax.el.ELException;
34  import javax.el.ExpressionFactory;
35  import javax.el.MethodExpression;
36  import javax.el.ValueExpression;
37  import javax.faces.FacesException;
38  import javax.faces.component.ActionSource;
39  import javax.faces.component.ActionSource2;
40  import javax.faces.component.EditableValueHolder;
41  import javax.faces.component.UIComponent;
42  import javax.faces.component.ValueHolder;
43  import javax.faces.convert.Converter;
44  import javax.faces.event.MethodExpressionActionListener;
45  import javax.faces.event.MethodExpressionValueChangeListener;
46  import javax.faces.validator.MethodExpressionValidator;
47  import javax.faces.view.facelets.ComponentHandler;
48  import javax.faces.view.facelets.FaceletContext;
49  import javax.faces.view.facelets.TagAttribute;
50  import javax.faces.view.facelets.TagConfig;
51  import javax.faces.view.facelets.TagException;
52  import javax.faces.view.facelets.TagHandler;
53  import java.beans.IntrospectionException;
54  import java.beans.PropertyDescriptor;
55  
56  public final class AttributeHandler extends TagHandler {
57  
58    private static final Logger LOG = LoggerFactory.getLogger(AttributeHandler.class);
59  
60    private final TagAttribute name;
61  
62    private final TagAttribute value;
63  
64    private final TagAttribute mode;
65  
66    public AttributeHandler(TagConfig config) {
67      super(config);
68      this.name = getRequiredAttribute(Attributes.NAME);
69      this.value = getRequiredAttribute(Attributes.VALUE);
70      this.mode = getAttribute(Attributes.MODE);
71    }
72  
73    public void apply(FaceletContext faceletContext, UIComponent parent)
74        throws FacesException, ELException {
75      if (parent == null) {
76        throw new TagException(tag, "Parent UIComponent was null");
77      }
78  
79      if (ComponentHandler.isNew(parent)) {
80  
81        if (mode != null) {
82          if ("isNotSet".equals(mode.getValue())) {
83            boolean result = false;
84            String expressionString = value.getValue();
85            if (!value.isLiteral()) {
86              while (isSimpleExpression(expressionString)) {
87                if (isMethodOrValueExpression(expressionString)) {
88                  ValueExpression expression
89                      = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
90                  if (expression == null) {
91                    result = true;
92                    break;
93                  } else {
94                    expressionString = expression.getExpressionString();
95                  }
96                } else {
97                  result = false;
98                  break;
99                }
100             }
101           } else {
102             result = StringUtils.isEmpty(expressionString);
103           }
104           parent.getAttributes().put(name.getValue(), result);
105         } else if ("isSet".equals(mode.getValue())) {
106           boolean result = true;
107           String expressionString = value.getValue();
108           if (!value.isLiteral()) {
109             while (isSimpleExpression(expressionString)) {
110               if (isMethodOrValueExpression(expressionString)) {
111                 ValueExpression expression
112                     = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
113                 if (expression == null) {
114                   result = false;
115                   break;
116                 } else {
117                   expressionString = expression.getExpressionString();
118                 }
119               } else {
120                 result = true;
121                 break;
122               }
123             }
124           } else {
125             result = StringUtils.isNotEmpty(expressionString);
126           }
127           parent.getAttributes().put(name.getValue(), result);
128         } else if ("action".equals(mode.getValue())) {
129           String expressionString = value.getValue();
130           while (isSimpleExpression(expressionString)) {
131             if (isMethodOrValueExpression(expressionString)) {
132               ValueExpression expression
133                   = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
134               if (expression == null) {
135                 // when the action hasn't been set while using a composition.
136                 if (LOG.isDebugEnabled()) {
137                   LOG.debug("Variable can't be resolved: value='" + expressionString + "'");
138                 }
139                 expressionString = null;
140                 break;
141               } else {
142                 expressionString = expression.getExpressionString();
143               }
144             } else {
145               break;
146             }
147           }
148           if (expressionString != null) {
149             ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
150             MethodExpression action = new TagMethodExpression(value, expressionFactory.createMethodExpression(
151                 faceletContext, expressionString, String.class, ComponentUtils.ACTION_ARGS));
152             ((ActionSource2) parent).setActionExpression(action);
153           }
154         } else if ("actionListener".equals(mode.getValue())) {
155           String expressionString = value.getValue();
156           while (isSimpleExpression(expressionString)) {
157             if (isMethodOrValueExpression(expressionString)) {
158               ValueExpression expression
159                   = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
160               if (expression == null) {
161                 if (LOG.isDebugEnabled()) {
162                   // when the action hasn't been set while using a composition.
163                   LOG.debug("Variable can't be resolved: value='" + expressionString + "'");
164                 }
165                 expressionString = null;
166                 break;
167               } else {
168                 expressionString = expression.getExpressionString();
169               }
170             } else {
171               LOG.warn("Only expressions are supported mode=actionListener value='" + expressionString + "'");
172               expressionString = null;
173               break;
174             }
175           }
176           if (expressionString != null) {
177             ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
178             MethodExpression actionListener = new TagMethodExpression(value, expressionFactory.createMethodExpression(
179                 faceletContext, expressionString, null, ComponentUtils.ACTION_LISTENER_ARGS));
180             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(actionListener));
181           }
182         } else if ("actionFromValue".equals(mode.getValue())) {
183           if (!value.isLiteral()) {
184             String result = value.getValue(faceletContext);
185             parent.getAttributes().put(name.getValue(), new ConstantMethodBinding(result));
186           }
187         } else if ("valueIfSet".equals(mode.getValue())) {
188           String expressionString = value.getValue();
189           String lastExpressionString = null;
190           while (isMethodOrValueExpression(expressionString) && isSimpleExpression(expressionString)) {
191             ValueExpression expression
192                 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
193             if (expression != null) {
194               lastExpressionString = expressionString;
195               expressionString = expression.getExpressionString();
196             } else {
197               // restore last value
198               expressionString = lastExpressionString;
199               break;
200             }
201           }
202           if (expressionString != null) {
203             final String attributeName = name.getValue(faceletContext);
204             if (containsMethodOrValueExpression(expressionString)) {
205               ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
206               parent.setValueExpression(attributeName, expression);
207             } else {
208               final Object literalValue = getValue(faceletContext, parent, expressionString, attributeName);
209               parent.getAttributes().put(attributeName, literalValue);
210             }
211           }
212         } else {
213           throw new FacesException("Type " + mode + " not supported");
214         }
215       } else {
216 
217         String nameValue = name.getValue(faceletContext);
218         if (Attributes.RENDERED.equals(nameValue)) {
219           if (value.isLiteral()) {
220             parent.setRendered(value.getBoolean(faceletContext));
221           } else {
222             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Boolean.class));
223           }
224         } else if (Attributes.RENDERED_PARTIALLY.equals(nameValue)
225             && parent instanceof SupportsRenderedPartially) {
226 
227           if (value.isLiteral()) {
228             String[] components = ComponentUtils.splitList(value.getValue());
229             ((SupportsRenderedPartially) parent).setRenderedPartially(components);
230           } else {
231             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Object.class));
232           }
233         } else if (Attributes.STYLE_CLASS.equals(nameValue)) {
234           // TODO expression
235           ComponentUtils.setStyleClasses(parent, value.getValue());
236         } else if (Attributes.MARKUP.equals(nameValue)) {
237           if (parent instanceof SupportsMarkup) {
238             if (value.isLiteral()) {
239               ((SupportsMarkup) parent).setMarkup(Markup.valueOf(value.getValue()));
240             } else {
241               ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
242               parent.setValueExpression(nameValue, expression);
243             }
244           } else {
245             LOG.error("Component is not instanceof SupportsMarkup. Instance is: " + parent.getClass().getName());
246           }
247         } else if (parent instanceof EditableValueHolder && Attributes.VALIDATOR.equals(nameValue)) {
248           MethodExpression methodExpression = getMethodExpression(faceletContext, null, ComponentUtils.VALIDATOR_ARGS);
249           if (methodExpression != null) {
250             ((EditableValueHolder) parent).addValidator(new MethodExpressionValidator(methodExpression));
251           }
252         } else if (parent instanceof EditableValueHolder
253             && Attributes.VALUE_CHANGE_LISTENER.equals(nameValue)) {
254           MethodExpression methodExpression =
255               getMethodExpression(faceletContext, null, ComponentUtils.VALUE_CHANGE_LISTENER_ARGS);
256           if (methodExpression != null) {
257             ((EditableValueHolder) parent).addValueChangeListener(
258                 new MethodExpressionValueChangeListener(methodExpression));
259           }
260         } else if (parent instanceof ValueHolder && Attributes.CONVERTER.equals(nameValue)) {
261           setConverter(faceletContext, parent, nameValue);
262         } else if (parent instanceof ActionSource && Attributes.ACTION.equals(nameValue)) {
263           MethodExpression action = getMethodExpression(faceletContext, String.class, ComponentUtils.ACTION_ARGS);
264           if (action != null) {
265             ((ActionSource2) parent).setActionExpression(action);
266           }
267         } else if (parent instanceof ActionSource && Attributes.ACTION_LISTENER.equals(nameValue)) {
268           MethodExpression action = getMethodExpression(faceletContext, null, ComponentUtils.ACTION_LISTENER_ARGS);
269           if (action != null) {
270             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(action));
271           }
272         } else if (!parent.getAttributes().containsKey(nameValue)) {
273           if (value.isLiteral()) {
274             parent.getAttributes().put(nameValue, value.getValue());
275           } else {
276             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Object.class));
277           }
278         }
279       }
280     }
281   }
282 
283   private boolean isMethodOrValueExpression(String string) {
284     return (string.startsWith("${") || string.startsWith("#{")) && string.endsWith("}");
285   }
286 
287   private boolean containsMethodOrValueExpression(String string) {
288     return (StringUtils.contains(string, "${") || StringUtils.contains(string, "#{"))
289         && StringUtils.contains(string, "}");
290   }
291 
292   private boolean isSimpleExpression(String string) {
293     return string.indexOf('.') < 0 && string.indexOf('[') < 0;
294   }
295 
296   private String removeElParenthesis(String string) {
297     return string.substring(2, string.length() - 1);
298   }
299 
300   private ValueExpression getExpression(FaceletContext faceletContext) {
301     String myValue = removeElParenthesis(value.getValue());
302     return faceletContext.getVariableMapper().resolveVariable(myValue);
303   }
304 
305   private MethodExpression getMethodExpression(FaceletContext faceletContext, Class returnType, Class[] args) {
306     // in a composition may be we get the method expression string from the current variable mapper
307     // the expression can be empty
308     // in this case return nothing
309     if (value.getValue().startsWith("${")) {
310       ValueExpression expression = getExpression(faceletContext);
311       if (expression != null) {
312         ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
313         return new TagMethodExpression(value, expressionFactory.createMethodExpression(faceletContext,
314             expression.getExpressionString(), returnType, args));
315       } else {
316         return null;
317       }
318     } else {
319       return value.getMethodExpression(faceletContext, returnType, args);
320     }
321   }
322 
323   private Object getValue(
324       FaceletContext faceletContext, UIComponent parent, String expressionString, String attributeName) {
325     Class type = Object.class;
326     try {
327       type = PropertyUtils.getReadMethod(
328           new PropertyDescriptor(attributeName, parent.getClass())).getReturnType();
329     } catch (IntrospectionException e) {
330       LOG.warn("Can't determine expected type", e);
331     }
332     ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
333     final ValueExpression valueExpression = expressionFactory
334         .createValueExpression(faceletContext, expressionString, type);
335     return valueExpression.getValue(faceletContext);
336   }
337 
338   private void setConverter(FaceletContext faceletContext, UIComponent parent, String nameValue) {
339     // in a composition may be we get the converter expression string from the current variable mapper
340     // the expression can be empty
341     // in this case return nothing
342     if (value.getValue().startsWith("${")) {
343       ValueExpression expression = getExpression(faceletContext);
344       if (expression != null) {
345         setConverter(faceletContext, parent, nameValue, expression);
346       }
347     } else {
348       setConverter(faceletContext, parent, nameValue, value.getValueExpression(faceletContext, Object.class));
349     }
350   }
351 
352   private void setConverter(
353       FaceletContext faceletContext, UIComponent parent, String nameValue, ValueExpression expression) {
354     if (expression.isLiteralText()) {
355       Converter converter =
356           faceletContext.getFacesContext().getApplication().createConverter(expression.getExpressionString());
357       ((ValueHolder) parent).setConverter(converter);
358     } else {
359       parent.setValueExpression(nameValue, expression);
360     }
361   }
362 }