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.Visual;
25  import org.apache.myfaces.tobago.component.SupportsRenderedPartially;
26  import org.apache.myfaces.tobago.context.Markup;
27  import org.apache.myfaces.tobago.el.ConstantMethodExpression;
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(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.renderedPartially == nameValue && parent instanceof SupportsRenderedPartially) {
226           if (value.isLiteral()) {
227             final String[] components = ComponentUtils.splitList(value.getValue());
228             ((SupportsRenderedPartially) parent).setRenderedPartially(components);
229           } else {
230             parent.setValueExpression(nameValue.getName(), value.getValueExpression(faceletContext, Object.class));
231           }
232         } else if (Attributes.markup == nameValue) {
233           if (parent instanceof Visual) {
234             if (value.isLiteral()) {
235               ((Visual) parent).setMarkup(Markup.valueOf(value.getValue()));
236             } else {
237               final ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
238               parent.setValueExpression(nameValue.getName(), expression);
239             }
240           } else {
241             LOG.error("Component is not instanceof Visual. Instance is: " + parent.getClass().getName());
242           }
243         } else if (parent instanceof EditableValueHolder && Attributes.validator == nameValue) {
244           final MethodExpression methodExpression
245               = getMethodExpression(faceletContext, null, ComponentUtils.VALIDATOR_ARGS);
246           if (methodExpression != null) {
247             ((EditableValueHolder) parent).addValidator(new MethodExpressionValidator(methodExpression));
248           }
249         } else if (parent instanceof EditableValueHolder && Attributes.valueChangeListener == nameValue) {
250           final MethodExpression methodExpression =
251               getMethodExpression(faceletContext, null, ComponentUtils.VALUE_CHANGE_LISTENER_ARGS);
252           if (methodExpression != null) {
253             ((EditableValueHolder) parent).addValueChangeListener(
254                 new MethodExpressionValueChangeListener(methodExpression));
255           }
256         } else if (parent instanceof ValueHolder && Attributes.converter == nameValue) {
257           setConverter(faceletContext, parent, nameValue.getName());
258         } else if (parent instanceof ActionSource && Attributes.action.equals(nameValue)) {
259           final MethodExpression action = getMethodExpression(faceletContext, String.class, ComponentUtils.ACTION_ARGS);
260           if (action != null) {
261             ((ActionSource2) parent).setActionExpression(action);
262           }
263         } else if (parent instanceof ActionSource && Attributes.actionListener == nameValue) {
264           final MethodExpression action
265               = getMethodExpression(faceletContext, null, ComponentUtils.ACTION_LISTENER_ARGS);
266           if (action != null) {
267             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(action));
268           }
269         } else if (!parent.getAttributes().containsKey(nameValue.getName())) {
270           if (value.isLiteral()) {
271             parent.getAttributes().put(nameValue.getName(), value.getValue());
272           } else {
273             parent.setValueExpression(nameValue.getName(), value.getValueExpression(faceletContext, Object.class));
274           }
275         }
276       }
277     }
278   }
279 
280   private boolean isMethodOrValueExpression(final String string) {
281     return (string.startsWith("${") || string.startsWith("#{")) && string.endsWith("}");
282   }
283 
284   private boolean containsMethodOrValueExpression(final String string) {
285     return (string.contains("${") || string.contains("#{")) && string.contains("}");
286   }
287 
288   private boolean isSimpleExpression(final String string) {
289     return string.indexOf('.') < 0 && string.indexOf('[') < 0;
290   }
291 
292   private String removeElParenthesis(final String string) {
293     return string.substring(2, string.length() - 1);
294   }
295 
296   private ValueExpression getExpression(final FaceletContext faceletContext) {
297     final String myValue = removeElParenthesis(value.getValue());
298     return faceletContext.getVariableMapper().resolveVariable(myValue);
299   }
300 
301   private MethodExpression getMethodExpression(
302       final FaceletContext faceletContext, final Class returnType, final Class[] args) {
303     // in a composition may be we get the method expression string from the current variable mapper
304     // the expression can be empty
305     // in this case return nothing
306     if (value.getValue().startsWith("${")) {
307       final ValueExpression expression = getExpression(faceletContext);
308       if (expression != null) {
309         final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
310         return new TagMethodExpression(value, expressionFactory.createMethodExpression(faceletContext,
311             expression.getExpressionString(), returnType, args));
312       } else {
313         return null;
314       }
315     } else {
316       return value.getMethodExpression(faceletContext, returnType, args);
317     }
318   }
319 
320   private Object getValue(
321       final FaceletContext faceletContext, final UIComponent parent, final String expressionString,
322       final String attributeName) {
323     Class type = Object.class;
324     try {
325       type = PropertyUtils.getReadMethod(
326           new PropertyDescriptor(attributeName, parent.getClass())).getReturnType();
327     } catch (final IntrospectionException e) {
328       LOG.warn("Can't determine expected type", e);
329     }
330     final 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(final FaceletContext faceletContext, final UIComponent parent, final 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       final 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       final FaceletContext faceletContext, final UIComponent parent, final String nameValue,
352       final ValueExpression expression) {
353     if (expression.isLiteralText()) {
354       final Converter converter =
355           faceletContext.getFacesContext().getApplication().createConverter(expression.getExpressionString());
356       ((ValueHolder) parent).setConverter(converter);
357     } else {
358       parent.setValueExpression(nameValue, expression);
359     }
360   }
361 }