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.internal.behavior;
21  
22  import javax.el.ValueExpression;
23  import javax.faces.component.StateHelper;
24  import javax.faces.component.behavior.ClientBehaviorBase;
25  import javax.faces.component.behavior.ClientBehaviorHint;
26  import javax.faces.component.behavior.FacesBehavior;
27  import javax.faces.context.FacesContext;
28  import javax.faces.event.AjaxBehaviorListener;
29  import java.util.Arrays;
30  import java.util.Collection;
31  import java.util.Collections;
32  import java.util.EnumSet;
33  import java.util.Map;
34  import java.util.Set;
35  
36  // todo: clean up (is a copy of MyFaces, but not all stuff is refactored)
37  
38  /**
39   * @since 3.0.0
40   */
41  @FacesBehavior(value = EventBehavior.BEHAVIOR_ID)
42  public class EventBehavior extends ClientBehaviorBase {
43  
44    /**
45     * not needed anymore but enforced by the spec
46     * theoretically a
47     *
48     * FacesBehavior(value = "javax.faces.behavior.Ajax")
49     * could do it
50     */
51    public static final String BEHAVIOR_ID = "org.apache.myfaces.tobago.behavior.Event";
52  
53    private static final String ATTR_EXECUTE = "execute";
54    private static final String ATTR_ON_ERROR = "onerror";
55    private static final String ATTR_ON_EVENT = "onevent";
56    private static final String ATTR_RENDER = "render";
57    private static final String ATTR_DISABLED = "disabled";
58    private static final String ATTR_IMMEDIATE = "immediate";
59    private static final String ATTR_FOR = "for";
60  
61    /**
62     * special render and execute targets
63     */
64    private static final String VAL_FORM = "@form";
65    private static final String VAL_ALL = "@all";
66    private static final String VAL_THIS = "@this";
67    private static final String VAL_NONE = "@none";
68  
69    private static final Collection<String> VAL_FORM_LIST = Collections.singletonList(VAL_FORM);
70    private static final Collection<String> VAL_ALL_LIST = Collections.singletonList(VAL_ALL);
71    private static final Collection<String> VAL_THIS_LIST = Collections.singletonList(VAL_THIS);
72    private static final Collection<String> VAL_NONE_LIST = Collections.singletonList(VAL_NONE);
73  
74    //To enable delta state saving we need this one
75    private DeltaStateHelper<EventBehavior> stateHelper = null;
76  
77    //private Map<String, ValueExpression> _valueExpressions
78    //        = new HashMap<String, ValueExpression>();
79  
80    public EventBehavior() {
81      super();
82    }
83  
84    public void addAjaxBehaviorListener(final AjaxBehaviorListener listener) {
85      super.addBehaviorListener(listener);
86    }
87  
88    public void removeAjaxBehaviorListener(final AjaxBehaviorListener listener) {
89      removeBehaviorListener(listener);
90    }
91  
92    public Collection<String> getExecute() {
93      // we have to evaluate the real value in this method,
94      // because the value of the ValueExpression might
95      // change (almost sure it does!)
96      return evalForCollection(ATTR_EXECUTE);
97    }
98  
99    public void setExecute(final Collection<String> execute) {
100     getStateHelper().put(ATTR_EXECUTE, execute);
101   }
102 
103   public String getOnerror() {
104     return (String) getStateHelper().eval(ATTR_ON_ERROR);
105   }
106 
107   public void setOnerror(final String onError) {
108     getStateHelper().put(ATTR_ON_ERROR, onError);
109   }
110 
111   public String getOnevent() {
112     return (String) getStateHelper().eval(ATTR_ON_EVENT);
113   }
114 
115   public void setOnevent(final String onEvent) {
116     getStateHelper().put(ATTR_ON_EVENT, onEvent);
117   }
118 
119   public Collection<String> getRender() {
120     // we have to evaluate the real value in this method,
121     // because the value of the ValueExpression might
122     // change (almost sure it does!)
123     return evalForCollection(ATTR_RENDER);
124   }
125 
126   public void setRender(final Collection<String> render) {
127     getStateHelper().put(ATTR_RENDER, render);
128   }
129 
130   @SuppressWarnings("unchecked")
131   public ValueExpression getValueExpression(final String name) {
132     //return getValueExpressionMap().get(name);
133     if (name == null) {
134       throw new NullPointerException("name can not be null");
135     }
136 
137     final Map<String, Object> bindings = (Map<String, Object>) getStateHelper().
138         get(EventBehavior.PropertyKeys.bindings);
139     if (bindings != null) {
140       return (ValueExpression) bindings.get(name);
141     } else {
142       return null;
143     }
144   }
145 
146   public void setValueExpression(final String name, final ValueExpression expression) {
147         /*
148         if (item == null)
149         {
150             getValueExpressionMap().remove(name);
151             getStateHelper().remove(name);
152         }
153         else
154         {
155             getValueExpressionMap().put(name, item);
156         }
157         */
158     if (name == null) {
159       throw new NullPointerException("name");
160     }
161 
162     if (expression == null) {
163       getStateHelper().remove(EventBehavior.PropertyKeys.bindings, name);
164     } else {
165       getStateHelper().put(EventBehavior.PropertyKeys.bindings, name, expression);
166     }
167   }
168 
169   public boolean isDisabled() {
170     Boolean retVal = (Boolean) getStateHelper().eval(ATTR_DISABLED);
171     retVal = (retVal == null) ? false : retVal;
172     return retVal;
173   }
174 
175   public void setDisabled(final boolean disabled) {
176     getStateHelper().put(ATTR_DISABLED, disabled);
177   }
178 
179   public boolean isImmediate() {
180     Boolean retVal = (Boolean) getStateHelper().eval(ATTR_IMMEDIATE);
181     retVal = (retVal == null) ? false : retVal;
182     return retVal;
183   }
184 
185   public void setImmediate(final boolean immediate) {
186     getStateHelper().put(ATTR_IMMEDIATE, immediate);
187   }
188 
189   public boolean isImmediateSet() {
190     return (getStateHelper().get(ATTR_IMMEDIATE) != null) || (getValueExpression(ATTR_IMMEDIATE) != null);
191   }
192 
193   public String getFor() {
194     return (String) getStateHelper().eval(ATTR_FOR);
195   }
196 
197   public void setFor(final String id) {
198     getStateHelper().put(ATTR_FOR, id);
199   }
200 
201   @Override
202   public Set<ClientBehaviorHint> getHints() {
203     return EnumSet.of(ClientBehaviorHint.SUBMITTING);
204   }
205 
206   @Override
207   public String getRendererType() {
208     return BEHAVIOR_ID;
209   }
210 
211   @Override
212   public void restoreState(final FacesContext facesContext, final Object o) {
213     if (o == null) {
214       return;
215     }
216     final Object[] values = (Object[]) o;
217     if (values[0] != null) {
218       super.restoreState(facesContext, values[0]);
219     }
220     getStateHelper().restoreState(facesContext, values[1]);
221   }
222 
223   private StateHelper getStateHelper() {
224     return getStateHelper(true);
225   }
226 
227   /**
228    * returns a delta state saving enabled state helper
229    * for the current component
230    *
231    * @param create if true a state helper is created if not already existing
232    * @return an implementation of the StateHelper interface or null if none exists and create is set to false
233    */
234   private StateHelper getStateHelper(final boolean create) {
235     if (stateHelper != null) {
236       return stateHelper;
237     }
238     if (create) {
239       stateHelper = new DeltaStateHelper<>(this);
240     }
241     return stateHelper;
242   }
243 
244   @Override
245   public Object saveState(final FacesContext facesContext) {
246     if (initialStateMarked()) {
247       final Object parentSaved = super.saveState(facesContext);
248       Object stateHelperSaved = null;
249       final StateHelper myStateHelper = getStateHelper(false);
250       if (myStateHelper != null) {
251         stateHelperSaved = myStateHelper.saveState(facesContext);
252       }
253 
254       if (parentSaved == null && stateHelperSaved == null) {
255         //No values
256         return null;
257       }
258       return new Object[]{parentSaved, stateHelperSaved};
259     } else {
260       final Object[] values = new Object[2];
261       values[0] = super.saveState(facesContext);
262       final StateHelper myStateHelper = getStateHelper(false);
263       if (myStateHelper != null) {
264         values[1] = myStateHelper.saveState(facesContext);
265       }
266       return values;
267     }
268   }
269 
270   //private Map<String, ValueExpression> getValueExpressionMap()
271   //{
272   //    return _valueExpressions;
273   //}
274 
275   /**
276    * Invokes eval on the getStateHelper() and tries to get a
277    * Collection out of the result.
278    */
279   @SuppressWarnings("unchecked")
280   private Collection<String> evalForCollection(final String attributeName) {
281     final Object value = getStateHelper().eval(attributeName);
282     if (value == null) {
283       return Collections.emptyList();
284     } else if (value instanceof Collection) {
285       return (Collection<String>) value;
286     } else if (value instanceof String) {
287       return getCollectionFromSpaceSplitString((String) value);
288     } else {
289       throw new IllegalArgumentException("Type " + value.getClass()
290           + " not supported for attribute " + attributeName);
291     }
292   }
293 
294   /**
295    * Splits the String based on spaces and returns the
296    * resulting Strings as Collection.
297    */
298   private Collection<String> getCollectionFromSpaceSplitString(final String stringValue) {
299     //@special handling for @all, @none, @form and @this
300     if (stringValue.equals(VAL_FORM)) {
301       return VAL_FORM_LIST;
302     } else if (stringValue.equals(VAL_ALL)) {
303       return VAL_ALL_LIST;
304     } else if (stringValue.equals(VAL_NONE)) {
305       return VAL_NONE_LIST;
306     } else if (stringValue.equals(VAL_THIS)) {
307       return VAL_THIS_LIST;
308     }
309 
310     // not one of the "normal" values - split it and return the Collection
311     final String[] arrValue = stringValue.split(" ");
312     return Arrays.asList(arrValue);
313   }
314 
315   private enum PropertyKeys {
316     bindings,
317   }
318 }