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.myfaces.tobago.component.Attributes;
23  import org.apache.myfaces.tobago.component.Visual;
24  import org.apache.myfaces.tobago.context.Markup;
25  import org.apache.myfaces.tobago.el.ConstantMethodExpression;
26  import org.apache.myfaces.tobago.internal.util.StringUtils;
27  import org.apache.myfaces.tobago.util.ComponentUtils;
28  
29  import javax.el.ELException;
30  import javax.el.ExpressionFactory;
31  import javax.el.MethodExpression;
32  import javax.el.ValueExpression;
33  import javax.faces.FacesException;
34  import javax.faces.component.ActionSource;
35  import javax.faces.component.ActionSource2;
36  import javax.faces.component.EditableValueHolder;
37  import javax.faces.component.UIComponent;
38  import javax.faces.component.ValueHolder;
39  import javax.faces.convert.Converter;
40  import javax.faces.event.MethodExpressionActionListener;
41  import javax.faces.event.MethodExpressionValueChangeListener;
42  import javax.faces.validator.MethodExpressionValidator;
43  import javax.faces.view.facelets.ComponentHandler;
44  import javax.faces.view.facelets.FaceletContext;
45  import javax.faces.view.facelets.TagAttribute;
46  import javax.faces.view.facelets.TagConfig;
47  import javax.faces.view.facelets.TagException;
48  import javax.faces.view.facelets.TagHandler;
49  import java.beans.IntrospectionException;
50  import java.beans.PropertyDescriptor;
51  
52  import org.apache.commons.beanutils.PropertyUtils;
53  import org.slf4j.Logger;
54  import org.slf4j.LoggerFactory;
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(final TagConfig config) {
67      super(config);
68      this.name = getRequiredAttribute(Attributes.name.getName());
69      this.value = getRequiredAttribute(Attributes.value.getName());
70      this.mode = getAttribute(Attributes.mode.getName());
71    }
72  
73    @Override
74    public void apply(final FaceletContext faceletContext, final UIComponent parent) throws 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                  final 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                 final 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               final 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             final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
150             final 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               final 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             final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
178             final MethodExpression actionListener
179                 = new TagMethodExpression(value, expressionFactory.createMethodExpression(
180                 faceletContext, expressionString, null, ComponentUtils.ACTION_LISTENER_ARGS));
181             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(actionListener));
182           }
183         } else if ("actionFromValue".equals(mode.getValue())) {
184           if (!value.isLiteral()) {
185             final String result = value.getValue(faceletContext);
186             parent.getAttributes().put(name.getValue(), new ConstantMethodExpression(result));
187           }
188         } else if ("valueIfSet".equals(mode.getValue())) {
189           String expressionString = value.getValue();
190           String lastExpressionString = null;
191           while (isMethodOrValueExpression(expressionString) && isSimpleExpression(expressionString)) {
192             final ValueExpression expression
193                 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
194             if (expression != null) {
195               lastExpressionString = expressionString;
196               expressionString = expression.getExpressionString();
197             } else {
198               // restore last value
199               expressionString = lastExpressionString;
200               break;
201             }
202           }
203           if (expressionString != null) {
204             final String attributeName = name.getValue(faceletContext);
205             if (containsMethodOrValueExpression(expressionString)) {
206               final ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
207               parent.setValueExpression(attributeName, expression);
208             } else {
209               final Object literalValue = getValue(faceletContext, parent, expressionString, attributeName);
210               parent.getAttributes().put(attributeName, literalValue);
211             }
212           }
213         } else {
214           throw new FacesException("Type " + mode + " not supported");
215         }
216       } else {
217 
218         final Attributes nameValue = Attributes.valueOfFailsafe(name.getValue(faceletContext));
219         if (Attributes.rendered == nameValue) {
220           if (value.isLiteral()) {
221             parent.setRendered(value.getBoolean(faceletContext));
222           } else {
223             parent.setValueExpression(nameValue.getName(), value.getValueExpression(faceletContext, Boolean.class));
224           }
225         } else if (Attributes.markup == nameValue) {
226           if (parent instanceof Visual) {
227             if (value.isLiteral()) {
228               ((Visual) parent).setMarkup(Markup.valueOf(value.getValue()));
229             } else {
230               final ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
231               parent.setValueExpression(nameValue.getName(), expression);
232             }
233           } else {
234             LOG.error("Component is not instanceof Visual. Instance is: " + parent.getClass().getName());
235           }
236         } else if (parent instanceof EditableValueHolder && Attributes.validator == nameValue) {
237           final MethodExpression methodExpression
238               = getMethodExpression(faceletContext, null, ComponentUtils.VALIDATOR_ARGS);
239           if (methodExpression != null) {
240             ((EditableValueHolder) parent).addValidator(new MethodExpressionValidator(methodExpression));
241           }
242         } else if (parent instanceof EditableValueHolder && Attributes.valueChangeListener == nameValue) {
243           final MethodExpression methodExpression =
244               getMethodExpression(faceletContext, null, ComponentUtils.VALUE_CHANGE_LISTENER_ARGS);
245           if (methodExpression != null) {
246             ((EditableValueHolder) parent).addValueChangeListener(
247                 new MethodExpressionValueChangeListener(methodExpression));
248           }
249         } else if (parent instanceof ValueHolder && Attributes.converter == nameValue) {
250           setConverter(faceletContext, parent, nameValue.getName());
251         } else if (parent instanceof ActionSource && Attributes.action.equals(nameValue)) {
252           final MethodExpression action = getMethodExpression(faceletContext, String.class, ComponentUtils.ACTION_ARGS);
253           if (action != null) {
254             ((ActionSource2) parent).setActionExpression(action);
255           }
256         } else if (parent instanceof ActionSource && Attributes.actionListener == nameValue) {
257           final MethodExpression action
258               = getMethodExpression(faceletContext, null, ComponentUtils.ACTION_LISTENER_ARGS);
259           if (action != null) {
260             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(action));
261           }
262         } else if (!parent.getAttributes().containsKey(nameValue.getName())) {
263           if (value.isLiteral()) {
264             parent.getAttributes().put(nameValue.getName(), value.getValue());
265           } else {
266             parent.setValueExpression(nameValue.getName(), value.getValueExpression(faceletContext, Object.class));
267           }
268         }
269       }
270     }
271   }
272 
273   private boolean isMethodOrValueExpression(final String string) {
274     return (string.startsWith("${") || string.startsWith("#{")) && string.endsWith("}");
275   }
276 
277   private boolean containsMethodOrValueExpression(final String string) {
278     return (string.contains("${") || string.contains("#{")) && string.contains("}");
279   }
280 
281   private boolean isSimpleExpression(final String string) {
282     return string.indexOf('.') < 0 && string.indexOf('[') < 0;
283   }
284 
285   private String removeElParenthesis(final String string) {
286     return string.substring(2, string.length() - 1);
287   }
288 
289   private ValueExpression getExpression(final FaceletContext faceletContext) {
290     final String myValue = removeElParenthesis(value.getValue());
291     return faceletContext.getVariableMapper().resolveVariable(myValue);
292   }
293 
294   private MethodExpression getMethodExpression(
295       final FaceletContext faceletContext, final Class returnType, final Class[] args) {
296     // in a composition may be we get the method expression string from the current variable mapper
297     // the expression can be empty
298     // in this case return nothing
299     if (value.getValue().startsWith("${")) {
300       final ValueExpression expression = getExpression(faceletContext);
301       if (expression != null) {
302         final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
303         return new TagMethodExpression(value, expressionFactory.createMethodExpression(faceletContext,
304             expression.getExpressionString(), returnType, args));
305       } else {
306         return null;
307       }
308     } else {
309       return value.getMethodExpression(faceletContext, returnType, args);
310     }
311   }
312 
313   private Object getValue(
314       final FaceletContext faceletContext, final UIComponent parent, final String expressionString,
315       final String attributeName) {
316     Class type = Object.class;
317     try {
318       type = PropertyUtils.getReadMethod(
319           new PropertyDescriptor(attributeName, parent.getClass())).getReturnType();
320     } catch (final IntrospectionException e) {
321       LOG.warn("Can't determine expected type", e);
322     }
323     final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
324     final ValueExpression valueExpression = expressionFactory
325         .createValueExpression(faceletContext, expressionString, type);
326     return valueExpression.getValue(faceletContext);
327   }
328 
329   private void setConverter(final FaceletContext faceletContext, final UIComponent parent, final String nameValue) {
330     // in a composition may be we get the converter expression string from the current variable mapper
331     // the expression can be empty
332     // in this case return nothing
333     if (value.getValue().startsWith("${")) {
334       final ValueExpression expression = getExpression(faceletContext);
335       if (expression != null) {
336         setConverter(faceletContext, parent, nameValue, expression);
337       }
338     } else {
339       setConverter(faceletContext, parent, nameValue, value.getValueExpression(faceletContext, Object.class));
340     }
341   }
342 
343   private void setConverter(
344       final FaceletContext faceletContext, final UIComponent parent, final String nameValue,
345       final ValueExpression expression) {
346     if (expression.isLiteralText()) {
347       final Converter converter =
348           faceletContext.getFacesContext().getApplication().createConverter(expression.getExpressionString());
349       ((ValueHolder) parent).setConverter(converter);
350     } else {
351       parent.setValueExpression(nameValue, expression);
352     }
353   }
354 }