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 }