Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
MethodExpressionValueChangeListener |
|
| 1.8181818181818181;1.818 |
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 | package javax.faces.event; | |
20 | ||
21 | import javax.el.ELContext; | |
22 | import javax.el.ELException; | |
23 | import javax.el.ExpressionFactory; | |
24 | import javax.el.MethodExpression; | |
25 | import javax.el.MethodNotFoundException; | |
26 | import javax.faces.component.StateHolder; | |
27 | import javax.faces.context.FacesContext; | |
28 | ||
29 | /** | |
30 | * See Javadoc of <a href="https://javaserverfaces.dev.java.net/nonav/docs/2.0/javadocs/javax/faces/event/ | |
31 | * MethodExpressionValueChangeListener.html">JSF Specification</a> | |
32 | */ | |
33 | public class MethodExpressionValueChangeListener implements ValueChangeListener, StateHolder | |
34 | { | |
35 | ||
36 | 2 | private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; |
37 | 2 | private static final Object[] EMPTY_PARAMS = new Object[0]; |
38 | ||
39 | private MethodExpression methodExpressionOneArg; | |
40 | private MethodExpression methodExpressionZeroArg; | |
41 | 14 | private boolean isTransient = false; |
42 | ||
43 | /** Creates a new instance of MethodExpressionValueChangeListener */ | |
44 | public MethodExpressionValueChangeListener() | |
45 | 4 | { |
46 | // constructor for state-saving | |
47 | 4 | } |
48 | ||
49 | public MethodExpressionValueChangeListener(MethodExpression methodExpressionOneArg) | |
50 | 4 | { |
51 | 4 | this.methodExpressionOneArg = methodExpressionOneArg; |
52 | ||
53 | 4 | _createZeroArgsMethodExpression(methodExpressionOneArg); |
54 | 4 | } |
55 | ||
56 | public MethodExpressionValueChangeListener(MethodExpression methodExpressionOneArg, | |
57 | MethodExpression methodExpressionZeroArg) | |
58 | 6 | { |
59 | 6 | this.methodExpressionOneArg = methodExpressionOneArg; |
60 | 6 | if (methodExpressionZeroArg != null) |
61 | { | |
62 | 6 | this.methodExpressionZeroArg = methodExpressionZeroArg; |
63 | } | |
64 | else | |
65 | { | |
66 | 0 | _createZeroArgsMethodExpression(methodExpressionOneArg); |
67 | } | |
68 | 6 | } |
69 | ||
70 | public void processValueChange(ValueChangeEvent event) throws AbortProcessingException | |
71 | { | |
72 | try | |
73 | { | |
74 | try | |
75 | { | |
76 | // call to the one argument MethodExpression | |
77 | 4 | Object[] params = new Object[] { event }; |
78 | 4 | methodExpressionOneArg.invoke(getElContext(), params); |
79 | } | |
80 | 2 | catch (MethodNotFoundException mnfe) |
81 | { | |
82 | // call to the zero argument MethodExpression | |
83 | 2 | methodExpressionZeroArg.invoke(getElContext(), EMPTY_PARAMS); |
84 | 2 | } |
85 | } | |
86 | 0 | catch (ELException e) |
87 | { | |
88 | // "... If that fails for any reason, throw an AbortProcessingException, | |
89 | // including the cause of the failure ..." | |
90 | // -= Leonardo Uribe =- after discussing this topic on MYFACES-3199, the conclusion is the part is an advice | |
91 | // for the developer implementing a listener in a method expressions that could be wrapped by this class. | |
92 | // The spec wording is poor but, to keep this coherently with ExceptionHandler API, | |
93 | // the spec and code on UIViewRoot we need: | |
94 | // 2a) "exception is instance of APE or any of the causes of the exception are an APE, | |
95 | // DON'T publish ExceptionQueuedEvent and terminate processing for current event". | |
96 | // 2b) for any other exception publish ExceptionQueuedEvent and continue broadcast processing. | |
97 | 0 | Throwable cause = e.getCause(); |
98 | 0 | AbortProcessingException ape = null; |
99 | 0 | if (cause != null) |
100 | { | |
101 | do | |
102 | { | |
103 | 0 | if (cause instanceof AbortProcessingException) |
104 | { | |
105 | 0 | ape = (AbortProcessingException) cause; |
106 | 0 | break; |
107 | } | |
108 | 0 | cause = cause.getCause(); |
109 | } | |
110 | 0 | while (cause != null); |
111 | } | |
112 | ||
113 | 0 | if (ape != null) |
114 | { | |
115 | // 2a) "exception is instance of APE or any of the causes of the exception are an APE, | |
116 | // DON'T publish ExceptionQueuedEvent and terminate processing for current event". | |
117 | // To do this throw an AbortProcessingException here, later on UIViewRoot.broadcastAll, | |
118 | // this exception will be received and stored to handle later. | |
119 | 0 | throw ape; |
120 | } | |
121 | //for any other exception publish ExceptionQueuedEvent and continue broadcast processing. | |
122 | 0 | throw e; |
123 | //Throwable cause = e.getCause(); | |
124 | //if (cause == null) | |
125 | //{ | |
126 | // cause = e; | |
127 | //} | |
128 | //if (cause instanceof AbortProcessingException) | |
129 | //{ | |
130 | // throw (AbortProcessingException) cause; | |
131 | //} | |
132 | //else | |
133 | //{ | |
134 | // throw new AbortProcessingException(cause); | |
135 | //} | |
136 | 4 | } |
137 | 4 | } |
138 | ||
139 | public void restoreState(FacesContext context, Object state) | |
140 | { | |
141 | 2 | methodExpressionOneArg = (MethodExpression) ((Object[]) state)[0]; |
142 | 2 | methodExpressionZeroArg = (MethodExpression) ((Object[]) state)[1]; |
143 | 2 | } |
144 | ||
145 | public Object saveState(FacesContext context) | |
146 | { | |
147 | 2 | return new Object[] {methodExpressionOneArg, methodExpressionZeroArg}; |
148 | } | |
149 | ||
150 | public void setTransient(boolean newTransientValue) | |
151 | { | |
152 | 0 | isTransient = newTransientValue; |
153 | 0 | } |
154 | ||
155 | public boolean isTransient() | |
156 | { | |
157 | 0 | return isTransient; |
158 | } | |
159 | ||
160 | private ELContext getElContext() | |
161 | { | |
162 | 10 | return getFacesContext().getELContext(); |
163 | } | |
164 | ||
165 | private FacesContext getFacesContext() | |
166 | { | |
167 | 14 | return FacesContext.getCurrentInstance(); |
168 | } | |
169 | ||
170 | /** | |
171 | * Creates a {@link MethodExpression} with no params and with the same Expression as | |
172 | * param <code>methodExpression</code> | |
173 | * <b>WARNING!</b> This method creates new {@link MethodExpression} with expressionFactory.createMethodExpression. | |
174 | * That means is not decorating MethodExpression passed as parameter - | |
175 | * support for EL VariableMapper will not be available! | |
176 | * This is a problem when using facelets and <ui:decorate/> with EL params (see MYFACES-2541 for details). | |
177 | */ | |
178 | private void _createZeroArgsMethodExpression(MethodExpression methodExpression) | |
179 | { | |
180 | 4 | ExpressionFactory expressionFactory = getFacesContext().getApplication().getExpressionFactory(); |
181 | ||
182 | 4 | this.methodExpressionZeroArg = expressionFactory.createMethodExpression(getElContext(), |
183 | methodExpression.getExpressionString(), Void.class, EMPTY_CLASS_ARRAY); | |
184 | 4 | } |
185 | ||
186 | } |