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(final 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(final FaceletContext faceletContext, final 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                  final 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                 final 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               final 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             final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
149             final 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               final 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             final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
177             final MethodExpression actionListener
178                 = 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             final 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             final 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               final 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         final 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             final 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               final 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           final MethodExpression methodExpression
249               = getMethodExpression(faceletContext, null, ComponentUtils.VALIDATOR_ARGS);
250           if (methodExpression != null) {
251             ((EditableValueHolder) parent).addValidator(new MethodExpressionValidator(methodExpression));
252           }
253         } else if (parent instanceof EditableValueHolder
254             && Attributes.VALUE_CHANGE_LISTENER.equals(nameValue)) {
255           final MethodExpression methodExpression =
256               getMethodExpression(faceletContext, null, ComponentUtils.VALUE_CHANGE_LISTENER_ARGS);
257           if (methodExpression != null) {
258             ((EditableValueHolder) parent).addValueChangeListener(
259                 new MethodExpressionValueChangeListener(methodExpression));
260           }
261         } else if (parent instanceof ValueHolder && Attributes.CONVERTER.equals(nameValue)) {
262           setConverter(faceletContext, parent, nameValue);
263         } else if (parent instanceof ActionSource && Attributes.ACTION.equals(nameValue)) {
264           final MethodExpression action = getMethodExpression(faceletContext, String.class, ComponentUtils.ACTION_ARGS);
265           if (action != null) {
266             ((ActionSource2) parent).setActionExpression(action);
267           }
268         } else if (parent instanceof ActionSource && Attributes.ACTION_LISTENER.equals(nameValue)) {
269           final MethodExpression action
270               = getMethodExpression(faceletContext, null, ComponentUtils.ACTION_LISTENER_ARGS);
271           if (action != null) {
272             ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(action));
273           }
274         } else if (!parent.getAttributes().containsKey(nameValue)) {
275           if (value.isLiteral()) {
276             parent.getAttributes().put(nameValue, value.getValue());
277           } else {
278             parent.setValueExpression(nameValue, value.getValueExpression(faceletContext, Object.class));
279           }
280         }
281       }
282     }
283   }
284 
285   private boolean isMethodOrValueExpression(final String string) {
286     return (string.startsWith("${") || string.startsWith("#{")) && string.endsWith("}");
287   }
288 
289   private boolean containsMethodOrValueExpression(final String string) {
290     return (string.contains("${") || string.contains("#{")) && string.contains("}");
291   }
292 
293   private boolean isSimpleExpression(final String string) {
294     return string.indexOf('.') < 0 && string.indexOf('[') < 0;
295   }
296 
297   private String removeElParenthesis(final String string) {
298     return string.substring(2, string.length() - 1);
299   }
300 
301   private ValueExpression getExpression(final FaceletContext faceletContext) {
302     final String myValue = removeElParenthesis(value.getValue());
303     return faceletContext.getVariableMapper().resolveVariable(myValue);
304   }
305 
306   private MethodExpression getMethodExpression(
307       final FaceletContext faceletContext, final Class returnType, final Class[] args) {
308     // in a composition may be we get the method expression string from the current variable mapper
309     // the expression can be empty
310     // in this case return nothing
311     if (value.getValue().startsWith("${")) {
312       final ValueExpression expression = getExpression(faceletContext);
313       if (expression != null) {
314         final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
315         return new TagMethodExpression(value, expressionFactory.createMethodExpression(faceletContext,
316             expression.getExpressionString(), returnType, args));
317       } else {
318         return null;
319       }
320     } else {
321       return value.getMethodExpression(faceletContext, returnType, args);
322     }
323   }
324 
325   private Object getValue(
326       final FaceletContext faceletContext, final UIComponent parent, final String expressionString,
327       final String attributeName) {
328     Class type = Object.class;
329     try {
330       type = PropertyUtils.getReadMethod(
331           new PropertyDescriptor(attributeName, parent.getClass())).getReturnType();
332     } catch (final IntrospectionException e) {
333       LOG.warn("Can't determine expected type", e);
334     }
335     final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
336     final ValueExpression valueExpression = expressionFactory
337         .createValueExpression(faceletContext, expressionString, type);
338     return valueExpression.getValue(faceletContext);
339   }
340 
341   private void setConverter(final FaceletContext faceletContext, final UIComponent parent, final String nameValue) {
342     // in a composition may be we get the converter expression string from the current variable mapper
343     // the expression can be empty
344     // in this case return nothing
345     if (value.getValue().startsWith("${")) {
346       final ValueExpression expression = getExpression(faceletContext);
347       if (expression != null) {
348         setConverter(faceletContext, parent, nameValue, expression);
349       }
350     } else {
351       setConverter(faceletContext, parent, nameValue, value.getValueExpression(faceletContext, Object.class));
352     }
353   }
354 
355   private void setConverter(
356       final FaceletContext faceletContext, final UIComponent parent, final String nameValue,
357       final ValueExpression expression) {
358     if (expression.isLiteralText()) {
359       final Converter converter =
360           faceletContext.getFacesContext().getApplication().createConverter(expression.getExpressionString());
361       ((ValueHolder) parent).setConverter(converter);
362     } else {
363       parent.setValueExpression(nameValue, expression);
364     }
365   }
366 }