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.myfaces.tobago.component.Attributes;
24  import org.apache.myfaces.tobago.component.SupportsMarkup;
25  import org.apache.myfaces.tobago.component.SupportsRenderedPartially;
26  import org.apache.myfaces.tobago.context.Markup;
27  import org.apache.myfaces.tobago.el.ConstantMethodBinding;
28  import org.apache.myfaces.tobago.internal.util.StringUtils;
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) throws ELException {
74      if (parent == null) {
75        throw new TagException(tag, "Parent UIComponent was null");
76      }
77  
78      if (ComponentHandler.isNew(parent)) {
79  
80        if (mode != null) {
81          if ("isNotSet".equals(mode.getValue())) {
82            boolean result = false;
83            String expressionString = value.getValue();
84            if (!value.isLiteral()) {
85              while (isSimpleExpression(expressionString)) {
86                if (isMethodOrValueExpression(expressionString)) {
87                  ValueExpression expression
88                      = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
89                  if (expression == null) {
90                    result = true;
91                    break;
92                  } else {
93                    expressionString = expression.getExpressionString();
94                  }
95                } else {
96                  result = false;
97                  break;
98                }
99              }
100           } else {
101             result = StringUtils.isEmpty(expressionString);
102           }
103           parent.getAttributes().put(name.getValue(), result);
104         } else if ("isSet".equals(mode.getValue())) {
105           boolean result = true;
106           String expressionString = value.getValue();
107           if (!value.isLiteral()) {
108             while (isSimpleExpression(expressionString)) {
109               if (isMethodOrValueExpression(expressionString)) {
110                 ValueExpression expression
111                     = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
112                 if (expression == null) {
113                   result = false;
114                   break;
115                 } else {
116                   expressionString = expression.getExpressionString();
117                 }
118               } else {
119                 result = true;
120                 break;
121               }
122             }
123           } else {
124             result = StringUtils.isNotEmpty(expressionString);
125           }
126           parent.getAttributes().put(name.getValue(), result);
127         } else if ("action".equals(mode.getValue())) {
128           String expressionString = value.getValue();
129           while (isSimpleExpression(expressionString)) {
130             if (isMethodOrValueExpression(expressionString)) {
131               ValueExpression expression
132                   = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
133               if (expression == null) {
134                 // when the action hasn't been set while using a composition.
135                 if (LOG.isDebugEnabled()) {
136                   LOG.debug("Variable can't be resolved: value='" + expressionString + "'");
137                 }
138                 expressionString = null;
139                 break;
140               } else {
141                 expressionString = expression.getExpressionString();
142               }
143             } else {
144               break;
145             }
146           }
147           if (expressionString != null) {
148             ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
149             MethodExpression action = new TagMethodExpression(value, expressionFactory.createMethodExpression(
150                 faceletContext, expressionString, String.class, ComponentUtils.ACTION_ARGS));
151             ((ActionSource2) parent).setActionExpression(action);
152           }
153         } else if ("actionListener".equals(mode.getValue())) {
154           String expressionString = value.getValue();
155           while (isSimpleExpression(expressionString)) {
156             if (isMethodOrValueExpression(expressionString)) {
157               ValueExpression expression
158                   = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
159               if (expression == null) {
160                 if (LOG.isDebugEnabled()) {
161                   // when the action hasn't been set while using a composition.
162                   LOG.debug("Variable can't be resolved: value='" + expressionString + "'");
163                 }
164                 expressionString = null;
165                 break;
166               } else {
167                 expressionString = expression.getExpressionString();
168               }
169             } else {
170               LOG.warn("Only expressions are supported mode=actionListener value='" + expressionString + "'");
171               expressionString = null;
172               break;
173             }
174           }
175           if (expressionString != null) {
176             ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
177             MethodExpression actionListener = new TagMethodExpression(value, expressionFactory.createMethodExpression(
178                 faceletContext, expressionString, null, ComponentUtils.ACTION_LISTENER_ARGS));
179             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(actionListener));
180           }
181         } else if ("actionFromValue".equals(mode.getValue())) {
182           if (!value.isLiteral()) {
183             String result = value.getValue(faceletContext);
184             parent.getAttributes().put(name.getValue(), new ConstantMethodBinding(result));
185           }
186         } else if ("valueIfSet".equals(mode.getValue())) {
187           String expressionString = value.getValue();
188           String lastExpressionString = null;
189           while (isMethodOrValueExpression(expressionString) && isSimpleExpression(expressionString)) {
190             ValueExpression expression
191                 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
192             if (expression != null) {
193               lastExpressionString = expressionString;
194               expressionString = expression.getExpressionString();
195             } else {
196               // restore last value
197               expressionString = lastExpressionString;
198               break;
199             }
200           }
201           if (expressionString != null) {
202             final String attributeName = name.getValue(faceletContext);
203             if (containsMethodOrValueExpression(expressionString)) {
204               ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
205               parent.setValueExpression(attributeName, expression);
206             } else {
207               final Object literalValue = getValue(faceletContext, parent, expressionString, attributeName);
208               parent.getAttributes().put(attributeName, literalValue);
209             }
210           }
211         } else {
212           throw new FacesException("Type " + mode + " not supported");
213         }
214       } else {
215 
216         String nameValue = name.getValue(faceletContext);
217         if (Attributes.RENDERED.equals(nameValue)) {
218           if (value.isLiteral()) {
219             parent.setRendered(value.getBoolean(faceletContext));
220           } else {
221             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Boolean.class));
222           }
223         } else if (Attributes.RENDERED_PARTIALLY.equals(nameValue)
224             && parent instanceof SupportsRenderedPartially) {
225 
226           if (value.isLiteral()) {
227             String[] components = ComponentUtils.splitList(value.getValue());
228             ((SupportsRenderedPartially) parent).setRenderedPartially(components);
229           } else {
230             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Object.class));
231           }
232         } else if (Attributes.STYLE_CLASS.equals(nameValue)) {
233           // TODO expression
234           ComponentUtils.setStyleClasses(parent, value.getValue());
235         } else if (Attributes.MARKUP.equals(nameValue)) {
236           if (parent instanceof SupportsMarkup) {
237             if (value.isLiteral()) {
238               ((SupportsMarkup) parent).setMarkup(Markup.valueOf(value.getValue()));
239             } else {
240               ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
241               parent.setValueExpression(nameValue, expression);
242             }
243           } else {
244             LOG.error("Component is not instanceof SupportsMarkup. Instance is: " + parent.getClass().getName());
245           }
246         } else if (parent instanceof EditableValueHolder && Attributes.VALIDATOR.equals(nameValue)) {
247           MethodExpression methodExpression = getMethodExpression(faceletContext, null, ComponentUtils.VALIDATOR_ARGS);
248           if (methodExpression != null) {
249             ((EditableValueHolder) parent).addValidator(new MethodExpressionValidator(methodExpression));
250           }
251         } else if (parent instanceof EditableValueHolder
252             && Attributes.VALUE_CHANGE_LISTENER.equals(nameValue)) {
253           MethodExpression methodExpression =
254               getMethodExpression(faceletContext, null, ComponentUtils.VALUE_CHANGE_LISTENER_ARGS);
255           if (methodExpression != null) {
256             ((EditableValueHolder) parent).addValueChangeListener(
257                 new MethodExpressionValueChangeListener(methodExpression));
258           }
259         } else if (parent instanceof ValueHolder && Attributes.CONVERTER.equals(nameValue)) {
260           setConverter(faceletContext, parent, nameValue);
261         } else if (parent instanceof ActionSource && Attributes.ACTION.equals(nameValue)) {
262           MethodExpression action = getMethodExpression(faceletContext, String.class, ComponentUtils.ACTION_ARGS);
263           if (action != null) {
264             ((ActionSource2) parent).setActionExpression(action);
265           }
266         } else if (parent instanceof ActionSource && Attributes.ACTION_LISTENER.equals(nameValue)) {
267           MethodExpression action = getMethodExpression(faceletContext, null, ComponentUtils.ACTION_LISTENER_ARGS);
268           if (action != null) {
269             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(action));
270           }
271         } else if (!parent.getAttributes().containsKey(nameValue)) {
272           if (value.isLiteral()) {
273             parent.getAttributes().put(nameValue, value.getValue());
274           } else {
275             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Object.class));
276           }
277         }
278       }
279     }
280   }
281 
282   private boolean isMethodOrValueExpression(String string) {
283     return (string.startsWith("${") || string.startsWith("#{")) && string.endsWith("}");
284   }
285 
286   private boolean containsMethodOrValueExpression(String string) {
287     return (string.contains("${") || string.contains("#{")) && string.contains("}");
288   }
289 
290   private boolean isSimpleExpression(String string) {
291     return string.indexOf('.') < 0 && string.indexOf('[') < 0;
292   }
293 
294   private String removeElParenthesis(String string) {
295     return string.substring(2, string.length() - 1);
296   }
297 
298   private ValueExpression getExpression(FaceletContext faceletContext) {
299     String myValue = removeElParenthesis(value.getValue());
300     return faceletContext.getVariableMapper().resolveVariable(myValue);
301   }
302 
303   private MethodExpression getMethodExpression(FaceletContext faceletContext, Class returnType, Class[] args) {
304     // in a composition may be we get the method expression string from the current variable mapper
305     // the expression can be empty
306     // in this case return nothing
307     if (value.getValue().startsWith("${")) {
308       ValueExpression expression = getExpression(faceletContext);
309       if (expression != null) {
310         ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
311         return new TagMethodExpression(value, expressionFactory.createMethodExpression(faceletContext,
312             expression.getExpressionString(), returnType, args));
313       } else {
314         return null;
315       }
316     } else {
317       return value.getMethodExpression(faceletContext, returnType, args);
318     }
319   }
320 
321   private Object getValue(
322       FaceletContext faceletContext, UIComponent parent, String expressionString, String attributeName) {
323     Class type = Object.class;
324     try {
325       type = PropertyUtils.getReadMethod(
326           new PropertyDescriptor(attributeName, parent.getClass())).getReturnType();
327     } catch (IntrospectionException e) {
328       LOG.warn("Can't determine expected type", e);
329     }
330     ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
331     final ValueExpression valueExpression = expressionFactory
332         .createValueExpression(faceletContext, expressionString, type);
333     return valueExpression.getValue(faceletContext);
334   }
335 
336   private void setConverter(FaceletContext faceletContext, UIComponent parent, String nameValue) {
337     // in a composition may be we get the converter expression string from the current variable mapper
338     // the expression can be empty
339     // in this case return nothing
340     if (value.getValue().startsWith("${")) {
341       ValueExpression expression = getExpression(faceletContext);
342       if (expression != null) {
343         setConverter(faceletContext, parent, nameValue, expression);
344       }
345     } else {
346       setConverter(faceletContext, parent, nameValue, value.getValueExpression(faceletContext, Object.class));
347     }
348   }
349 
350   private void setConverter(
351       FaceletContext faceletContext, UIComponent parent, String nameValue, ValueExpression expression) {
352     if (expression.isLiteralText()) {
353       Converter converter =
354           faceletContext.getFacesContext().getApplication().createConverter(expression.getExpressionString());
355       ((ValueHolder) parent).setConverter(converter);
356     } else {
357       parent.setValueExpression(nameValue, expression);
358     }
359   }
360 }