View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.dynaform;
23  
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.faces.FacesException;
29  import javax.faces.application.ResourceDependency;
30  import javax.faces.component.ContextCallback;
31  import javax.faces.component.UIComponent;
32  import javax.faces.component.UINamingContainer;
33  import javax.faces.component.visit.VisitCallback;
34  import javax.faces.component.visit.VisitContext;
35  import javax.faces.context.FacesContext;
36  import javax.faces.event.PhaseId;
37  
38  import org.primefaces.component.api.Widget;
39  import org.primefaces.extensions.component.base.AbstractDynamicData;
40  import org.primefaces.extensions.model.common.KeyData;
41  import org.primefaces.extensions.model.dynaform.DynaFormControl;
42  import org.primefaces.extensions.model.dynaform.DynaFormModel;
43  
44  /**
45   * <code>DynaForm</code> component.
46   *
47   * @author Oleg Varaksin / last modified by $Author$
48   * @version $Revision$
49   * @since 0.5
50   */
51  @ResourceDependency(library = "primefaces", name = "jquery/jquery.js")
52  @ResourceDependency(library = "primefaces", name = "jquery/jquery-plugins.js")
53  @ResourceDependency(library = "primefaces", name = "core.js")
54  @ResourceDependency(library = "primefaces-extensions", name = "primefaces-extensions.js")
55  @ResourceDependency(library = "primefaces-extensions", name = "dynaform/dynaform.css")
56  @ResourceDependency(library = "primefaces-extensions", name = "dynaform/dynaform.js")
57  public class DynaForm extends AbstractDynamicData implements Widget {
58  
59      public static final String COMPONENT_TYPE = "org.primefaces.extensions.component.DynaForm";
60      public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";
61      private static final String DEFAULT_RENDERER = "org.primefaces.extensions.component.DynaFormRenderer";
62  
63      private Map<String, UIDynaFormControl> cells;
64  
65      /**
66       * Properties that are tracked by state saving.
67       *
68       * @author Oleg Varaksin / last modified by $Author$
69       * @version $Revision$
70       */
71      @SuppressWarnings("java:S115")
72      protected enum PropertyKeys {
73          //@formatter:off
74          widgetVar,
75          autoSubmit,
76          openExtended,
77          buttonBarPosition,
78          style,
79          styleClass,
80          columnClasses
81          //@formatter:on
82      }
83  
84      public DynaForm() {
85          setRendererType(DEFAULT_RENDERER);
86      }
87  
88      @Override
89      public String getFamily() {
90          return COMPONENT_FAMILY;
91      }
92  
93      public String getWidgetVar() {
94          return (String) getStateHelper().eval(PropertyKeys.widgetVar, null);
95      }
96  
97      public void setWidgetVar(final String widgetVar) {
98          getStateHelper().put(PropertyKeys.widgetVar, widgetVar);
99      }
100 
101     public boolean isAutoSubmit() {
102         return (Boolean) getStateHelper().eval(PropertyKeys.autoSubmit, false);
103     }
104 
105     public void setAutoSubmit(final boolean autoSubmit) {
106         getStateHelper().put(PropertyKeys.autoSubmit, autoSubmit);
107     }
108 
109     public boolean isOpenExtended() {
110         return (Boolean) getStateHelper().eval(PropertyKeys.openExtended, false);
111     }
112 
113     public void setOpenExtended(final boolean openExtended) {
114         getStateHelper().put(PropertyKeys.openExtended, openExtended);
115     }
116 
117     public String getButtonBarPosition() {
118         return (String) getStateHelper().eval(PropertyKeys.buttonBarPosition, "bottom");
119     }
120 
121     public void setButtonBarPosition(final String buttonBarPosition) {
122         getStateHelper().put(PropertyKeys.buttonBarPosition, buttonBarPosition);
123     }
124 
125     public void setStyle(final String style) {
126         getStateHelper().put(PropertyKeys.style, style);
127     }
128 
129     public String getStyle() {
130         return (String) getStateHelper().eval(PropertyKeys.style, null);
131     }
132 
133     public void setStyleClass(final String styleClass) {
134         getStateHelper().put(PropertyKeys.styleClass, styleClass);
135     }
136 
137     public String getStyleClass() {
138         return (String) getStateHelper().eval(PropertyKeys.styleClass, null);
139     }
140 
141     public void setColumnClasses(java.lang.String columnClasses) {
142         getStateHelper().put(PropertyKeys.columnClasses, columnClasses);
143     }
144 
145     public java.lang.String getColumnClasses() {
146         return (java.lang.String) getStateHelper().eval(PropertyKeys.columnClasses, null);
147     }
148 
149     public UIDynaFormControl getControlCell(final String type) {
150         final UIDynaFormControl cell = getControlCells().get(type);
151 
152         if (cell == null) {
153             throw new FacesException("UIDynaFormControl to type " + type + " was not found");
154         }
155         else {
156             return cell;
157         }
158     }
159 
160     protected Map<String, UIDynaFormControl> getControlCells() {
161         if (cells == null) {
162             cells = new HashMap<>();
163             for (final UIComponent child : getChildren()) {
164                 if (child instanceof UIDynaFormControl) {
165                     final UIDynaFormControl dynaFormCell = (UIDynaFormControl) child;
166                     cells.put(dynaFormCell.getType(), dynaFormCell);
167                 }
168             }
169         }
170 
171         return cells;
172     }
173 
174     protected static void checkModelInstance(Object value) {
175         if (!(value instanceof DynaFormModel)) {
176             throw new FacesException("Value in DynaForm must be of type DynaFormModel");
177         }
178     }
179 
180     @Override
181     protected KeyData findData(final String key) {
182         final Object value = getValue();
183         if (value == null) {
184             return null;
185         }
186 
187         checkModelInstance(value);
188 
189         final List<DynaFormControl> dynaFormControls = ((DynaFormModel) value).getControls();
190         for (final DynaFormControl dynaFormControl : dynaFormControls) {
191             if (key.equals(dynaFormControl.getKey())) {
192                 return dynaFormControl;
193             }
194         }
195 
196         return null;
197     }
198 
199     @Override
200     protected void processChildren(final FacesContext context, final PhaseId phaseId) {
201         final Object value = getValue();
202         if (value != null) {
203             checkModelInstance(value);
204 
205             final List<DynaFormControl> dynaFormControls = ((DynaFormModel) value).getControls();
206             for (final DynaFormControl dynaFormControl : dynaFormControls) {
207                 processDynaFormCells(context, phaseId, dynaFormControl);
208             }
209         }
210 
211         resetData();
212     }
213 
214     @Override
215     protected boolean visitChildren(final VisitContext context, final VisitCallback callback) {
216         final Object value = getValue();
217         if (value == null) {
218             return false;
219         }
220 
221         checkModelInstance(value);
222 
223         final List<DynaFormControl> dynaFormControls = ((DynaFormModel) value).getControls();
224         for (final DynaFormControl dynaFormControl : dynaFormControls) {
225             if (visitDynaFormCells(context, callback, dynaFormControl)) {
226                 return true;
227             }
228         }
229 
230         resetData();
231 
232         return false;
233     }
234 
235     @Override
236     protected boolean invokeOnChildren(final FacesContext context, final String clientId, final ContextCallback callback) {
237         final Object value = getValue();
238         if (value == null) {
239             return false;
240         }
241 
242         checkModelInstance(value);
243 
244         if (getChildCount() > 0) {
245             // extract the dynaFormControl key from the clientId
246             // it's similar to rowKey in UIData
247             String key = clientId.substring(getClientId().length() + 1);
248             key = key.substring(0, key.indexOf(UINamingContainer.getSeparatorChar(context)));
249 
250             final List<DynaFormControl> dynaFormControls = ((DynaFormModel) value).getControls();
251             for (final DynaFormControl dynaFormControl : dynaFormControls) {
252 
253                 // determine associated DynaFormControl
254                 if (dynaFormControl.getKey().equals(key)) {
255 
256                     // get UI control for DynaFormControl
257                     final UIDynaFormControl uiDynaFormControl = getControlCell(dynaFormControl.getType());
258 
259                     try {
260                         // push the associated data before visiting the child components
261                         setData(dynaFormControl);
262 
263                         // visit childs
264                         if (uiDynaFormControl.invokeOnComponent(context, clientId, callback)) {
265                             return true;
266                         }
267                     }
268                     finally {
269                         resetData();
270                     }
271 
272                     break;
273                 }
274             }
275         }
276 
277         return false;
278     }
279 
280     private void processDynaFormCells(final FacesContext context, final PhaseId phaseId, final DynaFormControl dynaFormControl) {
281         for (int i = 0; i < getChildCount(); i++) {
282             final UIComponent kid = getChildren().get(i);
283             if (!(kid instanceof UIDynaFormControl) || !kid.isRendered()
284                         || !((UIDynaFormControl) kid).getType().equals(dynaFormControl.getType())) {
285                 continue;
286             }
287 
288             setData(dynaFormControl);
289             if (getData() == null) {
290                 return;
291             }
292 
293             for (int j = 0; j < kid.getChildCount(); j++) {
294                 final UIComponent grandkid = kid.getChildren().get(j);
295                 processGrandkid(context, phaseId, grandkid);
296             }
297         }
298     }
299 
300     private static void processGrandkid(final FacesContext context, final PhaseId phaseId, final UIComponent grandkid) {
301         if (!grandkid.isRendered()) {
302             return;
303         }
304 
305         if (phaseId == PhaseId.APPLY_REQUEST_VALUES) {
306             grandkid.processDecodes(context);
307         }
308         else if (phaseId == PhaseId.PROCESS_VALIDATIONS) {
309             grandkid.processValidators(context);
310         }
311         else if (phaseId == PhaseId.UPDATE_MODEL_VALUES) {
312             grandkid.processUpdates(context);
313         }
314         else {
315             throw new IllegalArgumentException();
316         }
317     }
318 
319     private boolean visitDynaFormCells(final VisitContext context, final VisitCallback callback, final DynaFormControl dynaFormControl) {
320         if (getChildCount() > 0) {
321             for (final UIComponent child : getChildren()) {
322                 if (child instanceof UIDynaFormControl
323                             && ((UIDynaFormControl) child).getType().equals(dynaFormControl.getType())) {
324                     setData(dynaFormControl);
325                     if (getData() == null) {
326                         return false;
327                     }
328 
329                     if (child.visitTree(context, callback)) {
330                         return true;
331                     }
332                 }
333             }
334         }
335 
336         return false;
337     }
338 
339     @Override
340     public Object saveState(FacesContext context) {
341         // reset component for MyFaces view pooling
342         cells = null;
343 
344         return super.saveState(context);
345     }
346 }