View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.lifecycle;
17  
18  import java.beans.PropertyEditor;
19  import java.io.Serializable;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.kuali.rice.krad.uif.component.Component;
28  
29  /**
30   * Holds data about the rendered view that might be needed to handle a post request.
31   *
32   * <p>When an action is requested on a view (for example add/delete line, field query, so on), it might be
33   * necessary to read configuration from the view that was rendered to cary out the action. However, the rendered
34   * view is not stored, and the new view is not rendered until after the controller completes. Therefore it is
35   * necessary to provide this mechanism.</p>
36   *
37   * <p>The post metadata is retrieved in the controller though the {@link org.kuali.rice.krad.web.form.UifFormBase}
38   * instance</p>
39   *
40   * @author Kuali Rice Team (rice.collab@kuali.org)
41   */
42  public class ViewPostMetadata implements Serializable {
43      private static final long serialVersionUID = -515221881981451818L;
44  
45      private String id;
46  
47      private Map<String, ComponentPostMetadata> componentPostMetadataMap;
48  
49      private Map<String, PropertyEditor> fieldPropertyEditors;
50      private Map<String, PropertyEditor> secureFieldPropertyEditors;
51  
52      private Set<String> inputFieldIds;
53      private Set<String> allDataFieldPropertyPaths;
54      private Map<String, List<Object>> addedCollectionObjects;
55  
56      private Set<String> accessibleBindingPaths;
57      private Set<String> accessibleMethodToCalls;
58  
59      /**
60       * Default contructor.
61       */
62      public ViewPostMetadata() {
63          fieldPropertyEditors = Collections.synchronizedMap(new HashMap<String, PropertyEditor>());
64          secureFieldPropertyEditors = Collections.synchronizedMap(new HashMap<String, PropertyEditor>());
65          inputFieldIds = Collections.synchronizedSet(new HashSet<String>());
66          allDataFieldPropertyPaths = Collections.synchronizedSet(new HashSet<String>());
67          addedCollectionObjects = Collections.synchronizedMap(new HashMap<String, List<Object>>());
68          accessibleBindingPaths = Collections.synchronizedSet(new HashSet<String>());
69          accessibleMethodToCalls =  Collections.synchronizedSet(new HashSet<String>());
70      }
71  
72      /**
73       * Constructor that takes the view id.
74       *
75       * @param id id for the view
76       */
77      public ViewPostMetadata(String id) {
78          this();
79  
80          this.id = id;
81      }
82  
83      /**
84       * Invoked after the lifecycle is complete to perform an necessary cleaning.
85       */
86      public void cleanAfterLifecycle() {
87          allDataFieldPropertyPaths = Collections.synchronizedSet(new HashSet<String>());
88          addedCollectionObjects = Collections.synchronizedMap(new HashMap<String, List<Object>>());
89      }
90  
91      /**
92       * Id for the view the post metadata is associated with.
93       *
94       * @return view id
95       */
96      public String getId() {
97          return id;
98      }
99  
100     /**
101      * @see ViewPostMetadata#getId()
102      */
103     public void setId(String id) {
104         this.id = id;
105     }
106 
107     /**
108      * Map containing post metadata for a component keyed by the component id.
109      *
110      * @return post metadata map, key is component id and value is post metadata
111      */
112     public Map<String, ComponentPostMetadata> getComponentPostMetadataMap() {
113         return componentPostMetadataMap;
114     }
115 
116     /**
117      * @see ViewPostMetadata#getComponentPostMetadataMap()
118      */
119     public void setComponentPostMetadataMap(Map<String, ComponentPostMetadata> componentPostMetadataMap) {
120         this.componentPostMetadataMap = componentPostMetadataMap;
121     }
122 
123     /**
124      * Gets the component post metadata for the given component id.
125      *
126      * @param componentId id for the component whose post metadata should be retrieved
127      * @return post metadata object
128      */
129     public ComponentPostMetadata getComponentPostMetadata(String componentId) {
130         ComponentPostMetadata componentPostMetadata = null;
131 
132         if (componentPostMetadataMap != null && (componentPostMetadataMap.containsKey(componentId))) {
133             componentPostMetadata = componentPostMetadataMap.get(componentId);
134         }
135 
136         return componentPostMetadata;
137     }
138 
139     /**
140      * Adds post data for the given component (this is a convenience method for add component post metadata).
141      *
142      * @param component component instance the data should be added for
143      * @param key key for the post data, this will be used to retrieve the value
144      * @param value value for the post data
145      */
146     public void addComponentPostData(Component component, String key, Object value) {
147         if (component == null) {
148             throw new IllegalArgumentException("Component must not be null for adding post data");
149         }
150 
151         addComponentPostData(component.getId(), key, value);
152     }
153 
154     /**
155      * Adds post data for the given component id (this is a convenience method for add component post metadata).
156      *
157      * @param componentId id for the component the data should be added for
158      * @param key key for the post data, this will be used to retrieve the value
159      * @param value value for the post data
160      */
161     public void addComponentPostData(String componentId, String key, Object value) {
162         if (value == null) {
163             return;
164         }
165 
166         ComponentPostMetadata componentPostMetadata = initializeComponentPostMetadata(componentId);
167 
168         componentPostMetadata.addData(key, value);
169     }
170 
171     /**
172      * Retrieves post data that has been stored for the given component id and key.
173      *
174      * @param componentId id for the component the data should be retrieved for
175      * @param key key for the post data to retrieve
176      * @return value for the data, or null if the data does not exist
177      */
178     public Object getComponentPostData(String componentId, String key) {
179         ComponentPostMetadata componentPostMetadata = getComponentPostMetadata(componentId);
180 
181         if (componentPostMetadata != null) {
182             return componentPostMetadata.getData(key);
183         }
184 
185         return null;
186     }
187 
188     /**
189      * Initializes a component post metadata instance for the given component.
190      *
191      * @param component component instance to initialize post metadata for
192      * @return post metadata instance
193      */
194     public ComponentPostMetadata initializeComponentPostMetadata(Component component) {
195         if (component == null) {
196             throw new IllegalArgumentException("Component must not be null to initialize post metadata");
197         }
198 
199         return initializeComponentPostMetadata(component.getId());
200     }
201 
202     /**
203      * Initializes a component post metadata instance for the given component id.
204      *
205      * @param componentId id for the component to initialize post metadata for
206      * @return post metadata instance
207      */
208     public ComponentPostMetadata initializeComponentPostMetadata(String componentId) {
209         ComponentPostMetadata componentPostMetadata;
210 
211         if (componentPostMetadataMap == null) {
212             synchronized (this) {
213                 if (componentPostMetadataMap == null) {
214                     componentPostMetadataMap = new HashMap<String, ComponentPostMetadata>();
215                 }
216             }
217         }
218 
219         componentPostMetadata = componentPostMetadataMap.get(componentId);
220 
221         if (componentPostMetadata == null) {
222             synchronized (componentPostMetadataMap) {
223                 componentPostMetadata = new ComponentPostMetadata(componentId);
224                 componentPostMetadataMap.put(componentId, componentPostMetadata);
225             }
226         }
227 
228         return componentPostMetadata;
229     }
230 
231     /**
232      * Maintains configuration of properties that have been configured for the view (if render was
233      * set to true) and there corresponding PropertyEdtior (if configured).
234      *
235      * <p>Information is pulled out of the View during the lifecycle so it can be used when a form post
236      * is done from the View. Note if a field is secure, it will be placed in the
237      * {@link #getSecureFieldPropertyEditors()} map instead</p>
238      *
239      * @return map of property path (full) to PropertyEditor
240      */
241     public Map<String, PropertyEditor> getFieldPropertyEditors() {
242         return fieldPropertyEditors;
243     }
244 
245     /**
246      * Associates a property editor instance with the given property path.
247      *
248      * @param propertyPath path for the property the editor should be associated with
249      * @param propertyEditor editor instance to use when binding data for the property
250      */
251     public void addFieldPropertyEditor(String propertyPath, PropertyEditor propertyEditor) {
252         if (fieldPropertyEditors == null) {
253             fieldPropertyEditors = new HashMap<String, PropertyEditor>();
254         }
255 
256         fieldPropertyEditors.put(propertyPath, propertyEditor);
257     }
258 
259     /**
260      * Maintains configuration of secure properties that have been configured for the view (if
261      * render was set to true) and there corresponding PropertyEdtior (if configured).
262      *
263      * <p>Information is pulled out of the View during the lifecycle so it can be used when a form post
264      * is done from the View. Note if a field is non-secure, it will be placed in the
265      * {@link #getFieldPropertyEditors()} map instead</p>
266      *
267      * @return map of property path (full) to PropertyEditor
268      */
269     public Map<String, PropertyEditor> getSecureFieldPropertyEditors() {
270         return secureFieldPropertyEditors;
271     }
272 
273     /**
274      * Associates a secure property editor instance with the given property path.
275      *
276      * @param propertyPath path for the property the editor should be associated with
277      * @param propertyEditor secure editor instance to use when binding data for the property
278      */
279     public void addSecureFieldPropertyEditor(String propertyPath, PropertyEditor propertyEditor) {
280         if (secureFieldPropertyEditors == null) {
281             secureFieldPropertyEditors = new HashMap<String, PropertyEditor>();
282         }
283 
284         secureFieldPropertyEditors.put(propertyPath, propertyEditor);
285     }
286 
287     /**
288      * Set of ids for all input fields rendered with the view.
289      *
290      * @return set of id strings
291      */
292     public Set<String> getInputFieldIds() {
293         return inputFieldIds;
294     }
295 
296     /**
297      * @see ViewPostMetadata#getInputFieldIds()
298      */
299     public void setInputFieldIds(Set<String> inputFieldIds) {
300         this.inputFieldIds = inputFieldIds;
301     }
302 
303     /**
304      * Set of data field property paths that have been rendered as part of the lifecycle.
305      *
306      * <p>Note this will include all property paths (of data fields) that were rendered as part of the
307      * last full lifecycle and any component refreshes since then. It will not contain all paths of a view
308      * (which would include all pages)</p>
309      *
310      * @return set of property paths as strings
311      */
312     public Set<String> getAllDataFieldPropertyPaths() {
313         return allDataFieldPropertyPaths;
314     }
315 
316     /**
317      * @see ViewPostMetadata#getAllDataFieldPropertyPaths()
318      */
319     public void setAllDataFieldPropertyPaths(Set<String> allDataFieldPropertyPaths) {
320         this.allDataFieldPropertyPaths = Collections.synchronizedSet(new HashSet<String>(allDataFieldPropertyPaths));
321     }
322 
323     /**
324      * Adds a property path to the list of data field property paths.
325      *
326      * @param propertyPath property path to add
327      * @see ViewPostMetadata#getAllDataFieldPropertyPaths()
328      */
329     public void addDataFieldPropertyPath(String propertyPath) {
330         if (this.allDataFieldPropertyPaths == null) {
331             this.allDataFieldPropertyPaths = Collections.synchronizedSet(new HashSet<String>());
332         }
333 
334         this.allDataFieldPropertyPaths.add(propertyPath);
335     }
336 
337     /**
338      * The collection objects that were added during the current controller call, these will be emptied after
339      * the lifecycle process is run.
340      *
341      * <p>Note: If a list is empty this means that a collection had an addLine call occur and a new line must
342      * be initialized for the collection.</p>
343      *
344      * @return the collection objects that were added during the current controller call if added through a process
345      * other than the collection's own addLine call
346      * @see org.kuali.rice.krad.uif.container.CollectionGroupBase
347      */
348     public Map<String, List<Object>> getAddedCollectionObjects() {
349         return addedCollectionObjects;
350     }
351 
352     /**
353      * @see ViewPostMetadata#getAddedCollectionObjects()
354      */
355     public void setAddedCollectionObjects(Map<String, List<Object>> addedCollectionObjects) {
356         this.addedCollectionObjects = addedCollectionObjects;
357     }
358 
359     /**
360      * Set of property paths from the view that will allow binding to (by default).
361      *
362      * <p>Used by the UIF web infrastructure to provide security during the binding process. By default, binding
363      * will only occur for properties within the view configuration (for properties that allow updating).</p>
364      *
365      * @return Set of property paths
366      */
367     public Set<String> getAccessibleBindingPaths() {
368         return accessibleBindingPaths;
369     }
370 
371     /**
372      * @see ViewPostMetadata#getAccessibleBindingPaths()
373      */
374     public void setAccessibleBindingPaths(Set<String> accessibleBindingPaths) {
375         this.accessibleBindingPaths = accessibleBindingPaths;
376     }
377 
378     /**
379      * Adds a path to the set of accessible binding paths.
380      *
381      * @param accessibleBindingPath path to add as accessible
382      * @see ViewPostMetadata#getAccessibleBindingPaths()
383      */
384     public void addAccessibleBindingPath(String accessibleBindingPath) {
385         if (this.accessibleBindingPaths == null) {
386             this.accessibleBindingPaths = Collections.synchronizedSet(new HashSet<String>());
387         }
388 
389         this.accessibleBindingPaths.add(accessibleBindingPath);
390     }
391 
392     /**
393      * Set of method to calls configured within the view that access should be allowed for.
394      *
395      * <p>Used by the UIF web infrastructure to provide security for invoking controller methods. By default,
396      * only methods within the view configuration can be called.</p>
397      *
398      * @return Set of method names
399      */
400     public Set<String> getAccessibleMethodToCalls() {
401         return accessibleMethodToCalls;
402     }
403 
404     /**
405      * @see ViewPostMetadata#getAccessibleMethodToCalls()
406      */
407     public void setAccessibleMethodToCalls(Set<String> accessibleMethodToCalls) {
408         this.accessibleMethodToCalls = accessibleMethodToCalls;
409     }
410 
411     /**
412      * Adds a method to the set of accessible controller methods.
413      *
414      * @param methodToCall method to add as accessible
415      * @see ViewPostMetadata#getAccessibleMethodToCalls()
416      */
417     public void addAccessibleMethodToCall(String methodToCall) {
418         if (this.accessibleMethodToCalls == null) {
419             this.accessibleMethodToCalls = Collections.synchronizedSet(new HashSet<String>());
420         }
421 
422         this.accessibleMethodToCalls.add(methodToCall);
423     }
424 }