1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.internal.util.StringUtils;
26 import org.apache.myfaces.tobago.util.ComponentUtils;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import javax.el.ELContext;
31 import javax.el.ELException;
32 import javax.el.ExpressionFactory;
33 import javax.el.MethodExpression;
34 import javax.el.MethodInfo;
35 import javax.el.ValueExpression;
36 import javax.faces.FacesException;
37 import javax.faces.component.ActionSource;
38 import javax.faces.component.ActionSource2;
39 import javax.faces.component.EditableValueHolder;
40 import javax.faces.component.StateHolder;
41 import javax.faces.component.UIComponent;
42 import javax.faces.component.ValueHolder;
43 import javax.faces.context.FacesContext;
44 import javax.faces.convert.Converter;
45 import javax.faces.event.MethodExpressionActionListener;
46 import javax.faces.event.MethodExpressionValueChangeListener;
47 import javax.faces.validator.MethodExpressionValidator;
48 import javax.faces.view.facelets.ComponentHandler;
49 import javax.faces.view.facelets.FaceletContext;
50 import javax.faces.view.facelets.TagAttribute;
51 import javax.faces.view.facelets.TagConfig;
52 import javax.faces.view.facelets.TagException;
53 import javax.faces.view.facelets.TagHandler;
54 import java.beans.IntrospectionException;
55 import java.beans.PropertyDescriptor;
56 import java.lang.invoke.MethodHandles;
57 import java.util.Objects;
58
59 public final class AttributeHandler extends TagHandler {
60
61 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
62
63 private final TagAttribute name;
64
65 private final TagAttribute value;
66
67 private final TagAttribute mode;
68
69 public AttributeHandler(final TagConfig config) {
70 super(config);
71 this.name = getRequiredAttribute(Attributes.name.getName());
72 this.value = getRequiredAttribute(Attributes.value.getName());
73 this.mode = getAttribute(Attributes.mode.getName());
74 }
75
76 @Override
77 public void apply(final FaceletContext faceletContext, final UIComponent parent) throws ELException {
78 if (parent == null) {
79 throw new TagException(tag, "Parent UIComponent was null");
80 }
81
82 if (ComponentHandler.isNew(parent)) {
83
84 if (mode != null) {
85 if ("isNotSet".equals(mode.getValue())) {
86 boolean result = false;
87 String expressionString = value.getValue();
88 if (!value.isLiteral()) {
89 while (isSimpleExpression(expressionString)) {
90 if (isMethodOrValueExpression(expressionString)) {
91 final ValueExpression expression
92 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
93 if (expression == null) {
94 result = true;
95 break;
96 } else {
97 expressionString = expression.getExpressionString();
98 }
99 } else {
100 result = false;
101 break;
102 }
103 }
104 } else {
105 result = StringUtils.isEmpty(expressionString);
106 }
107 parent.getAttributes().put(name.getValue(), result);
108 } else if ("isSet".equals(mode.getValue())) {
109 boolean result = true;
110 String expressionString = value.getValue();
111 if (!value.isLiteral()) {
112 while (isSimpleExpression(expressionString)) {
113 if (isMethodOrValueExpression(expressionString)) {
114 final ValueExpression expression
115 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
116 if (expression == null) {
117 result = false;
118 break;
119 } else {
120 expressionString = expression.getExpressionString();
121 }
122 } else {
123 result = true;
124 break;
125 }
126 }
127 } else {
128 result = StringUtils.isNotEmpty(expressionString);
129 }
130 parent.getAttributes().put(name.getValue(), result);
131 } else if ("action".equals(mode.getValue())) {
132 String expressionString = value.getValue();
133 while (isSimpleExpression(expressionString)) {
134 if (isMethodOrValueExpression(expressionString)) {
135 final ValueExpression expression
136 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
137 if (expression == null) {
138
139 if (LOG.isDebugEnabled()) {
140 LOG.debug("Variable can't be resolved: value='" + expressionString + "'");
141 }
142 expressionString = null;
143 break;
144 } else {
145 expressionString = expression.getExpressionString();
146 }
147 } else {
148 break;
149 }
150 }
151 if (expressionString != null) {
152 final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
153 final MethodExpression action = new TagMethodExpression(value, expressionFactory.createMethodExpression(
154 faceletContext, expressionString, String.class, ComponentUtils.ACTION_ARGS));
155 ((ActionSource2) parent).setActionExpression(action);
156 }
157 } else if ("actionListener".equals(mode.getValue())) {
158 String expressionString = value.getValue();
159 while (isSimpleExpression(expressionString)) {
160 if (isMethodOrValueExpression(expressionString)) {
161 final ValueExpression expression
162 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
163 if (expression == null) {
164 if (LOG.isDebugEnabled()) {
165
166 LOG.debug("Variable can't be resolved: value='" + expressionString + "'");
167 }
168 expressionString = null;
169 break;
170 } else {
171 expressionString = expression.getExpressionString();
172 }
173 } else {
174 LOG.warn("Only expressions are supported mode=actionListener value='" + expressionString + "'");
175 expressionString = null;
176 break;
177 }
178 }
179 if (expressionString != null) {
180 final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
181 final MethodExpression actionListener
182 = new TagMethodExpression(value, expressionFactory.createMethodExpression(
183 faceletContext, expressionString, null, ComponentUtils.ACTION_LISTENER_ARGS));
184 ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(actionListener));
185 }
186 } else if ("actionFromValue".equals(mode.getValue())) {
187 if (!value.isLiteral()) {
188 final String result = value.getValue(faceletContext);
189 parent.getAttributes().put(name.getValue(), new ConstantMethodExpression(result));
190 }
191 } else if ("valueIfSet".equals(mode.getValue())) {
192 String expressionString = value.getValue();
193 String lastExpressionString = null;
194 while (isMethodOrValueExpression(expressionString) && isSimpleExpression(expressionString)) {
195 final ValueExpression expression
196 = faceletContext.getVariableMapper().resolveVariable(removeElParenthesis(expressionString));
197 if (expression != null) {
198 lastExpressionString = expressionString;
199 expressionString = expression.getExpressionString();
200 } else {
201
202 expressionString = lastExpressionString;
203 break;
204 }
205 }
206 if (expressionString != null) {
207 final String attributeName = name.getValue(faceletContext);
208 if (containsMethodOrValueExpression(expressionString)) {
209 final ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
210 parent.setValueExpression(attributeName, expression);
211 } else {
212 final Object literalValue = getValue(faceletContext, parent, expressionString, attributeName);
213 parent.getAttributes().put(attributeName, literalValue);
214 }
215 }
216 } else {
217 throw new FacesException("Type " + mode + " not supported");
218 }
219 } else {
220
221 final Attributes nameValue = Attributes.valueOfFailsafe(name.getValue(faceletContext));
222 if (Attributes.rendered == nameValue) {
223 if (value.isLiteral()) {
224 parent.setRendered(value.getBoolean(faceletContext));
225 } else {
226 parent.setValueExpression(nameValue.getName(), value.getValueExpression(faceletContext, Boolean.class));
227 }
228 } else if (Attributes.markup == nameValue) {
229 if (parent instanceof Visual) {
230 if (value.isLiteral()) {
231 ((Visual) parent).setMarkup(Markup.valueOf(value.getValue()));
232 } else {
233 final ValueExpression expression = value.getValueExpression(faceletContext, Object.class);
234 parent.setValueExpression(nameValue.getName(), expression);
235 }
236 } else {
237 LOG.error("Component is not instanceof Visual. Instance is: " + parent.getClass().getName());
238 }
239 } else if (parent instanceof EditableValueHolder && Attributes.validator == nameValue) {
240 final MethodExpression methodExpression
241 = getMethodExpression(faceletContext, null, ComponentUtils.VALIDATOR_ARGS);
242 if (methodExpression != null) {
243 ((EditableValueHolder) parent).addValidator(new MethodExpressionValidator(methodExpression));
244 }
245 } else if (parent instanceof EditableValueHolder && Attributes.valueChangeListener == nameValue) {
246 final MethodExpression methodExpression =
247 getMethodExpression(faceletContext, null, ComponentUtils.VALUE_CHANGE_LISTENER_ARGS);
248 if (methodExpression != null) {
249 ((EditableValueHolder) parent).addValueChangeListener(
250 new MethodExpressionValueChangeListener(methodExpression));
251 }
252 } else if (parent instanceof ValueHolder && Attributes.converter == nameValue) {
253 setConverter(faceletContext, parent, nameValue.getName());
254 } else if (parent instanceof ActionSource && Attributes.action.equals(nameValue)) {
255 final MethodExpression action = getMethodExpression(faceletContext, String.class, ComponentUtils.ACTION_ARGS);
256 if (action != null) {
257 ((ActionSource2) parent).setActionExpression(action);
258 }
259 } else if (parent instanceof ActionSource && Attributes.actionListener == nameValue) {
260 final MethodExpression action
261 = getMethodExpression(faceletContext, null, ComponentUtils.ACTION_LISTENER_ARGS);
262 if (action != null) {
263 ((ActionSource) parent).addActionListener(new MethodExpressionActionListener(action));
264 }
265 } else if (nameValue != null && !parent.getAttributes().containsKey(nameValue.getName())) {
266 if (value.isLiteral()) {
267 parent.getAttributes().put(nameValue.getName(), value.getValue());
268 } else {
269 parent.setValueExpression(nameValue.getName(), value.getValueExpression(faceletContext, Object.class));
270 }
271 } else if (nameValue == null) {
272 LOG.warn("Null value for {}", name);
273 }
274 }
275 }
276 }
277
278 private boolean isMethodOrValueExpression(final String string) {
279 return (string.startsWith("${") || string.startsWith("#{")) && string.endsWith("}");
280 }
281
282 private boolean containsMethodOrValueExpression(final String string) {
283 return (string.contains("${") || string.contains("#{")) && string.contains("}");
284 }
285
286 private boolean isSimpleExpression(final String string) {
287 return string.indexOf('.') < 0 && string.indexOf('[') < 0;
288 }
289
290 private String removeElParenthesis(final String string) {
291 return string.substring(2, string.length() - 1);
292 }
293
294 private ValueExpression getExpression(final FaceletContext faceletContext) {
295 final String myValue = removeElParenthesis(value.getValue());
296 return faceletContext.getVariableMapper().resolveVariable(myValue);
297 }
298
299 private MethodExpression getMethodExpression(
300 final FaceletContext faceletContext, final Class returnType, final Class[] args) {
301
302
303
304 if (value.getValue().startsWith("${")) {
305 final ValueExpression expression = getExpression(faceletContext);
306 if (expression != null) {
307 final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
308 return new TagMethodExpression(value, expressionFactory.createMethodExpression(faceletContext,
309 expression.getExpressionString(), returnType, args));
310 } else {
311 return null;
312 }
313 } else {
314 return value.getMethodExpression(faceletContext, returnType, args);
315 }
316 }
317
318 private Object getValue(
319 final FaceletContext faceletContext, final UIComponent parent, final String expressionString,
320 final String attributeName) {
321 Class type = Object.class;
322 try {
323 type = new PropertyDescriptor(attributeName, parent.getClass()).getReadMethod().getReturnType();
324 } catch (final IntrospectionException e) {
325 LOG.warn("Can't determine expected type", e);
326 }
327 final ExpressionFactory expressionFactory = faceletContext.getExpressionFactory();
328 final ValueExpression valueExpression = expressionFactory
329 .createValueExpression(faceletContext, expressionString, type);
330 return valueExpression.getValue(faceletContext);
331 }
332
333 private void setConverter(final FaceletContext faceletContext, final UIComponent parent, final String nameValue) {
334
335
336
337 if (value.getValue().startsWith("${")) {
338 final ValueExpression expression = getExpression(faceletContext);
339 if (expression != null) {
340 setConverter(faceletContext, parent, nameValue, expression);
341 }
342 } else {
343 setConverter(faceletContext, parent, nameValue, value.getValueExpression(faceletContext, Object.class));
344 }
345 }
346
347 private void setConverter(
348 final FaceletContext faceletContext, final UIComponent parent, final String nameValue,
349 final ValueExpression expression) {
350 if (expression.isLiteralText()) {
351 final Converter converter =
352 faceletContext.getFacesContext().getApplication().createConverter(expression.getExpressionString());
353 ((ValueHolder) parent).setConverter(converter);
354 } else {
355 parent.setValueExpression(nameValue, expression);
356 }
357 }
358
359 private static class ConstantMethodExpression extends MethodExpression implements StateHolder {
360
361 private String outcome;
362
363 private boolean transientFlag;
364
365 ConstantMethodExpression() {
366 }
367
368 ConstantMethodExpression(final String outcome) {
369 this.outcome = outcome;
370 }
371
372 @Override
373 public MethodInfo getMethodInfo(final ELContext context)
374 throws NullPointerException, ELException {
375 return null;
376 }
377
378 @Override
379 public Object invoke(final ELContext context, final Object[] params)
380 throws NullPointerException, ELException {
381 return outcome;
382 }
383
384 @Override
385 public boolean equals(final Object o) {
386 if (this == o) {
387 return true;
388 }
389 if (o == null || getClass() != o.getClass()) {
390 return false;
391 }
392
393 final ConstantMethodExpression that = (ConstantMethodExpression) o;
394
395 return Objects.equals(outcome, that.outcome);
396 }
397
398 @Override
399 public int hashCode() {
400 return outcome.hashCode();
401 }
402
403 @Override
404 public String getExpressionString() {
405 return outcome;
406 }
407
408 @Override
409 public boolean isLiteralText() {
410 return true;
411 }
412
413 @Override
414 public Object saveState(final FacesContext context) {
415 return outcome;
416 }
417
418 @Override
419 public void restoreState(final FacesContext context, final Object state) {
420 this.outcome = (String) state;
421 }
422
423 @Override
424 public void setTransient(final boolean transientFlagParameter) {
425 this.transientFlag = transientFlag;
426 }
427
428 @Override
429 public boolean isTransient() {
430 return transientFlag;
431 }
432 }
433 }