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.util.Collections;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Queue;
23  import java.util.Set;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.kuali.rice.krad.uif.UifConstants;
27  import org.kuali.rice.krad.uif.component.Component;
28  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle.LifecycleEvent;
29  import org.kuali.rice.krad.uif.lifecycle.finalize.SetReadOnlyOnDataBindingTask;
30  import org.kuali.rice.krad.uif.lifecycle.model.ApplyAuthAndPresentationLogicTask;
31  import org.kuali.rice.krad.uif.lifecycle.model.ComponentDefaultApplyModelTask;
32  import org.kuali.rice.krad.uif.lifecycle.model.EvaluateExpressionsTask;
33  import org.kuali.rice.krad.uif.lifecycle.model.HelperCustomApplyModelTask;
34  import org.kuali.rice.krad.uif.lifecycle.model.PopulateComponentContextTask;
35  import org.kuali.rice.krad.uif.lifecycle.model.RefreshStateModifyTask;
36  import org.kuali.rice.krad.uif.lifecycle.model.SuffixIdFromContainerTask;
37  import org.kuali.rice.krad.uif.lifecycle.model.SyncClientSideStateTask;
38  import org.kuali.rice.krad.uif.util.LifecycleElement;
39  import org.kuali.rice.krad.uif.view.View;
40  import org.kuali.rice.krad.uif.view.ViewTheme;
41  
42  /**
43   * Lifecycle phase processing task for applying the model to a component.
44   *
45   * <p>
46   * During the apply model phase each component of the tree if invoked to setup any state based on
47   * the given model data
48   * </p>
49   *
50   * <p>
51   * Part of the view lifecycle that applies the model data to the view. Should be called after the
52   * model has been populated before the view is rendered. The main things that occur during this
53   * phase are:
54   * <ul>
55   * <li>Generation of dynamic fields (such as collection rows)</li>
56   * <li>Execution of conditional logic (hidden, read-only, required settings based on model values)</li>
57   * </ul>
58   * </p>
59   *
60   * <p>
61   * The update phase can be called multiple times for the view's lifecycle (typically only once per
62   * request)
63   * </p>
64   *
65   * @author Kuali Rice Team (rice.collab@kuali.org)
66   */
67  public class ApplyModelComponentPhase extends ViewLifecyclePhaseBase {
68  
69      /**
70       * Set of IDs that have been visited during the view's apply model phase.
71       *
72       * <p>
73       * This reference is typically shared by all component apply model phases.
74       * </p>
75       */
76      private Set<String> visitedIds;
77  
78      /**
79       * Mapping of context variables inherited from the view.
80       */
81      private Map<String, Object> commonContext;
82  
83      /**
84       * {@inheritDoc}
85       */
86      @Override
87      protected void recycle() {
88          super.recycle();
89          visitedIds = null;
90          commonContext = null;
91      }
92  
93      /**
94       * Create a new lifecycle phase processing task for applying the model to a element.
95       *
96       * @param element The element the model should be applied to
97       * @param model Top level object containing the data
98       * @param path The path to the element relative to the parent element
99       * @param refreshPaths list of paths to run lifecycle on when executing a refresh lifecycle
100      * @param parent The parent element
101      * @param nextPhase The phase to queue directly upon completion of this phase, if applicable
102      * @param visitedIds Tracks components ids that have been seen for adjusting duplicates
103      */
104     protected void prepare(LifecycleElement element, Object model, String path, List<String> refreshPaths,
105             Component parent, ViewLifecyclePhaseBase nextPhase, Set<String> visitedIds) {
106         super.prepare(element, model, path, refreshPaths, parent, nextPhase);
107 
108         this.visitedIds = visitedIds;
109 
110         Map<String, Object> commonContext = new HashMap<String, Object>();
111 
112         View view = ViewLifecycle.getView();
113         Map<String, Object> viewContext = view.getContext();
114         if (viewContext != null) {
115             commonContext.putAll(viewContext);
116         }
117 
118         ViewTheme theme = view.getTheme();
119         if (theme != null) {
120             commonContext.put(UifConstants.ContextVariableNames.THEME_IMAGES, theme.getImageDirectory());
121         }
122 
123         commonContext.put(UifConstants.ContextVariableNames.COMPONENT, element instanceof Component ? element : parent);
124 
125         this.commonContext = Collections.unmodifiableMap(commonContext);
126     }
127 
128     /**
129      * {@inheritDoc}
130      */
131     @Override
132     public String getViewPhase() {
133         return UifConstants.ViewPhases.APPLY_MODEL;
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
140     public String getStartViewStatus() {
141         return UifConstants.ViewStatus.INITIALIZED;
142     }
143 
144     /**
145      * {@inheritDoc}
146      */
147     @Override
148     public String getEndViewStatus() {
149         return UifConstants.ViewStatus.MODEL_APPLIED;
150     }
151 
152     /**
153      * {@inheritDoc}
154      */
155     @Override
156     public LifecycleEvent getEventToNotify() {
157         return null;
158     }
159 
160     /**
161      * Gets global objects for the context map and pushes them to the context for the component
162      *
163      * @return The common context elements to use while applying model elements to the view.
164      * @see #prepare
165      */
166     public Map<String, Object> getCommonContext() {
167         return commonContext;
168     }
169 
170     /**
171      * Visit a lifecycle element.
172      *
173      * @param element The lifecycle element (component or layout manager) to mark as visisted.
174      * @return True if the element has been visited before, false if this was the first visit.
175      */
176     public boolean visit(LifecycleElement element) {
177         if (visitedIds.contains(element.getId())) {
178             return true;
179         }
180 
181         synchronized (visitedIds) {
182             return !visitedIds.add(element.getId());
183         }
184     }
185 
186     /**
187      * {@inheritDoc}
188      */
189     @Override
190     protected void initializeSkipLifecyclePendingTasks(Queue<ViewLifecycleTask<?>> tasks) {
191         super.initializeSkipLifecyclePendingTasks(tasks);
192 
193         if ((getParent() != null) && StringUtils.isNotBlank(getParent().getContainerIdSuffix())) {
194             tasks.add(LifecycleTaskFactory.getTask(SuffixIdFromContainerTask.class, this));
195         }
196     }
197 
198     /**
199      * Applies the model data to a component of the View instance
200      *
201      * <p>
202      * TODO: Revise - The component is invoked to to apply the model data. Here the component can
203      * generate any additional fields needed or alter the configured fields. After the component is
204      * invoked a hook for custom helper service processing is invoked. Finally the method is
205      * recursively called for all the component children
206      * </p>
207      *
208      * {@inheritDoc}
209      */
210     @Override
211     protected void initializePendingTasks(Queue<ViewLifecycleTask<?>> tasks) {
212         if ((getParent() != null) && StringUtils.isNotBlank(getParent().getContainerIdSuffix())) {
213             tasks.add(LifecycleTaskFactory.getTask(SuffixIdFromContainerTask.class, this));
214         }
215 
216         tasks.add(LifecycleTaskFactory.getTask(PopulateComponentContextTask.class, this));
217         tasks.add(LifecycleTaskFactory.getTask(EvaluateExpressionsTask.class, this));
218         tasks.add(LifecycleTaskFactory.getTask(SyncClientSideStateTask.class, this));
219         tasks.add(LifecycleTaskFactory.getTask(ApplyAuthAndPresentationLogicTask.class, this));
220 
221         if (ViewLifecycle.isRefreshComponent(getViewPhase(), getViewPath())) {
222             tasks.add(LifecycleTaskFactory.getTask(RefreshStateModifyTask.class, this));
223         }
224 
225         tasks.add(LifecycleTaskFactory.getTask(ComponentDefaultApplyModelTask.class, this));
226         getElement().initializePendingTasks(this, tasks);
227         tasks.offer(LifecycleTaskFactory.getTask(RunComponentModifiersTask.class, this));
228         tasks.add(LifecycleTaskFactory.getTask(HelperCustomApplyModelTask.class, this));
229         tasks.add(LifecycleTaskFactory.getTask(SetReadOnlyOnDataBindingTask.class, this));
230     }
231 
232     /**
233      * Define all nested lifecycle components, and component prototypes, as successors.
234      * 
235      * {@inheritDoc}
236      */
237     @Override
238     protected ViewLifecyclePhase initializeSuccessor(LifecycleElement nestedElement, String nestedPath,
239             Component parent) {
240         ApplyModelComponentPhase applyModelPhase = LifecyclePhaseFactory.applyModel(nestedElement, getModel(),
241                 nestedPath, getRefreshPaths(), parent, null, visitedIds);
242 
243         if (nestedElement.isInitialized()) {
244             return applyModelPhase;
245         }
246 
247         return LifecyclePhaseFactory.initialize(nestedElement, getModel(), nestedPath, getRefreshPaths(), parent,
248                 applyModelPhase);
249     }
250 
251 }