001/**
002 * Copyright 2005-2018 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 */
016package org.kuali.rice.krad.uif.lifecycle;
017
018import static org.kuali.rice.krad.uif.UifConstants.ViewPhases.APPLY_MODEL;
019import static org.kuali.rice.krad.uif.UifConstants.ViewPhases.FINALIZE;
020import static org.kuali.rice.krad.uif.UifConstants.ViewPhases.INITIALIZE;
021
022import org.kuali.rice.krad.uif.UifConstants;
023import org.kuali.rice.krad.uif.component.Component;
024import org.kuali.rice.krad.uif.freemarker.RenderComponentTask;
025import org.kuali.rice.krad.uif.lifecycle.finalize.AddFocusAndJumpDataAttributesTask;
026import org.kuali.rice.krad.uif.lifecycle.finalize.AddViewTemplatesTask;
027import org.kuali.rice.krad.uif.lifecycle.finalize.ComponentDefaultFinalizeTask;
028import org.kuali.rice.krad.uif.lifecycle.finalize.FinalizeViewTask;
029import org.kuali.rice.krad.uif.lifecycle.finalize.HelperCustomFinalizeTask;
030import org.kuali.rice.krad.uif.lifecycle.finalize.InvokeFinalizerTask;
031import org.kuali.rice.krad.uif.lifecycle.finalize.RegisterPropertyEditorTask;
032import org.kuali.rice.krad.uif.lifecycle.finalize.SetReadOnlyOnDataBindingTask;
033import org.kuali.rice.krad.uif.lifecycle.initialize.AssignIdsTask;
034import org.kuali.rice.krad.uif.lifecycle.initialize.ComponentDefaultInitializeTask;
035import org.kuali.rice.krad.uif.lifecycle.initialize.HelperCustomInitializeTask;
036import org.kuali.rice.krad.uif.lifecycle.initialize.InitializeContainerFromHelperTask;
037import org.kuali.rice.krad.uif.lifecycle.initialize.InitializeDataFieldFromDictionaryTask;
038import org.kuali.rice.krad.uif.lifecycle.initialize.PopulateComponentFromExpressionGraphTask;
039import org.kuali.rice.krad.uif.lifecycle.initialize.PopulatePathTask;
040import org.kuali.rice.krad.uif.lifecycle.initialize.PopulateReplacersAndModifiersFromExpressionGraphTask;
041import org.kuali.rice.krad.uif.lifecycle.initialize.PrepareForCacheTask;
042import org.kuali.rice.krad.uif.lifecycle.initialize.ProcessRemoteFieldsHolderTask;
043import org.kuali.rice.krad.uif.lifecycle.initialize.SortContainerTask;
044import org.kuali.rice.krad.uif.lifecycle.model.AfterEvaluateExpressionTask;
045import org.kuali.rice.krad.uif.lifecycle.model.ApplyAuthAndPresentationLogicTask;
046import org.kuali.rice.krad.uif.lifecycle.model.ComponentDefaultApplyModelTask;
047import org.kuali.rice.krad.uif.lifecycle.model.EvaluateExpressionsTask;
048import org.kuali.rice.krad.uif.lifecycle.model.HelperCustomApplyModelTask;
049import org.kuali.rice.krad.uif.lifecycle.model.PopulateComponentContextTask;
050import org.kuali.rice.krad.uif.lifecycle.model.RefreshStateModifyTask;
051import org.kuali.rice.krad.uif.lifecycle.model.SuffixIdFromContainerTask;
052import org.kuali.rice.krad.uif.lifecycle.model.SyncClientSideStateTask;
053import org.kuali.rice.krad.uif.util.CopyUtils;
054import org.kuali.rice.krad.uif.util.LifecycleElement;
055import org.kuali.rice.krad.uif.util.RecycleUtils;
056import org.kuali.rice.krad.uif.view.View;
057
058import java.util.ArrayList;
059import java.util.List;
060import java.util.Set;
061
062/**
063 * Default phase builder implementation.
064 *
065 * @author Kuali Rice Team (rice.collab@kuali.org)
066 */
067public class ViewLifecyclePhaseBuilderBase implements ViewLifecyclePhaseBuilder {
068
069    /**
070     * {@inheritDoc}
071     */
072    @Override
073    public ViewLifecyclePhase buildPhase(View view, String viewPhase, List<String> refreshPaths) {
074        return buildPhase(viewPhase, view, null, "", refreshPaths);
075    }
076
077    /**
078     * {@inheritDoc}
079     */
080    @Override
081    public ViewLifecyclePhase buildPhase(String viewPhase, LifecycleElement element, Component parent,
082            String parentPath, List<String> refreshPaths) {
083        ViewLifecyclePhase phase = getPhaseInstance(viewPhase);
084        phase.prepare(element, parent, parentPath, refreshPaths);
085
086        return finishBuildPhase(phase);
087    }
088
089    /**
090     * Determines if the previous phases have been run for the given element and if not backs up
091     * until it finds the first phase we need to run.
092     *
093     * @param phase phase being requested
094     * @return phase that should be run
095     */
096    protected ViewLifecyclePhase finishBuildPhase(ViewLifecyclePhase phase) {
097        String previousViewPhase = getPreviousViewPhase(phase);
098
099        while (previousViewPhase != null) {
100            ViewLifecyclePhase prevPhase = getPhaseInstance(previousViewPhase);
101            prevPhase.prepare(phase.getElement(), phase.getParent(), phase.getParentPath(), phase.getRefreshPaths());
102
103            prevPhase.setNextPhase(phase);
104            phase = prevPhase;
105
106            previousViewPhase = getPreviousViewPhase(phase);
107        }
108
109        return phase;
110    }
111
112    /**
113     * Return the previous view phase, for automatic phase spawning.
114     *
115     * @return phase view phase
116     */
117    protected static String getPreviousViewPhase(ViewLifecyclePhase phase) {
118        String viewPhase = phase.getViewPhase();
119        if (FINALIZE.equals(viewPhase) && !phase.getElement().isModelApplied()) {
120            return APPLY_MODEL;
121        }
122
123        if (APPLY_MODEL.equals(viewPhase) && !phase.getElement().isInitialized()) {
124            return INITIALIZE;
125        }
126
127        return null;
128    }
129
130    /**
131     * Build a phase instance for processing the given view phase.
132     *
133     * @param viewPhase name of the view phase to return
134     * @return phase instance
135     */
136    protected ViewLifecyclePhase getPhaseInstance(String viewPhase) {
137        ViewLifecyclePhase phase = RecycleUtils.getRecycledInstance(viewPhase, ViewLifecyclePhase.class);
138
139        if (phase != null) {
140            return phase;
141        }
142
143        if (UifConstants.ViewPhases.PRE_PROCESS.equals(viewPhase)) {
144            phase = buildPreProcessPhase();
145        } else if (UifConstants.ViewPhases.INITIALIZE.equals(viewPhase)) {
146            phase = buildInitializePhase();
147        } else if (UifConstants.ViewPhases.APPLY_MODEL.equals(viewPhase)) {
148            phase = buildApplyModelPhase();
149        } else if (UifConstants.ViewPhases.FINALIZE.equals(viewPhase)) {
150            phase = buildFinalizePhase();
151        } else if (UifConstants.ViewPhases.RENDER.equals(viewPhase)) {
152            phase = buildRenderPhase();
153        }
154
155        if (phase == null) {
156            throw new RuntimeException("Cannnot create phase instance for view phase name: " + viewPhase);
157        }
158
159        return phase;
160    }
161
162    /**
163     * Creates an instance of the {@link PreProcessElementPhase} phase with configured tasks.
164     *
165     * @return view lifecycle phase instance
166     */
167    protected ViewLifecyclePhase buildPreProcessPhase() {
168        PreProcessElementPhase phase = new PreProcessElementPhase();
169
170        List<ViewLifecycleTask<?>> tasks = new ArrayList<ViewLifecycleTask<?>>();
171
172        tasks.add(new AssignIdsTask());
173        tasks.add(new PopulatePathTask());
174        tasks.add(new SortContainerTask());
175        tasks.add(new PrepareForCacheTask());
176
177        phase.setTasks(tasks);
178        phase.setSkipLifecycleTasks(new ArrayList<ViewLifecycleTask<?>>());
179
180        return phase;
181    }
182
183    /**
184     * Creates an instance of the {@link InitializeComponentPhase} phase with configured tasks.
185     *
186     * @return view lifecycle phase instance
187     */
188    protected ViewLifecyclePhase buildInitializePhase() {
189        InitializeComponentPhase phase = new InitializeComponentPhase();
190
191        List<ViewLifecycleTask<?>> tasks = new ArrayList<ViewLifecycleTask<?>>();
192
193        tasks.add(new AssignIdsTask());
194        tasks.add(new PopulatePathTask());
195        tasks.add(new PopulateComponentFromExpressionGraphTask());
196        tasks.add(new ComponentDefaultInitializeTask());
197        tasks.add(new InitializeDataFieldFromDictionaryTask());
198        tasks.add(new PopulateReplacersAndModifiersFromExpressionGraphTask());
199        tasks.add(new InitializeContainerFromHelperTask());
200        tasks.add(new ProcessRemoteFieldsHolderTask());
201        tasks.add(new InitializeDataFieldFromDictionaryTask());
202        tasks.add(new RunComponentModifiersTask());
203        tasks.add(new HelperCustomInitializeTask());
204
205        phase.setTasks(tasks);
206
207        List<ViewLifecycleTask<?>> skipLifecycleTasks = new ArrayList<ViewLifecycleTask<?>>();
208
209        skipLifecycleTasks.add(new AssignIdsTask());
210
211        phase.setSkipLifecycleTasks(skipLifecycleTasks);
212
213        return phase;
214    }
215
216    /**
217     * Creates an instance of the {@link ApplyModelComponentPhase} phase with configured tasks.
218     *
219     * @return view lifecycle phase instance
220     */
221    protected ViewLifecyclePhase buildApplyModelPhase() {
222        ApplyModelComponentPhase phase = new ApplyModelComponentPhase();
223
224        List<ViewLifecycleTask<?>> tasks = new ArrayList<ViewLifecycleTask<?>>();
225
226        tasks.add(new SuffixIdFromContainerTask());
227        tasks.add(new PopulateComponentContextTask());
228        tasks.add(new EvaluateExpressionsTask());
229        tasks.add(new AfterEvaluateExpressionTask());
230        tasks.add(new SyncClientSideStateTask());
231        tasks.add(new ApplyAuthAndPresentationLogicTask());
232        tasks.add(new RefreshStateModifyTask());
233        tasks.add(new ComponentDefaultApplyModelTask());
234        tasks.add(new RunComponentModifiersTask());
235        tasks.add(new HelperCustomApplyModelTask());
236        tasks.add(new SetReadOnlyOnDataBindingTask());
237
238        phase.setTasks(tasks);
239
240        List<ViewLifecycleTask<?>> skipLifecycleTasks = new ArrayList<ViewLifecycleTask<?>>();
241
242        skipLifecycleTasks.add(new SuffixIdFromContainerTask());
243
244        phase.setSkipLifecycleTasks(skipLifecycleTasks);
245
246        return phase;
247    }
248
249    /**
250     * Creates an instance of the {@link FinalizeComponentPhase} phase with configured tasks.
251     *
252     * @return view lifecycle phase instance
253     */
254    protected ViewLifecyclePhase buildFinalizePhase() {
255        FinalizeComponentPhase phase = new FinalizeComponentPhase();
256
257        List<ViewLifecycleTask<?>> tasks = new ArrayList<ViewLifecycleTask<?>>();
258
259        tasks.add(new InvokeFinalizerTask());
260        tasks.add(new ComponentDefaultFinalizeTask());
261        tasks.add(new AddViewTemplatesTask());
262        tasks.add(new FinalizeViewTask());
263        tasks.add(new RunComponentModifiersTask());
264        tasks.add(new HelperCustomFinalizeTask());
265        tasks.add(new RegisterPropertyEditorTask());
266        tasks.add(new AddFocusAndJumpDataAttributesTask());
267
268        phase.setTasks(tasks);
269        phase.setSkipLifecycleTasks(new ArrayList<ViewLifecycleTask<?>>());
270
271        return phase;
272    }
273
274    /**
275     * Creates an instance of the {@link RenderComponentPhase} phase with configured tasks.
276     *
277     * @return view lifecycle phase instance
278     */
279    protected ViewLifecyclePhase buildRenderPhase() {
280        RenderComponentPhase phase = new RenderComponentPhase();
281
282        List<ViewLifecycleTask<?>> tasks = new ArrayList<ViewLifecycleTask<?>>();
283
284        tasks.add(new RenderComponentTask());
285
286        phase.setTasks(tasks);
287        phase.setSkipLifecycleTasks(new ArrayList<ViewLifecycleTask<?>>());
288
289        return phase;
290    }
291}