001 /**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.uif.lifecycle;
017
018 import java.util.Collections;
019 import java.util.HashMap;
020 import java.util.List;
021 import java.util.Map;
022 import java.util.Queue;
023 import java.util.Set;
024
025 import org.apache.commons.lang.StringUtils;
026 import org.kuali.rice.krad.uif.UifConstants;
027 import org.kuali.rice.krad.uif.component.Component;
028 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle.LifecycleEvent;
029 import org.kuali.rice.krad.uif.lifecycle.finalize.SetReadOnlyOnDataBindingTask;
030 import org.kuali.rice.krad.uif.lifecycle.model.ApplyAuthAndPresentationLogicTask;
031 import org.kuali.rice.krad.uif.lifecycle.model.ComponentDefaultApplyModelTask;
032 import org.kuali.rice.krad.uif.lifecycle.model.EvaluateExpressionsTask;
033 import org.kuali.rice.krad.uif.lifecycle.model.HelperCustomApplyModelTask;
034 import org.kuali.rice.krad.uif.lifecycle.model.PopulateComponentContextTask;
035 import org.kuali.rice.krad.uif.lifecycle.model.RefreshStateModifyTask;
036 import org.kuali.rice.krad.uif.lifecycle.model.SyncClientSideStateTask;
037 import org.kuali.rice.krad.uif.util.LifecycleElement;
038 import org.kuali.rice.krad.uif.view.View;
039 import org.kuali.rice.krad.uif.view.ViewTheme;
040
041 /**
042 * Lifecycle phase processing task for applying the model to a component.
043 *
044 * <p>
045 * During the apply model phase each component of the tree if invoked to setup any state based on
046 * the given model data
047 * </p>
048 *
049 * <p>
050 * Part of the view lifecycle that applies the model data to the view. Should be called after the
051 * model has been populated before the view is rendered. The main things that occur during this
052 * phase are:
053 * <ul>
054 * <li>Generation of dynamic fields (such as collection rows)</li>
055 * <li>Execution of conditional logic (hidden, read-only, required settings based on model values)</li>
056 * </ul>
057 * </p>
058 *
059 * <p>
060 * The update phase can be called multiple times for the view's lifecycle (typically only once per
061 * request)
062 * </p>
063 *
064 * @author Kuali Rice Team (rice.collab@kuali.org)
065 */
066 public class ApplyModelComponentPhase extends ViewLifecyclePhaseBase {
067
068 /**
069 * Set of IDs that have been visited during the view's apply model phase.
070 *
071 * <p>
072 * This reference is typically shared by all component apply model phases.
073 * </p>
074 */
075 private Set<String> visitedIds;
076
077 /**
078 * Mapping of context variables inherited from the view.
079 */
080 private Map<String, Object> commonContext;
081
082 /**
083 * {@inheritDoc}
084 */
085 @Override
086 protected void recycle() {
087 super.recycle();
088 visitedIds = null;
089 commonContext = null;
090 }
091
092 /**
093 * Create a new lifecycle phase processing task for applying the model to a element.
094 *
095 * @param element The element the model should be applied to
096 * @param model Top level object containing the data
097 * @param path The path to the element relative to the parent element
098 * @param refreshPaths list of paths to run lifecycle on when executing a refresh lifecycle
099 * @param parent The parent element
100 * @param nextPhase The phase to queue directly upon completion of this phase, if applicable
101 * @param visitedIds Tracks components ids that have been seen for adjusting duplicates
102 */
103 protected void prepare(LifecycleElement element, Object model, String path, List<String> refreshPaths,
104 Component parent, ViewLifecyclePhaseBase nextPhase, Set<String> visitedIds) {
105 super.prepare(element, model, path, refreshPaths, parent, nextPhase);
106
107 this.visitedIds = visitedIds;
108
109 Map<String, Object> commonContext = new HashMap<String, Object>();
110
111 View view = ViewLifecycle.getView();
112 Map<String, Object> viewContext = view.getContext();
113 if (viewContext != null) {
114 commonContext.putAll(viewContext);
115 }
116
117 ViewTheme theme = view.getTheme();
118 if (theme != null) {
119 commonContext.put(UifConstants.ContextVariableNames.THEME_IMAGES, theme.getImageDirectory());
120 }
121
122 commonContext.put(UifConstants.ContextVariableNames.COMPONENT, element instanceof Component ? element : parent);
123
124 this.commonContext = Collections.unmodifiableMap(commonContext);
125 }
126
127 /**
128 * {@inheritDoc}
129 */
130 @Override
131 public String getViewPhase() {
132 return UifConstants.ViewPhases.APPLY_MODEL;
133 }
134
135 /**
136 * {@inheritDoc}
137 */
138 @Override
139 public String getStartViewStatus() {
140 return UifConstants.ViewStatus.INITIALIZED;
141 }
142
143 /**
144 * {@inheritDoc}
145 */
146 @Override
147 public String getEndViewStatus() {
148 return UifConstants.ViewStatus.MODEL_APPLIED;
149 }
150
151 /**
152 * {@inheritDoc}
153 */
154 @Override
155 public LifecycleEvent getEventToNotify() {
156 return null;
157 }
158
159 /**
160 * Gets global objects for the context map and pushes them to the context for the component
161 *
162 * @return The common context elements to use while applying model elements to the view.
163 * @see #prepare(LifecycleElement, Object, String, Component, ViewLifecyclePhaseBase, Set)
164 */
165 public Map<String, Object> getCommonContext() {
166 return commonContext;
167 }
168
169 /**
170 * Visit a lifecycle element.
171 *
172 * @param element The lifecycle element (component or layout manager) to mark as visisted.
173 * @return True if the element has been visited before, false if this was the first visit.
174 */
175 public boolean visit(LifecycleElement element) {
176 if (visitedIds.contains(element.getId())) {
177 return true;
178 }
179
180 synchronized (visitedIds) {
181 return !visitedIds.add(element.getId());
182 }
183 }
184
185 /**
186 * Applies the model data to a component of the View instance
187 *
188 * <p>
189 * TODO: Revise - The component is invoked to to apply the model data. Here the component can
190 * generate any additional fields needed or alter the configured fields. After the component is
191 * invoked a hook for custom helper service processing is invoked. Finally the method is
192 * recursively called for all the component children
193 * </p>
194 *
195 * {@inheritDoc}
196 */
197 @Override
198 protected void initializePendingTasks(Queue<ViewLifecycleTask<?>> tasks) {
199 tasks.add(LifecycleTaskFactory.getTask(PopulateComponentContextTask.class, this));
200 tasks.add(LifecycleTaskFactory.getTask(EvaluateExpressionsTask.class, this));
201 tasks.add(LifecycleTaskFactory.getTask(SyncClientSideStateTask.class, this));
202 tasks.add(LifecycleTaskFactory.getTask(ApplyAuthAndPresentationLogicTask.class, this));
203
204 if (ViewLifecycle.isRefreshComponent(getViewPhase(), getViewPath())) {
205 tasks.add(LifecycleTaskFactory.getTask(RefreshStateModifyTask.class, this));
206 }
207
208 tasks.add(LifecycleTaskFactory.getTask(ComponentDefaultApplyModelTask.class, this));
209 getElement().initializePendingTasks(this, tasks);
210 tasks.offer(LifecycleTaskFactory.getTask(RunComponentModifiersTask.class, this));
211 tasks.add(LifecycleTaskFactory.getTask(HelperCustomApplyModelTask.class, this));
212 tasks.add(LifecycleTaskFactory.getTask(SetReadOnlyOnDataBindingTask.class, this));
213 }
214
215 /**
216 * Define all nested lifecycle components, and component prototypes, as successors.
217 *
218 * {@inheritDoc}
219 */
220 @Override
221 protected ViewLifecyclePhase initializeSuccessor(LifecycleElement nestedElement, String nestedPath,
222 Component parent) {
223 ApplyModelComponentPhase applyModelPhase = LifecyclePhaseFactory.applyModel(nestedElement, getModel(),
224 nestedPath, getRefreshPaths(), parent, null, visitedIds);
225
226 if (nestedElement.isInitialized()) {
227 return applyModelPhase;
228 }
229
230 return LifecyclePhaseFactory.initialize(nestedElement, getModel(), nestedPath, getRefreshPaths(), parent,
231 applyModelPhase);
232 }
233
234 }