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.view;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreApiServiceLocator;
20  import org.kuali.rice.krad.datadictionary.DataDictionary;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
22  import org.kuali.rice.krad.datadictionary.state.StateMapping;
23  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
24  import org.kuali.rice.krad.datadictionary.validator.Validator;
25  import org.kuali.rice.krad.lookup.LookupView;
26  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
27  import org.kuali.rice.krad.uif.UifConstants;
28  import org.kuali.rice.krad.uif.UifConstants.ViewStatus;
29  import org.kuali.rice.krad.uif.UifConstants.ViewType;
30  import org.kuali.rice.krad.uif.UifParameters;
31  import org.kuali.rice.krad.uif.UifPropertyPaths;
32  import org.kuali.rice.krad.uif.component.BindingInfo;
33  import org.kuali.rice.krad.uif.component.Component;
34  import org.kuali.rice.krad.uif.component.DelayedCopy;
35  import org.kuali.rice.krad.uif.component.ReferenceCopy;
36  import org.kuali.rice.krad.uif.component.RequestParameter;
37  import org.kuali.rice.krad.uif.container.ContainerBase;
38  import org.kuali.rice.krad.uif.container.Group;
39  import org.kuali.rice.krad.uif.container.PageGroup;
40  import org.kuali.rice.krad.uif.element.BreadcrumbItem;
41  import org.kuali.rice.krad.uif.element.BreadcrumbOptions;
42  import org.kuali.rice.krad.uif.element.HeadLink;
43  import org.kuali.rice.krad.uif.element.Header;
44  import org.kuali.rice.krad.uif.element.MetaTag;
45  import org.kuali.rice.krad.uif.element.ViewHeader;
46  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
47  import org.kuali.rice.krad.uif.lifecycle.ViewLifecyclePhase;
48  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction;
49  import org.kuali.rice.krad.uif.service.ViewHelperService;
50  import org.kuali.rice.krad.uif.util.ClientValidationUtils;
51  import org.kuali.rice.krad.uif.util.ComponentFactory;
52  import org.kuali.rice.krad.uif.util.CopyUtils;
53  import org.kuali.rice.krad.uif.util.LifecycleAwareList;
54  import org.kuali.rice.krad.uif.util.LifecycleAwareMap;
55  import org.kuali.rice.krad.uif.util.LifecycleElement;
56  import org.kuali.rice.krad.uif.util.ParentLocation;
57  import org.kuali.rice.krad.uif.util.ScriptUtils;
58  import org.kuali.rice.krad.uif.widget.BlockUI;
59  import org.kuali.rice.krad.uif.widget.Breadcrumbs;
60  import org.kuali.rice.krad.uif.widget.Growls;
61  import org.kuali.rice.krad.uif.widget.Help;
62  import org.kuali.rice.krad.util.GlobalVariables;
63  import org.kuali.rice.krad.util.KRADConstants;
64  import org.kuali.rice.krad.util.KRADUtils;
65  import org.kuali.rice.krad.web.form.UifFormBase;
66  import org.slf4j.Logger;
67  import org.slf4j.LoggerFactory;
68  
69  import java.util.ArrayList;
70  import java.util.Collections;
71  import java.util.HashMap;
72  import java.util.HashSet;
73  import java.util.List;
74  import java.util.Map;
75  import java.util.Set;
76  
77  /**
78   * Root of the component tree which encompasses a set of related
79   * GroupContainer instances tied together with a common page layout
80   * and navigation.
81   *
82   * <p>
83   * The View component ties together all the components and
84   * configuration of the User Interface for a piece of functionality. In Rice
85   * applications the view is typically associated with a Document
86   * instance.
87   * </p>
88   *
89   * <p>
90   * The view template lays out the common header, footer, and navigation for the
91   * related pages. In addition the view renders the HTML head element bringing in
92   * common script files and style sheets, along with optionally rendering a form
93   * element for pages that need to post data back to the server.
94   * </p>
95   *
96   * <p>
97   * Configuration of UIF features such as model validation is also done through
98   * the View
99   * </p>
100  *
101  * @author Kuali Rice Team (rice.collab@kuali.org)
102  */
103 public class View extends ContainerBase {
104     private static final long serialVersionUID = -1220009725554576953L;
105     private static final Logger LOG = LoggerFactory.getLogger(ContainerBase.class);
106 
107     private String namespaceCode;
108     private String viewName;
109     private ViewTheme theme;
110 
111     private String stateObjectBindingPath;
112     private StateMapping stateMapping;
113 
114     // view header setting
115     private boolean unifiedHeader;
116 
117     // additional view group(s)
118     private Group topGroup;
119 
120     // application
121     private Header applicationHeader;
122     private Group applicationFooter;
123     private Group buildFooter;
124     private String applicationTitleText;
125 
126     // sticky flags
127     private boolean stickyTopGroup;
128     private boolean stickyBreadcrumbs;
129     private boolean stickyHeader;
130     private boolean stickyApplicationHeader;
131     private boolean stickyFooter;
132     private boolean stickyApplicationFooter;
133 
134     private List<String> contentContainerCssClasses;
135 
136     // Breadcrumbs
137     private Breadcrumbs breadcrumbs;
138     private BreadcrumbOptions breadcrumbOptions;
139     private BreadcrumbItem breadcrumbItem;
140     private ParentLocation parentLocation;
141     private List<BreadcrumbItem> pathBasedBreadcrumbs;
142 
143     // Growls support
144     private Growls growls;
145     private boolean growlMessagingEnabled;
146 
147     private BlockUI refreshBlockUI;
148     private BlockUI navigationBlockUI;
149 
150     private String entryPageId;
151 
152     @RequestParameter
153     private String currentPageId;
154 
155     private Group navigation;
156 
157     private Class<?> formClass;
158     private String defaultBindingObjectPath;
159     private Map<String, Class<?>> objectPathToConcreteClassMapping;
160 
161     private List<String> additionalScriptFiles;
162     private List<String> additionalCssFiles;
163     private List<HeadLink> additionalHeadLinks;
164     private List<MetaTag> additionalMetaTags;
165     private boolean useLibraryCssClasses;
166 
167     private ViewType viewTypeName;
168 
169     protected ViewIndex viewIndex;
170     private Map<String, String> viewRequestParameters;
171 
172     private boolean persistFormToSession;
173     private ViewSessionPolicy sessionPolicy;
174 
175     private ViewPresentationController presentationController;
176     private ViewAuthorizer authorizer;
177 
178     private Map<String, Boolean> actionFlags;
179     private Map<String, Boolean> editModes;
180 
181     private Map<String, String> expressionVariables;
182 
183     private boolean singlePageView;
184     private boolean mergeWithPageItems;
185     private PageGroup page;
186 
187     @ReferenceCopy(referenceTransient=true)
188     private PageGroup currentPage;
189 
190     private List<Group> dialogs;
191 
192     protected boolean applyDirtyCheck;
193     private boolean translateCodesOnReadOnlyDisplay;
194     private boolean supportsRequestOverrideOfReadOnlyFields;
195     private boolean disableNativeAutocomplete;
196     private boolean disableBrowserCache;
197 
198     private String preLoadScript;
199 
200     @DelayedCopy
201     private List<? extends Component> items;
202 
203     private List<String> viewTemplates;
204 
205     private Class<? extends ViewHelperService> viewHelperServiceClass;
206 
207     @ReferenceCopy
208     private ViewHelperService viewHelperService;
209 
210     private Map<String, Object> preModelContext;
211 
212     public View() {
213         singlePageView = false;
214         mergeWithPageItems = true;
215         translateCodesOnReadOnlyDisplay = false;
216         viewTypeName = ViewType.DEFAULT;
217         formClass = UifFormBase.class;
218         supportsRequestOverrideOfReadOnlyFields = true;
219         disableBrowserCache = true;
220         persistFormToSession = true;
221         sessionPolicy = new ViewSessionPolicy();
222 
223         this.viewIndex = new ViewIndex();
224 
225         additionalScriptFiles = Collections.emptyList();
226         additionalCssFiles = Collections.emptyList();
227         additionalHeadLinks = Collections.emptyList();
228         additionalMetaTags = Collections.emptyList();
229         objectPathToConcreteClassMapping = Collections.emptyMap();
230         viewRequestParameters = Collections.emptyMap();
231         expressionVariables = Collections.emptyMap();
232 
233         dialogs = Collections.emptyList();
234 
235         items = Collections.emptyList();
236         viewTemplates = new LifecycleAwareList<String>(this);
237     }
238 
239     /**
240      * The following initialization is performed:
241      *
242      * <ul>
243      * <li>If a single paged view, set items in page group and put the page in
244      * the items list</li>
245      * <li>If {@link ViewSessionPolicy#enableTimeoutWarning} is enabled add the session timeout dialogs to the
246      * views list of dialog groups</li>
247      * </ul>
248      *
249      * {@inheritDoc}
250      */
251     @SuppressWarnings("unchecked")
252     @Override
253     public void performInitialization(Object model) {
254         if (model instanceof UifFormBase) {
255             UifFormBase form = (UifFormBase) model;
256 
257             // set view page to page requested on form
258             if (StringUtils.isNotBlank(form.getPageId())) {
259                 setCurrentPageId(form.getPageId());
260             }
261 
262             String dialogId = form.getActionParamaterValue(UifParameters.DIALOG_ID);
263             if (StringUtils.isNotBlank(dialogId)) {
264                 form.setShowDialogId(dialogId);
265 
266                 // initialize the view to open the dialog if necessary
267                 initializeDialogLoadScript((UifFormBase) model);
268             }
269         }
270 
271         initializeDialogReclocationScript();
272 
273         super.performInitialization(model);
274 
275         assert this == ViewLifecycle.getView();
276 
277         // populate items on page for single paged view
278         if (singlePageView) {
279             if (page != null) {
280                 // remove default sections of page when requested
281                 if (!mergeWithPageItems) {
282                     page.setItems(new ArrayList<Group>());
283                 }
284 
285                 // add the items configured on the view to the page items, and set as the
286                 // new page items
287                 List<Component> newItems = (List<Component>) page.getItems();
288                 newItems.addAll(items);
289                 page.setItems(newItems);
290 
291                 page.sortItems();
292 
293                 // reset the items list to include the one page
294                 items = new ArrayList<Group>();
295                 ((List<Group>) items).add(page);
296             }
297         }
298         // if items is only size one and instance of page, set singlePageView to true
299         else if ((this.items != null) && (this.items.size() == 1)) {
300             Component itemComponent = this.items.get(0);
301 
302             if (itemComponent instanceof PageGroup) {
303                 this.singlePageView = true;
304             }
305         }
306 
307         if (sessionPolicy.isEnableTimeoutWarning()) {
308             Group warningDialog = ComponentFactory.getSessionTimeoutWarningDialog();
309             warningDialog.setId(ComponentFactory.SESSION_TIMEOUT_WARNING_DIALOG);
310             getDialogs().add(warningDialog);
311 
312             Group timeoutDialog = ComponentFactory.getSessionTimeoutDialog();
313             timeoutDialog.setId(ComponentFactory.SESSION_TIMEOUT_DIALOG);
314             getDialogs().add(timeoutDialog);
315         }
316 
317         breadcrumbOptions.setupBreadcrumbs(model);
318     }
319 
320     /**
321      * Helper method to set the view's load script to open a dialog.
322      *
323      * @param form the form containing data
324      */
325     protected void initializeDialogLoadScript(UifFormBase form) {
326         // check for edit line dialog action parameters and if present, then show the dialog once page is loaded
327         String selectedCollectionPath = form.getActionParamaterValue(UifParameters.SELECTED_COLLECTION_PATH);
328         String selectedLineIndex = form.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX);
329         String dialogId = form.getShowDialogId();
330 
331         if (StringUtils.isNotBlank(dialogId) && StringUtils.isNotBlank(selectedCollectionPath) && StringUtils
332                 .isNotBlank(selectedLineIndex)) {
333             // the line index and the collection path need adjusted to account for nested collections within a
334             // dialog because the dialog is shown per the root collection, but the index and path are per
335             // the nested sub-collection
336             String originalLineIndex = StringUtils.substring(dialogId, dialogId.length() - 1);
337             if (!selectedLineIndex.equals(originalLineIndex)) {
338                 selectedLineIndex = originalLineIndex;
339             }
340 
341             // adjust the collection path to point to the root collection rather than the sub-collection
342             if (selectedCollectionPath.contains(UifPropertyPaths.DIALOG_DATA_OBJECT)) {
343                 String collectionId = StringUtils.substring(dialogId, dialogId.indexOf("_") + 1, dialogId.lastIndexOf(
344                         "_"));
345                 BindingInfo bindingInfo = (BindingInfo) form.getViewPostMetadata().getComponentPostMetadata(
346                         collectionId).getData("bindingInfo");
347                 selectedCollectionPath = bindingInfo.getBindingPath();
348             }
349 
350             String actionScript = "setupImages();";
351             if (StringUtils.startsWith(dialogId, ComponentFactory.EDIT_LINE_DIALOG)) {
352                 actionScript +=
353                         "showEditLineDialog('" + dialogId + "', '" + selectedCollectionPath + "', " + selectedLineIndex
354                                 + ");";
355             } else {
356                 String additionalData = "{ 'actionParameters[selectedCollectionPath]' : '" + selectedCollectionPath
357                         + "', 'actionParameters[selectedLineIndex]' : '0' }";
358                 actionScript += "showDialog('" + dialogId + "', " + additionalData + ");";
359             }
360             setOnLoadScript(ScriptUtils.appendScript(getOnLoadScript(), actionScript));
361         } else if (StringUtils.isNotBlank(dialogId) && !(this instanceof LookupView)) {
362             String actionScript =
363                     "jQuery.unblockUI();setupImages();showLoading('Loading...', window.document);showDialog('"
364                             + dialogId + "');";
365             setOnLoadScript(ScriptUtils.appendScript(getOnLoadScript(), actionScript));
366         }
367     }
368 
369     /**
370      * Without this script dialogs might be associated with a non visible action (i.e. inside a dropdown menu).  This
371      * would cause the dialog not being displayed.  To ensure that all dialog are able to be displayed the DIVs of the
372      * dialogs are moved to a common location.
373      */
374     protected void initializeDialogReclocationScript() {
375         String dialogRelocationScript = "jQuery('.modal').appendTo('#Uif-Dialogs');";
376         setOnLoadScript(ScriptUtils.appendScript(getOnLoadScript(), dialogRelocationScript));
377     }
378 
379     /**
380      * {@inheritDoc}
381      */
382     @Override
383     public void afterEvaluateExpression() {
384         super.afterEvaluateExpression();
385 
386         if (getReadOnly() == null) {
387             setReadOnly(false);
388         }
389     }
390 
391     /**
392      * The following updates are done here:
393      *
394      * <ul>
395      * <li>Invoke expression evaluation on view theme</li>
396      * <li>Invoke theme to configure defaults</li>
397      * </ul>
398      */
399     @Override
400     public void performApplyModel(Object model, LifecycleElement parent) {
401         super.performApplyModel(model, parent);
402 
403         View view = ViewLifecycle.getView();
404         if (theme != null) {
405             ViewLifecycle.getExpressionEvaluator().evaluateExpressionsOnConfigurable(view, theme, getContext());
406 
407             theme.configureThemeDefaults();
408         }
409 
410         if (sessionPolicy != null) {
411             ViewLifecycle.getExpressionEvaluator().evaluateExpressionsOnConfigurable(view, sessionPolicy, getContext());
412         }
413 
414         //handle parentLocation breadcrumb chain
415         parentLocation.constructParentLocationBreadcrumbItems(view, model, view.getContext());
416     }
417 
418     /**
419      * The following is performed:
420      *
421      * <ul>
422      * <li>Adds to its document ready script the setupValidator js function for setting
423      * up the validator for this view</li>
424      * </ul>
425      *
426      * {@inheritDoc}
427      */
428     @SuppressWarnings("unchecked")
429     @Override
430     public void performFinalize(Object model, LifecycleElement parent) {
431         super.performFinalize(model, parent);
432 
433         assert this == ViewLifecycle.getView();
434 
435         String preLoadScript = "";
436         if (this.getPreLoadScript() != null) {
437             preLoadScript = this.getPreLoadScript();
438         }
439 
440         // Retrieve Growl and BlockUI settings
441         Growls gw = getGrowls();
442         if (!gw.getTemplateOptions().isEmpty()) {
443             preLoadScript += "setGrowlDefaults(" + gw.getTemplateOptionsJSString() + ");";
444         }
445 
446         BlockUI navBlockUI = getNavigationBlockUI();
447         if (!navBlockUI.getTemplateOptions().isEmpty()) {
448             preLoadScript += "setBlockUIDefaults("
449                     + navBlockUI.getTemplateOptionsJSString()
450                     + ", '"
451                     + UifConstants.BLOCKUI_NAVOPTS
452                     + "');";
453         }
454 
455         BlockUI refBlockUI = getRefreshBlockUI();
456         if (!refBlockUI.getTemplateOptions().isEmpty()) {
457             preLoadScript += "setBlockUIDefaults("
458                     + refBlockUI.getTemplateOptionsJSString()
459                     + ", '"
460                     + UifConstants.BLOCKUI_REFRESHOPTS
461                     + "');";
462         }
463 
464         this.setPreLoadScript(preLoadScript);
465 
466         String onReadyScript = "";
467         if (this.getOnDocumentReadyScript() != null) {
468             onReadyScript = this.getOnDocumentReadyScript();
469         }
470 
471         // initialize session timers for giving timeout warnings
472         if (sessionPolicy.isEnableTimeoutWarning()) {
473             // warning minutes gives us the time before the timeout occurs to give the warning,
474             // so we need to determine how long that should be from the session start
475             int sessionTimeoutInterval = ((UifFormBase) model).getSessionTimeoutInterval();
476             int sessionWarningSeconds = sessionPolicy.getTimeoutWarningSeconds();
477 
478             if (sessionWarningSeconds >= sessionTimeoutInterval) {
479                 throw new RuntimeException(
480                         "Time until giving the session warning should be less than the session timeout. Session Warning is "
481                                 + sessionWarningSeconds
482                                 + "s, session timeout is "
483                                 + sessionTimeoutInterval
484                                 + "s.");
485             }
486 
487             int sessionWarningInterval = sessionTimeoutInterval - sessionWarningSeconds;
488 
489             onReadyScript = ScriptUtils.appendScript(onReadyScript, ScriptUtils.buildFunctionCall(
490                     UifConstants.JsFunctions.INITIALIZE_SESSION_TIMERS, sessionWarningInterval,
491                     sessionTimeoutInterval));
492         }
493 
494         onReadyScript = ScriptUtils.appendScript(onReadyScript, "jQuery.extend(jQuery.validator.messages, "
495                 + ClientValidationUtils.generateValidatorMessagesOption()
496                 + ");");
497 
498         this.setOnDocumentReadyScript(onReadyScript);
499 
500         // Breadcrumb handling
501         breadcrumbOptions.finalizeBreadcrumbs(model, this, breadcrumbItem);
502 
503         // Add validation default js options for validation framework to View's data attributes
504         Object groupValidationDataDefaults = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
505                 UifConstants.GROUP_VALIDATION_DEFAULTS_MAP_ID);
506         Object fieldValidationDataDefaults = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
507                 UifConstants.FIELD_VALIDATION_DEFAULTS_MAP_ID);
508         Object actionDataDefaults = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
509                 UifConstants.ACTION_DEFAULTS_MAP_ID);
510         Object requiredIndicator = KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(
511                 UifConstants.REQUIRED_INDICATOR_ID);
512 
513         // Add data defaults for common components to the view for use in js (to reduce size of individual components)
514         this.addScriptDataAttribute(UifConstants.DataAttributes.GROUP_VALIDATION_DEFAULTS, ScriptUtils.convertToJsValue(
515                 (Map<String, String>) groupValidationDataDefaults));
516         this.addScriptDataAttribute(UifConstants.DataAttributes.FIELD_VALIDATION_DEFAULTS, ScriptUtils.convertToJsValue(
517                 (Map<String, String>) fieldValidationDataDefaults));
518         this.addScriptDataAttribute(UifConstants.DataAttributes.ACTION_DEFAULTS, ScriptUtils.convertToJsValue(
519                 (Map<String, String>) actionDataDefaults));
520         this.addScriptDataAttribute(UifConstants.DataAttributes.REQ_INDICATOR, (String) requiredIndicator);
521 
522         // give view role attribute for js selections
523         this.addDataAttribute(UifConstants.DataAttributes.ROLE, UifConstants.RoleTypes.VIEW);
524 
525         // Add state mapping to post metadata
526         ViewLifecycle.getViewPostMetadata().addComponentPostData(this, "stateObjectBindingPath",
527                 stateObjectBindingPath);
528         ViewLifecycle.getViewPostMetadata().addComponentPostData(this, "stateMapping", stateMapping);
529 
530         setNestedComponentId(getInstructionalMessage(), this.getId() + UifConstants.IdSuffixes.INSTRUCTIONAL);
531 
532         Header header = getHeader();
533         Help help = getHelp();
534 
535         if (header != null) {
536             setNestedComponentId(header, this.getId() + UifConstants.IdSuffixes.HEADER_WRAPPER);
537             setNestedComponentId(header.getUpperGroup(), this.getId() + UifConstants.IdSuffixes.HEADER_UPPER_GROUP);
538             setNestedComponentId(header.getRightGroup(), this.getId() + UifConstants.IdSuffixes.HEADER_RIGHT_GROUP);
539             setNestedComponentId(header.getLowerGroup(), this.getId() + UifConstants.IdSuffixes.HEADER_LOWER_GROUP);
540             setNestedComponentId(help, this.getId() + UifConstants.IdSuffixes.HELP_WRAPPER);
541             if (help != null && help.getHelpAction() != null) {
542                 setNestedComponentId(help.getHelpAction(), this.getId() + UifConstants.IdSuffixes.HELP_WRAPPER);
543             }
544         }
545     }
546 
547     /**
548      * Helper method for setting a new ID for the nested components
549      *
550      * @param component component to adjust ID for
551      * @param newId
552      */
553     protected void setNestedComponentId(Component component, String newId) {
554         if (component != null) {
555             component.setId(newId);
556         }
557     }
558 
559     /**
560      * {@inheritDoc}
561      */
562     @Override
563     public void notifyCompleted(ViewLifecyclePhase phase) {
564         super.notifyCompleted(phase);
565 
566         if (phase.getViewPhase().equals(UifConstants.ViewPhases.FINALIZE)) {
567             ViewLifecycle.getHelper().performCustomViewFinalize(ViewLifecycle.getModel());
568         }
569     }
570 
571     /**
572      * Gets all breadcrumb items related to this view's parent location.
573      *
574      * @return breadcrumb items
575      */
576     public List<BreadcrumbItem> getBreadcrumbItems() {
577         if (parentLocation == null) {
578             return Collections.emptyList();
579         }
580 
581         List<BreadcrumbItem> breadcrumbItems = new ArrayList<BreadcrumbItem>();
582         breadcrumbItems.add(parentLocation.getPageBreadcrumbItem());
583         breadcrumbItems.add(parentLocation.getViewBreadcrumbItem());
584         for (BreadcrumbItem item : parentLocation.getResolvedBreadcrumbItems()) {
585             if (!breadcrumbItems.contains(item)) {
586                 breadcrumbItems.add(item);
587             }
588         }
589 
590         return breadcrumbItems;
591     }
592 
593     /**
594      * {@inheritDoc}
595      */
596     @Override
597     public Set<Class<? extends Component>> getSupportedComponents() {
598         Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>();
599         supportedComponents.add(Group.class);
600 
601         return supportedComponents;
602     }
603 
604     /**
605      * {@inheritDoc}
606      */
607     @Override
608     public String getComponentTypeName() {
609         return "view";
610     }
611 
612     /**
613      * Iterates through the contained page items and returns the Page that matches the set current page id or
614      * the first page in the case of a single page view.
615      *
616      * @return page group instance
617      */
618     @ViewLifecycleRestriction(exclude = UifConstants.ViewPhases.PRE_PROCESS)
619     public PageGroup getCurrentPage() {
620         if (currentPage != null) {
621             return currentPage;
622         }
623 
624         for (Component item : this.getItems()) {
625             if (!(item instanceof PageGroup)) {
626                 continue;
627             }
628 
629             if (singlePageView || StringUtils.equals(item.getId(), getCurrentPageId())) {
630                 currentPage = (PageGroup) CopyUtils.unwrap(item);
631             }
632         }
633 
634         return currentPage;
635     }
636 
637     /**
638      * Getter for returning the view's items and page for inclusion in the pre-process phase.
639      *
640      * <p>Note this is necessary so we get IDs assigned for all the pages during the pre-process phase. For other
641      * phases, only the current page is picked up.</p>
642      *
643      * @return list of components to include for the pre-process phase
644      */
645     @ViewLifecycleRestriction(UifConstants.ViewPhases.PRE_PROCESS)
646     public List<Component> getPagesForPreprocessing() {
647         List<Component> processProcessItems = new ArrayList<Component>();
648 
649         if (getItems() != null) {
650             processProcessItems.addAll(getItems());
651         }
652 
653         if (getPage() != null) {
654             processProcessItems.add(getPage());
655         }
656 
657         return processProcessItems;
658     }
659 
660     /**
661      * {@inheritDoc}
662      */
663     @Override
664     public void sortItems() {
665         if (!singlePageView) {
666             super.sortItems();
667         }
668     }
669 
670     /**
671      * Namespace code the view should be associated with.
672      *
673      * <p>The namespace code is used within the framework in such places as permission checks and parameter
674      * retrieval</p>
675      *
676      * @return namespace code
677      */
678     @BeanTagAttribute
679     public String getNamespaceCode() {
680         return namespaceCode;
681     }
682 
683     /**
684      * @see View#getNamespaceCode()
685      */
686     public void setNamespaceCode(String namespaceCode) {
687         checkMutable(true);
688         this.namespaceCode = namespaceCode;
689     }
690 
691     /**
692      * View name provides an identifier for a view within a type. That is if a
693      * set of View instances have the same values for the
694      * properties that are used to retrieve them by their type, the name can be
695      * given to further qualify the view that should be retrieved.
696      *
697      * <p>A view type like the LookupView might have several views for
698      * the same object class, but one that is the 'default' lookup and another
699      * that is the 'advanced' lookup. Therefore the name on the first could be
700      * set to 'default', and likewise the name for the second 'advanced'</p>
701      *
702      * @return name of view
703      */
704     @BeanTagAttribute
705     public String getViewName() {
706         return this.viewName;
707     }
708 
709     /**
710      * @see View#getViewName()
711      */
712     public void setViewName(String viewName) {
713         checkMutable(true);
714         this.viewName = viewName;
715     }
716 
717     /**
718      * When true, this view will use a unified header - the page header will be omitted and its title will be used
719      * in the ViewHeader supportTitle property (dynamically updated on page change).
720      *
721      * @return true if using a unified header
722      */
723     @BeanTagAttribute
724     public boolean isUnifiedHeader() {
725         return unifiedHeader;
726     }
727 
728     /**
729      * @see View#isUnifiedHeader()
730      */
731     public void setUnifiedHeader(boolean unifiedHeader) {
732         checkMutable(true);
733         this.unifiedHeader = unifiedHeader;
734     }
735 
736     /**
737      * TopGroup is an optional group of content that appears above the breadcrumbs and view header.
738      *
739      * @return the topGroup component
740      */
741     @BeanTagAttribute
742     public Group getTopGroup() {
743         return topGroup;
744     }
745 
746     /**
747      * @see View#getTopGroup()
748      */
749     public void setTopGroup(Group topGroup) {
750         checkMutable(true);
751         this.topGroup = topGroup;
752     }
753 
754     /**
755      * Header for the application containing the view.
756      *
757      * <p>When deploying outside a portal, the application header and footer property can be configured to
758      * display a consistent header/footer across all views. Here application logos, menus, login controls
759      * and so on can be rendered</p>
760      *
761      * @return application header
762      */
763     @BeanTagAttribute
764     public Header getApplicationHeader() {
765         return applicationHeader;
766     }
767 
768     /**
769      * @see View#getApplicationHeader()
770      */
771     public void setApplicationHeader(Header applicationHeader) {
772         checkMutable(true);
773         this.applicationHeader = applicationHeader;
774     }
775 
776     /**
777      * Footer for the application containing the view.
778      *
779      * <p>When deploying outside a portal, the application header and footer property can be configured to
780      * display a consistent header/footer across all views. Here such things as application links, copyrights
781      * and so on can be rendered</p>
782      *
783      * @return application footer
784      */
785     @BeanTagAttribute
786     public Group getApplicationFooter() {
787         return applicationFooter;
788     }
789 
790     /**
791      * @see View#getApplicationFooter()
792      */
793     public void setApplicationFooter(Group applicationFooter) {
794         checkMutable(true);
795         this.applicationFooter = applicationFooter;
796     }
797 
798     /**
799      * Build footer for the application containing the view.
800      *
801      * @return build footer
802      */
803     @BeanTagAttribute
804     public Group getBuildFooter() {
805         return buildFooter;
806     }
807 
808     /**
809      * @see View#getBuildFooter()
810      */
811     public void setBuildFooter(Group buildFooter) {
812         this.buildFooter = buildFooter;
813     }
814 
815     /**
816      * Title text to be displayed on browser tab.
817      *
818      * @return
819      */
820     @BeanTagAttribute(name = "applicationTitleText")
821     public String getApplicationTitleText() {
822         return applicationTitleText;
823     }
824 
825     /**
826      * @see View#getApplicationTitleText()
827      */
828     public void setApplicationTitleText(String applicationTitleText) {
829         this.applicationTitleText = applicationTitleText;
830     }
831 
832     /**
833      * If true, the top group will be sticky (fixed to top of window).
834      *
835      * @return true if the top group is sticky, false otherwise
836      */
837     @BeanTagAttribute
838     public boolean isStickyTopGroup() {
839         return stickyTopGroup;
840     }
841 
842     /**
843      * @see View#isStickyTopGroup()
844      */
845     public void setStickyTopGroup(boolean stickyTopGroup) {
846         checkMutable(true);
847         this.stickyTopGroup = stickyTopGroup;
848     }
849 
850     /**
851      * If true, the breadcrumb widget will be sticky (fixed to top of window).
852      *
853      * @return true if breadcrumbs are sticky, false otherwise
854      */
855     @BeanTagAttribute
856     public boolean isStickyBreadcrumbs() {
857         return stickyBreadcrumbs;
858     }
859 
860     /**
861      * @see View#isStickyBreadcrumbs()
862      */
863     public void setStickyBreadcrumbs(boolean stickyBreadcrumbs) {
864         checkMutable(true);
865         this.stickyBreadcrumbs = stickyBreadcrumbs;
866     }
867 
868     /**
869      * If true, the ViewHeader for this view will be sticky (fixed to top of window).
870      *
871      * @return true if the header is sticky, false otherwise
872      */
873     @BeanTagAttribute
874     public boolean isStickyHeader() {
875         if (this.getHeader() != null && this.getHeader() instanceof ViewHeader) {
876             return ((ViewHeader) this.getHeader()).isSticky();
877         } else {
878             return false;
879         }
880     }
881 
882     /**
883      * @see View#isStickyHeader()
884      */
885     public void setStickyHeader(boolean stickyHeader) {
886         checkMutable(true);
887         this.stickyHeader = stickyHeader;
888         if (this.getHeader() != null && this.getHeader() instanceof ViewHeader) {
889             ((ViewHeader) this.getHeader()).setSticky(stickyHeader);
890         }
891     }
892 
893     /**
894      * Set to true to make the applicationHeader sticky (fixed to top of window)
895      *
896      * @return true if applicationHeader is sticky, false otherwise
897      */
898     @BeanTagAttribute
899     public boolean isStickyApplicationHeader() {
900         return stickyApplicationHeader;
901     }
902 
903     /**
904      * @see View#isStickyApplicationHeader()
905      */
906     public void setStickyApplicationHeader(boolean stickyApplicationHeader) {
907         checkMutable(true);
908         this.stickyApplicationHeader = stickyApplicationHeader;
909     }
910 
911     /**
912      * If true, the view footer will become sticky (fixed to bottom of window).
913      *
914      * @return ture if the view footer is sticky, false otherwise
915      */
916     @BeanTagAttribute
917     public boolean isStickyFooter() {
918         return stickyFooter;
919     }
920 
921     /**
922      * @see View#isStickyFooter()
923      */
924     public void setStickyFooter(boolean stickyFooter) {
925         checkMutable(true);
926         this.stickyFooter = stickyFooter;
927         if (this.getFooter() != null) {
928             this.getFooter().addDataAttribute(UifConstants.DataAttributes.STICKY_FOOTER, Boolean.toString(
929                     stickyFooter));
930         }
931     }
932 
933     /**
934      * If true, the applicationFooter will become sticky (fixed to bottom of window).
935      *
936      * @return true if the application footer is sticky, false otherwise
937      */
938     @BeanTagAttribute
939     public boolean isStickyApplicationFooter() {
940         return stickyApplicationFooter;
941     }
942 
943     /**
944      * @see View#isStickyApplicationFooter()
945      */
946     public void setStickyApplicationFooter(boolean stickyApplicationFooter) {
947         checkMutable(true);
948         this.stickyApplicationFooter = stickyApplicationFooter;
949     }
950 
951     /**
952      * List of CSS style classes that will be applied to a div that wraps the content.
953      *
954      * <p>Wrapping the content gives the ability to move between a fluid width container or a fixed width
955      * container. The div is also wraps content inside sticky elements (header and footer), so visual treatment
956      * can be given to the full width of the screen while restricting the area of the content.</p>
957      *
958      * <p>In Bootstrap, use 'container-fluid' for a fluid width container, and 'container' for a fixed width
959      * container.</p>
960      *
961      * @return List of css classes to apply to content wrapper div
962      */
963     @BeanTagAttribute
964     public List<String> getContentContainerCssClasses() {
965         return contentContainerCssClasses;
966     }
967 
968     /**
969      * @see View#getContentContainerCssClasses()
970      */
971     public void setContentContainerCssClasses(List<String> contentContainerCssClasses) {
972         this.contentContainerCssClasses = contentContainerCssClasses;
973     }
974 
975     /**
976      * Returns the list of {@link View#getContentContainerCssClasses()} as a concatenated string (each class
977      * is separated by a space).
978      *
979      * @return String of content css classes
980      */
981     public String getContentContainerClassesAsString() {
982         if (contentContainerCssClasses != null) {
983             return StringUtils.join(contentContainerCssClasses, " ");
984         }
985 
986         return "";
987     }
988 
989     /**
990      * Specifies what page should be rendered by default. This is the page that
991      * will be rendered when the View is first rendered or when the
992      * current page is not set
993      *
994      * @return id of the page to render by default
995      */
996     @BeanTagAttribute
997     public String getEntryPageId() {
998         return this.entryPageId;
999     }
1000 
1001     /**
1002      * @see View#getEntryPageId()
1003      */
1004     public void setEntryPageId(String entryPageId) {
1005         checkMutable(true);
1006         this.entryPageId = entryPageId;
1007     }
1008 
1009     /**
1010      * The id for the page within the view that should be displayed in the UI. Other pages of the view will not be
1011      * rendered.
1012      *
1013      * <p>If current page id is not set, it is set to the configured entry page or first item in list id</p>
1014      *
1015      * @return id of the page that should be displayed
1016      */
1017     public String getCurrentPageId() {
1018         // default current page if not set
1019         if (StringUtils.isBlank(currentPageId)) {
1020             if (StringUtils.isNotBlank(entryPageId)) {
1021                 currentPageId = entryPageId;
1022             } else if ((getItems() != null) && !getItems().isEmpty()) {
1023                 Component firstPageGroup = getItems().get(0);
1024                 if (firstPageGroup instanceof PageGroup) {
1025                     currentPageId = firstPageGroup.getId();
1026                 }
1027             }
1028         }
1029 
1030         return this.currentPageId;
1031     }
1032 
1033     /**
1034      * @see View#getCurrentPageId()
1035      */
1036     public void setCurrentPageId(String currentPageId) {
1037         checkMutable(true);
1038         this.currentPageId = currentPageId;
1039         this.currentPage = null;
1040     }
1041 
1042     /**
1043      * NavigationGroup instance for the View<
1044      *
1045      * <p>Provides configuration necessary to render the navigation. This includes
1046      * navigation items in addition to configuration for the navigation
1047      * renderer</p>
1048      *
1049      * @return NavigationGroup
1050      */
1051     @BeanTagAttribute
1052     public Group getNavigation() {
1053         return this.navigation;
1054     }
1055 
1056     /**
1057      * @see View#getNavigation()
1058      */
1059     public void setNavigation(Group navigation) {
1060         checkMutable(true);
1061         this.navigation = navigation;
1062     }
1063 
1064     /**
1065      * Class of the Form that should be used with the View instance.
1066      *
1067      * <p>The form is the top level object for all the view's data and is
1068      * used to present and accept data in the user interface. All form classes
1069      * should extend UifFormBase</p>
1070      *
1071      * @return class for the view's form
1072      * @see org.kuali.rice.krad.web.form.UifFormBase
1073      */
1074     @BeanTagAttribute
1075     public Class<?> getFormClass() {
1076         return this.formClass;
1077     }
1078 
1079     /**
1080      * @see View#getFormClass()
1081      */
1082     public void setFormClass(Class<?> formClass) {
1083         checkMutable(true);
1084         this.formClass = formClass;
1085     }
1086 
1087     /**
1088      * For View types that work primarily with one nested object of
1089      * the form (for instance document, or bo) the default binding object path
1090      * can be set for each of the views DataBinding components. If
1091      * the component does not set its own binding object path it will inherit
1092      * the default.
1093      *
1094      * @return binding path to the object from the form
1095      */
1096     @BeanTagAttribute
1097     public String getDefaultBindingObjectPath() {
1098         return this.defaultBindingObjectPath;
1099     }
1100 
1101     /**
1102      * @see View#getDefaultBindingObjectPath()
1103      */
1104     public void setDefaultBindingObjectPath(String defaultBindingObjectPath) {
1105         checkMutable(true);
1106         this.defaultBindingObjectPath = defaultBindingObjectPath;
1107     }
1108 
1109     /**
1110      * Configures the concrete classes that will be used for properties in the
1111      * form object graph that have an abstract or interface type.
1112      *
1113      * <p>For properties that have an abstract or interface type, it is not
1114      * possible to perform operations like getting/settings property values and
1115      * getting information from the dictionary. When these properties are
1116      * encountered in the object graph, this Map will be consulted
1117      * to determine the concrete type to use</p>
1118      *
1119      * <p>e.g. Suppose we have a property document.accountingLine.accountNumber and
1120      * the accountingLine property on the document instance has an interface
1121      * type 'AccountingLine'. We can then put an entry into this map with key
1122      * 'document.accountingLine', and value
1123      * 'org.kuali.rice.sampleapp.TravelAccountingLine'. When getting the
1124      * property type or an entry from the dictionary for accountNumber, the
1125      * TravelAccountingLine class will be used</p>
1126      *
1127      * @return Map<String, Class> of class implementations keyed by path
1128      */
1129     @BeanTagAttribute
1130     public Map<String, Class<?>> getObjectPathToConcreteClassMapping() {
1131         if (objectPathToConcreteClassMapping == Collections.EMPTY_MAP && isMutable(true)) {
1132             objectPathToConcreteClassMapping = new HashMap<String, Class<?>>();
1133         }
1134 
1135         return this.objectPathToConcreteClassMapping;
1136     }
1137 
1138     /**
1139      * @see View#getObjectPathToConcreteClassMapping()
1140      */
1141     public void setObjectPathToConcreteClassMapping(Map<String, Class<?>> objectPathToConcreteClassMapping) {
1142         checkMutable(true);
1143         this.objectPathToConcreteClassMapping = objectPathToConcreteClassMapping;
1144     }
1145 
1146     /**
1147      * Declares additional script files that should be included with the
1148      * View.
1149      *
1150      * <p>These files are brought into the HTML page along with
1151      * common script files configured for the Rice application. Each entry
1152      * contain the path to the CSS file, either a relative path, path from web
1153      * root, or full URI</p>
1154      *
1155      * <p>e.g. '/krad/scripts/myScript.js', '../scripts/myScript.js',
1156      * 'http://my.edu/web/myScript.js'</p>;
1157      *
1158      * @return script file locations
1159      */
1160     @BeanTagAttribute
1161     public List<String> getAdditionalScriptFiles() {
1162         if (additionalScriptFiles == Collections.EMPTY_LIST && isMutable(true)) {
1163             additionalScriptFiles = new LifecycleAwareList<String>(this);
1164         }
1165 
1166         return additionalScriptFiles;
1167     }
1168 
1169     /**
1170      * @see View#getAdditionalScriptFiles()
1171      */
1172     public void setAdditionalScriptFiles(List<String> additionalScriptFiles) {
1173         checkMutable(true);
1174         if (additionalScriptFiles == null) {
1175             this.additionalScriptFiles = Collections.emptyList();
1176         } else {
1177             this.additionalScriptFiles = new LifecycleAwareList<String>(this, additionalScriptFiles);
1178         }
1179     }
1180 
1181     /**
1182      * Declares additional CSS files that should be included with the View.
1183      *
1184      * <p>These files are brought into the HTML page along with
1185      * common CSS files configured for the Rice application. Each entry should
1186      * contain the path to the CSS file, either a relative path, path from web
1187      * root, or full URI</p>
1188      *
1189      * <p>e.g. '/krad/css/stacked-view.css', '../css/stacked-view.css',
1190      * 'http://my.edu/web/stacked-view.css'</p>;
1191      *
1192      * @return CSS file locations
1193      */
1194     @BeanTagAttribute
1195     public List<String> getAdditionalCssFiles() {
1196         if (additionalCssFiles == Collections.EMPTY_LIST && isMutable(true)) {
1197             additionalCssFiles = new LifecycleAwareList<String>(this);
1198         }
1199 
1200         return additionalCssFiles;
1201     }
1202 
1203     /**
1204      * @see View#getAdditionalCssFiles()
1205      */
1206     public void setAdditionalCssFiles(List<String> additionalCssFiles) {
1207         checkMutable(true);
1208         if (additionalCssFiles == null) {
1209             this.additionalCssFiles = Collections.emptyList();
1210         } else {
1211             this.additionalCssFiles = new LifecycleAwareList<String>(this, additionalCssFiles);
1212         }
1213     }
1214 
1215     /**
1216      * List of additional link tags that should be included with the View in the html head.
1217      *
1218      * @return headlink objects
1219      */
1220     @BeanTagAttribute
1221     public List<HeadLink> getAdditionalHeadLinks() {
1222         return additionalHeadLinks;
1223     }
1224 
1225     /**
1226      * @see View#getAdditionalHeadLinks()
1227      */
1228     public void setAdditionalHeadLinks(List<HeadLink> additionalHeadLinks) {
1229         this.additionalHeadLinks = additionalHeadLinks;
1230     }
1231 
1232     /**
1233      * List of additional meta tags that should be included with the View in the html head tag.
1234      *
1235      * @return   additionalMetaTags
1236      */
1237     @BeanTagAttribute
1238     public List<MetaTag> getAdditionalMetaTags() {
1239         return additionalMetaTags;
1240     }
1241 
1242     /**
1243      * @see View#getAdditionalMetaTags()
1244      */
1245     public void setAdditionalMetaTags(List<MetaTag> additionalMetaTags) {
1246         this.additionalMetaTags = additionalMetaTags;
1247     }
1248 
1249     /**
1250      * True if the libraryCssClasses set on components will be output to their class attribute, false otherwise.
1251      *
1252      * @return true if using libraryCssClasses on components
1253      */
1254     @BeanTagAttribute
1255     public boolean isUseLibraryCssClasses() {
1256         return useLibraryCssClasses;
1257     }
1258 
1259     /**
1260      * @see View#isUseLibraryCssClasses()
1261      */
1262     public void setUseLibraryCssClasses(boolean useLibraryCssClasses) {
1263         checkMutable(true);
1264         this.useLibraryCssClasses = useLibraryCssClasses;
1265     }
1266 
1267     /**
1268      * List of templates that are used to render the view.
1269      *
1270      * <p>This list will be populated by unique template names as the components of the view are being processed.
1271      * Additional templates can be added in the view configuration if desired. At the beginning of the the view
1272      * rendering, each template in the list will then be included or processed by the template language</p>
1273      *
1274      * <p>Note the user of this depends on the template language being used for rendering. Some languages might require
1275      * including the template for each component instance (for example JSP templates). While others might simply
1276      * include markup that is then available for rendering each component instance (for example FreeMarker which has
1277      * a macro for each component that the template defines)</p>
1278      *
1279      * @return list of template names that should be included for rendering the view
1280      */
1281     public List<String> getViewTemplates() {
1282         return viewTemplates;
1283     }
1284 
1285     /**
1286      * Adds a template to the views include list.
1287      *
1288      * @param template path to template to add
1289      */
1290     public void addViewTemplate(String template) {
1291         if (StringUtils.isEmpty(template)) {
1292             return;
1293         }
1294 
1295         if (!viewTemplates.contains(template)) {
1296             synchronized (viewTemplates) {
1297                 viewTemplates.add(template);
1298             }
1299         }
1300     }
1301 
1302     /**
1303      * Setter for the the list of template names that should be included to render the view
1304      *
1305      * @param viewTemplates
1306      */
1307     public void setViewTemplates(List<String> viewTemplates) {
1308         checkMutable(true);
1309 
1310         if (viewTemplates == null) {
1311             this.viewTemplates = new LifecycleAwareList<String>(this);
1312         } else {
1313             this.viewTemplates = new LifecycleAwareList<String>(this, viewTemplates);
1314         }
1315     }
1316 
1317     /**
1318      * View type name the view is associated with the view instance
1319      *
1320      * <p>Views that share common features and functionality can be grouped by the
1321      * view type. Usually view types extend the <code>View</code> class to
1322      * provide additional configuration and to set defaults. View types can also
1323      * implement the <code>ViewTypeService</code> to add special indexing and
1324      * retrieval of views</p>
1325      *
1326      * @return view type name for the view
1327      */
1328     @BeanTagAttribute
1329     public ViewType getViewTypeName() {
1330         return this.viewTypeName;
1331     }
1332 
1333     /**
1334      * @see View#getViewTypeName()
1335      */
1336     public void setViewTypeName(ViewType viewTypeName) {
1337         checkMutable(true);
1338         this.viewTypeName = viewTypeName;
1339     }
1340 
1341     /**
1342      * Class name of the ViewHelperService that handles the various phases of the Views lifecycle.
1343      *
1344      * @return Class for the spring bean
1345      * @see org.kuali.rice.krad.uif.service.ViewHelperService
1346      */
1347     @BeanTagAttribute
1348     public Class<? extends ViewHelperService> getViewHelperServiceClass() {
1349         return this.viewHelperServiceClass;
1350     }
1351 
1352     /**
1353      * Setter for the <code>ViewHelperService</code> class name
1354      * Also initializes the viewHelperService
1355      *
1356      * @param viewHelperServiceClass
1357      */
1358     public void setViewHelperServiceClass(Class<? extends ViewHelperService> viewHelperServiceClass) {
1359         checkMutable(true);
1360         this.viewHelperServiceClass = viewHelperServiceClass;
1361         if ((this.viewHelperService == null) && (this.viewHelperServiceClass != null)) {
1362             viewHelperService = KRADUtils.createNewObjectFromClass(viewHelperServiceClass);
1363         }
1364     }
1365 
1366     /**
1367      * Creates the <code>ViewHelperService</code> associated with the View
1368      *
1369      * @return ViewHelperService instance
1370      */
1371     @BeanTagAttribute
1372     public ViewHelperService getViewHelperService() {
1373         return viewHelperService;
1374     }
1375 
1376     /**
1377      * @see View#getViewHelperServiceClass()
1378      */
1379     public void setViewHelperService(ViewHelperService viewHelperService) {
1380         checkMutable(true);
1381         this.viewHelperService = viewHelperService;
1382     }
1383 
1384     /**
1385      * Invoked to produce a ViewIndex of the current view's components
1386      */
1387     public void clearIndex() {
1388         if (this.viewIndex == null) {
1389             this.viewIndex = new ViewIndex();
1390         }
1391         this.viewIndex.clearIndex(this);
1392     }
1393 
1394     /**
1395      * Holds field indexes of the View instance for retrieval.
1396      *
1397      * @return ViewIndex instance
1398      */
1399     public ViewIndex getViewIndex() {
1400         return this.viewIndex;
1401     }
1402 
1403     /**
1404      * Map of parameters from the request that set view options, used to rebuild
1405      * the view on each post
1406      *
1407      * <p>
1408      * Views can be configured by parameters. These might impact which parts of
1409      * the view are rendered or how the view behaves. Generally these would get
1410      * passed in when a new view is requested (by request parameters). These
1411      * will be used to initially populate the view properties. In addition, on a
1412      * post the view will be rebuilt and properties reset again by the allow
1413      * request parameters.
1414      * </p>
1415      *
1416      * <p>
1417      * Example parameter would be for MaintenaceView whether a New, Edit, or
1418      * Copy was requested (maintenance mode)
1419      * </p>
1420      *
1421      * @return
1422      */
1423     public Map<String, String> getViewRequestParameters() {
1424         return this.viewRequestParameters;
1425     }
1426 
1427     /**
1428      * @see View#getViewRequestParameters()
1429      */
1430     public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
1431         checkMutable(true);
1432         this.viewRequestParameters = Collections.unmodifiableMap(viewRequestParameters);
1433     }
1434 
1435     /**
1436      * Indicates whether the form (model) associated with the view should be stored in the user session.
1437      *
1438      * <p>The form class (or model) is used to hold the data that backs the view along with the built view object. Storing
1439      * the form instance in session allows many things:
1440      *
1441      * <ul>
1442      * <li>Data does not need to be rebuilt for each server request (for example a collection)</li>
1443      * <li>Data that does not need to go to the user can remain on the form, reducing the size of the response and
1444      * improving security</li>
1445      * <li>Data can be keep around in a 'pre-save' state. When requested by the user changes can then be persisted to
1446      * the database</li>
1447      * <li>Certain information about the view that was rendered, such as input fields, collection paths, and refresh
1448      * components can be kept on the form to support UI interaction</li>
1449      * </ul>
1450      *
1451      * Setting this flag to false will prevent the form from being kept in session and as a result will limit what can
1452      * be done by the framework. In almost all cases this is not recommended</p>
1453      *
1454      * <p>Note all forms will be cleared when the user session expires (based on the rice configuration). In addition, the
1455      * framework enables clear points on certain actions to remove the form when it is no longer needed</p>
1456      *
1457      * @return true if the form should be stored in the user session, false if only request based
1458      */
1459     @BeanTagAttribute
1460     public boolean isPersistFormToSession() {
1461         return persistFormToSession;
1462     }
1463 
1464     /**
1465      * @see View#isPersistFormToSession()
1466      */
1467     public void setPersistFormToSession(boolean persistFormToSession) {
1468         checkMutable(true);
1469         this.persistFormToSession = persistFormToSession;
1470     }
1471 
1472     /**
1473      * Configures behavior that should occur when a session timeout occurs on the view.
1474      *
1475      * @return view session policy instance
1476      */
1477     @BeanTagAttribute
1478     public ViewSessionPolicy getSessionPolicy() {
1479         return sessionPolicy;
1480     }
1481 
1482     /**
1483      * @see View#getSessionPolicy()
1484      */
1485     public void setSessionPolicy(ViewSessionPolicy sessionPolicy) {
1486         checkMutable(true);
1487         this.sessionPolicy = sessionPolicy;
1488     }
1489 
1490     /**
1491      * PresentationController that should be used for the View instance.
1492      *
1493      * <p>The presentation controller is consulted to determine component (group,
1494      * field) state such as required, read-only, and hidden. The presentation
1495      * controller does not take into account user permissions. The presentation
1496      * controller can also output action flags and edit modes that will be set
1497      * onto the view instance and can be referred to by conditional expressions</p>
1498      *
1499      * @return PresentationController
1500      */
1501     @BeanTagAttribute
1502     public ViewPresentationController getPresentationController() {
1503         return this.presentationController;
1504     }
1505 
1506     /**
1507      * @see View#getPresentationController()
1508      */
1509     public void setPresentationController(ViewPresentationController presentationController) {
1510         checkMutable(true);
1511         this.presentationController = presentationController;
1512     }
1513 
1514     /**
1515      * Setter for the view's presentation controller by class
1516      *
1517      * @param presentationControllerClass
1518      */
1519     public void setPresentationControllerClass(
1520             Class<? extends ViewPresentationController> presentationControllerClass) {
1521         checkMutable(true);
1522         this.presentationController = KRADUtils.createNewObjectFromClass(presentationControllerClass);
1523     }
1524 
1525     /**
1526      * Authorizer that should be used for the View instance
1527      *
1528      * <p>The authorizer class is consulted to determine component (group, field)
1529      * state such as required, read-only, and hidden based on the users
1530      * permissions. It typically communicates with the Kuali Identity Management
1531      * system to determine roles and permissions. It is used with the
1532      * presentation controller and dictionary conditional logic to determine the
1533      * final component state. The authorizer can also output action flags and
1534      * edit modes that will be set onto the view instance and can be referred to
1535      * by conditional expressions</p>
1536      *
1537      * @return Authorizer
1538      */
1539     @BeanTagAttribute
1540     public ViewAuthorizer getAuthorizer() {
1541         return this.authorizer;
1542     }
1543 
1544     /**
1545      * @see View#getAuthorizer()
1546      */
1547     public void setAuthorizer(ViewAuthorizer authorizer) {
1548         checkMutable(true);
1549         this.authorizer = authorizer;
1550     }
1551 
1552     /**
1553      * Setter for the view's authorizer by class
1554      *
1555      * @param authorizerClass
1556      */
1557     public void setAuthorizerClass(Class<? extends ViewAuthorizer> authorizerClass) {
1558         checkMutable(true);
1559         this.authorizer = KRADUtils.createNewObjectFromClass(authorizerClass);
1560     }
1561 
1562     /**
1563      * Map of strings that flag what actions can be taken in the UI.
1564      *
1565      * <p>These can be used in conditional expressions in the dictionary or by
1566      * other UI logic</p>
1567      *
1568      * @return action flags
1569      */
1570     @BeanTagAttribute
1571     public Map<String, Boolean> getActionFlags() {
1572         if (actionFlags == Collections.EMPTY_MAP && isMutable(true)) {
1573             actionFlags = new LifecycleAwareMap<String, Boolean>(this);
1574         }
1575 
1576         return actionFlags;
1577     }
1578 
1579     /**
1580      * @see View#getActionFlags()
1581      */
1582     public void setActionFlags(Map<String, Boolean> actionFlags) {
1583         checkMutable(true);
1584         if (actionFlags == null) {
1585             this.actionFlags = Collections.emptyMap();
1586         } else {
1587             this.actionFlags = new LifecycleAwareMap<String, Boolean>(this, actionFlags);
1588         }
1589     }
1590 
1591     /**
1592      * Map of edit modes that enabled for the view.
1593      *
1594      * <p>These can be used in conditional expressions in the dictionary or by
1595      * other UI logic</p>
1596      *
1597      * @return edit modes
1598      */
1599     @BeanTagAttribute
1600     public Map<String, Boolean> getEditModes() {
1601         if (editModes == Collections.EMPTY_MAP && isMutable(true)) {
1602             editModes = new LifecycleAwareMap<String, Boolean>(this);
1603         }
1604 
1605         return editModes;
1606     }
1607 
1608     /**
1609      * @see View#getEditModes()
1610      */
1611     public void setEditModes(Map<String, Boolean> editModes) {
1612         checkMutable(true);
1613         if (editModes == null) {
1614             this.editModes = Collections.emptyMap();
1615         } else {
1616             this.editModes = new LifecycleAwareMap<String, Boolean>(this, editModes);
1617         }
1618     }
1619 
1620     /**
1621      * Map that contains expressions to evaluate and make available as variables
1622      * for conditional expressions within the view.
1623      *
1624      * <p>Each Map entry contains one expression variables, where the map key gives
1625      * the name for the variable, and the map value gives the variable
1626      * expression. The variables expressions will be evaluated before
1627      * conditional logic is run and made available as variables for other
1628      * conditional expressions. Variable expressions can be based on the model
1629      * and any object contained in the view's context</p>
1630      *
1631      * @return variable expressions
1632      */
1633     @BeanTagAttribute
1634     public Map<String, String> getExpressionVariables() {
1635         return this.expressionVariables;
1636     }
1637 
1638     /**
1639      * @see View#getExpressionVariables()
1640      */
1641     public void setExpressionVariables(Map<String, String> expressionVariables) {
1642         checkMutable(true);
1643         this.expressionVariables = Collections.unmodifiableMap(expressionVariables);
1644     }
1645 
1646     /**
1647      * Indicates whether the View only has a single page
1648      * Group or contains multiple page Group instances.
1649      *
1650      * <p>In the case of a single page it is assumed the group's items
1651      * list contains the section groups for the page, and the page itself is
1652      * given by the page property ({@link #getPage()}. This is for convenience
1653      * of configuration and also can drive other configuration like styling</p>
1654      *
1655      * @return true if the view only contains one page group, false if
1656      * it contains multple pages
1657      */
1658     @BeanTagAttribute
1659     public boolean isSinglePageView() {
1660         return this.singlePageView;
1661     }
1662 
1663     /**
1664      * @see View#isSinglePageView()
1665      */
1666     public void setSinglePageView(boolean singlePageView) {
1667         checkMutable(true);
1668         this.singlePageView = singlePageView;
1669     }
1670 
1671     /**
1672      * Indicates whether the default sections specified in the page items list
1673      * should be included for this view.  This only applies to single paged views.
1674      *
1675      * @return true if the view should contain the default sections
1676      * specified in the page
1677      */
1678     @BeanTagAttribute
1679     public boolean isMergeWithPageItems() {
1680         return mergeWithPageItems;
1681     }
1682 
1683     /**
1684      * @see View#isMergeWithPageItems()
1685      */
1686     public void setMergeWithPageItems(boolean mergeWithPageItems) {
1687         checkMutable(true);
1688         this.mergeWithPageItems = mergeWithPageItems;
1689     }
1690 
1691     /**
1692      * For single paged views ({@link #isSinglePageView()}, gives the page
1693      * Group the view should render. The actual items for the page
1694      * is taken from the group's items list ({@link #getItems()}, and set onto
1695      * the give page group. This is for convenience of configuration.
1696      *
1697      * @return page group for single page views
1698      */
1699     @ViewLifecycleRestriction
1700     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECT)
1701     public PageGroup getPage() {
1702         return this.page;
1703     }
1704 
1705     /**
1706      * @see View#getPage()
1707      */
1708     public void setPage(PageGroup page) {
1709         checkMutable(true);
1710         this.page = page;
1711     }
1712 
1713     /**
1714      * {@inheritDoc}
1715      */
1716     @Override
1717     @ViewLifecycleRestriction
1718     @BeanTagAttribute
1719     public List<? extends Component> getItems() {
1720         if (items == Collections.EMPTY_LIST && isMutable(true)) {
1721             items = new LifecycleAwareList<Component>(this);
1722         }
1723 
1724         return items;
1725     }
1726 
1727     /**
1728      * {@inheritDoc}
1729      */
1730     @SuppressWarnings("unchecked")
1731     @Override
1732     public void setItems(List<? extends Component> items) {
1733         checkMutable(true);
1734 
1735         if (items == null) {
1736             this.items = Collections.emptyList();
1737         } else {
1738             // TODO: Fix this unchecked condition.
1739             this.items = new LifecycleAwareList<Component>(this, (List<Component>) items);
1740         }
1741     }
1742 
1743     /**
1744      * Provide a list of dialog groups associated with this view.
1745      *
1746      * @return List of dialog Groups
1747      */
1748     @BeanTagAttribute
1749     @ViewLifecycleRestriction(value = UifConstants.ViewPhases.FINALIZE,
1750             condition = "!ajaxRequest || (ajaxReturnType eq 'update-view') || (ajaxReturnType eq 'update-page')")
1751     public List<Group> getDialogs() {
1752         if (dialogs == Collections.EMPTY_LIST && isMutable(true)) {
1753             dialogs = new LifecycleAwareList<Group>(this);
1754         }
1755 
1756         return dialogs;
1757     }
1758 
1759     /**
1760      * @see View#getDialogs()
1761      */
1762     public void setDialogs(List<Group> dialogs) {
1763         checkMutable(true);
1764 
1765         if (dialogs == null) {
1766             this.dialogs = Collections.emptyList();
1767         } else {
1768             this.dialogs = new LifecycleAwareList<Group>(this, dialogs);
1769         }
1770     }
1771 
1772     /**
1773      * Breadcrumb widget used for displaying homeward path and history
1774      *
1775      * @return the breadcrumbs
1776      */
1777     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
1778     public Breadcrumbs getBreadcrumbs() {
1779         return this.breadcrumbs;
1780     }
1781 
1782     /**
1783      * @param breadcrumbs the breadcrumbs to set
1784      */
1785     public void setBreadcrumbs(Breadcrumbs breadcrumbs) {
1786         checkMutable(true);
1787         this.breadcrumbs = breadcrumbs;
1788     }
1789 
1790     /**
1791      * The breadcrumbOptions for this view.
1792      *
1793      * <p>Render options set at the view level are always ignored (only apply to
1794      * page level BreadcrumbOptions).  BreadcrumbOptions for homewardPathBreadcrumbs,
1795      * preViewBreadcrumbs, prePageBreadcrumbs,
1796      * and breadcrumbOverrides are inherited by
1797      * child pages unless they override them themselves.</p>
1798      *
1799      * @return the BreadcrumbOptions for this view
1800      */
1801     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
1802     public BreadcrumbOptions getBreadcrumbOptions() {
1803         return breadcrumbOptions;
1804     }
1805 
1806     /**
1807      * @see View#getBreadcrumbOptions()
1808      */
1809     public void setBreadcrumbOptions(BreadcrumbOptions breadcrumbOptions) {
1810         checkMutable(true);
1811         this.breadcrumbOptions = breadcrumbOptions;
1812     }
1813 
1814     /**
1815      * The View's breadcrumbItem defines settings for the breadcrumb which appears in the breadcrumb list for this
1816      * view.
1817      *
1818      * @return the breadcrumbItem
1819      */
1820     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
1821     public BreadcrumbItem getBreadcrumbItem() {
1822         return breadcrumbItem;
1823     }
1824 
1825     /**
1826      * @see View#getBreadcrumbItem()
1827      */
1828     public void setBreadcrumbItem(BreadcrumbItem breadcrumbItem) {
1829         checkMutable(true);
1830         this.breadcrumbItem = breadcrumbItem;
1831     }
1832 
1833     /**
1834      * The parentLocation defines urls that represent the parent of a View in a conceptial site hierarchy.
1835      *
1836      * <p>By defining a parent with these urls defined, a breadcrumb chain can be generated and displayed automatically
1837      * before this View's breadcrumbItem(s).  To chain multiple views, the urls must be defining viewId and
1838      * controllerMapping settings instead of setting an href directly (this will end the chain).  If labels are
1839      * not set on parentLocations, the labels will attempt to be derived from parent views/pages breadcrumbItem
1840      * and headerText - if these contain expressions which cannot be evaluated in the current context an exception
1841      * will be thrown</p>
1842      *
1843      * @return the parentLocation
1844      */
1845     @BeanTagAttribute(type = BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
1846     public ParentLocation getParentLocation() {
1847         return parentLocation;
1848     }
1849 
1850     /**
1851      * @see View#getParentLocation()
1852      */
1853     public void setParentLocation(ParentLocation parentLocation) {
1854         checkMutable(true);
1855         this.parentLocation = parentLocation;
1856     }
1857 
1858     /**
1859      * The pathBasedBreadcrumbs for this View.  These can only be set by the framework.
1860      *
1861      * @return the path based breadcrumbs
1862      */
1863     public List<BreadcrumbItem> getPathBasedBreadcrumbs() {
1864         return pathBasedBreadcrumbs;
1865     }
1866 
1867     /**
1868      * The pathBasedBreadcrumbs for this View.
1869      *
1870      * @param pathBasedBreadcrumbs
1871      */
1872     public void setPathBasedBreadcrumbs(List<BreadcrumbItem> pathBasedBreadcrumbs) {
1873         checkMutable(true);
1874         this.pathBasedBreadcrumbs = pathBasedBreadcrumbs == null ? null : new LifecycleAwareList<BreadcrumbItem>(this,
1875                 pathBasedBreadcrumbs);
1876     }
1877 
1878     /**
1879      * Growls widget which sets up global settings for the growls used in this
1880      * view and its pages.
1881      *
1882      * @return the growls
1883      */
1884     @BeanTagAttribute
1885     public Growls getGrowls() {
1886         return this.growls;
1887     }
1888 
1889     /**
1890      * @param growls the growls to set
1891      */
1892     public void setGrowls(Growls growls) {
1893         checkMutable(true);
1894         this.growls = growls;
1895     }
1896 
1897     /**
1898      * @return returns the refresh block object
1899      */
1900     @BeanTagAttribute
1901     public BlockUI getRefreshBlockUI() {
1902         return refreshBlockUI;
1903     }
1904 
1905     /**
1906      * Set the refresh BlockUI used with single element blocking
1907      * (such as ajax based element loading/updates)
1908      *
1909      * @param refreshBlockUI
1910      */
1911     public void setRefreshBlockUI(BlockUI refreshBlockUI) {
1912         checkMutable(true);
1913         this.refreshBlockUI = refreshBlockUI;
1914     }
1915 
1916     /**
1917      * @return returns the navigation block object
1918      */
1919     @BeanTagAttribute
1920     public BlockUI getNavigationBlockUI() {
1921         return navigationBlockUI;
1922     }
1923 
1924     /**
1925      * Set the navigation BlockUI used with single page blocking
1926      * (such as full page loading/saving)
1927      *
1928      * @param navigationBlockUI
1929      */
1930     public void setNavigationBlockUI(BlockUI navigationBlockUI) {
1931         checkMutable(true);
1932         this.navigationBlockUI = navigationBlockUI;
1933     }
1934 
1935     /**
1936      * Whether to use growls to show messages - info, warning and error
1937      *
1938      * <p>Growls use the messages contained in the message map. If enabled, info
1939      * messages in their entirety will be displayed in growls, for warning and
1940      * error messages a growl message will notify the user that these messages
1941      * exist on the page.</p>
1942      *
1943      * <p> If this setting is disabled, it is recommended that
1944      * infoMessage display be enabled for the page ValidationMessages bean in order to
1945      * display relevant information to the user. Note: the growl scripts are
1946      * built out in the PageGroup class.</p>
1947      *
1948      * @return the growlMessagingEnabled
1949      */
1950     @BeanTagAttribute
1951     public boolean isGrowlMessagingEnabled() {
1952         return this.growlMessagingEnabled;
1953     }
1954 
1955     /**
1956      * enable or disable showing of messages using growls
1957      *
1958      * @param growlMessagingEnabled the growlMessagingEnabled to set
1959      */
1960     public void setGrowlMessagingEnabled(boolean growlMessagingEnabled) {
1961         checkMutable(true);
1962         this.growlMessagingEnabled = growlMessagingEnabled;
1963     }
1964 
1965     /**
1966      * Indicates whether the form should be validated for dirtyness.
1967      *
1968      * <p>For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
1969      * InputFields will be validated on refresh, navigate, cancel or close Action or on form
1970      * unload and if dirty, displays a message and user can decide whether to continue with
1971      * the action or stay on the form. For lookup and inquiry, it's not needed to validate</p>
1972      *
1973      * @return true if dirty validation is set
1974      */
1975     @BeanTagAttribute
1976     public boolean isApplyDirtyCheck() {
1977         return this.applyDirtyCheck;
1978     }
1979 
1980     /**
1981      * @see View#isApplyDirtyCheck()
1982      */
1983     public void setApplyDirtyCheck(boolean applyDirtyCheck) {
1984         checkMutable(true);
1985         this.applyDirtyCheck = applyDirtyCheck;
1986     }
1987 
1988     /**
1989      * Returns whether the current view supports displaying <code>KualiCode</code>'s name as additional display value
1990      *
1991      * @return true if the current view supports
1992      */
1993     @BeanTagAttribute
1994     public boolean isTranslateCodesOnReadOnlyDisplay() {
1995         return translateCodesOnReadOnlyDisplay;
1996     }
1997 
1998     /**
1999      * Indicates whether the Name of the Code should be displayed when a property is of type <code>KualiCode</code>
2000      *
2001      * @param translateCodesOnReadOnlyDisplay indicates whether KualiCode's name should be included.
2002      */
2003     public void setTranslateCodesOnReadOnlyDisplay(boolean translateCodesOnReadOnlyDisplay) {
2004         checkMutable(true);
2005         this.translateCodesOnReadOnlyDisplay = translateCodesOnReadOnlyDisplay;
2006     }
2007 
2008     /**
2009      * Indicates whether the view allows read only fields to be specified on the request URL which will
2010      * override the view setting.
2011      *
2012      * <p>If enabled, the readOnlyFields request parameter can be sent to indicate fields that should be set read only</p>
2013      *
2014      * @return true if read only request overrides are allowed, false if not
2015      */
2016     @BeanTagAttribute
2017     public boolean isSupportsRequestOverrideOfReadOnlyFields() {
2018         return supportsRequestOverrideOfReadOnlyFields;
2019     }
2020 
2021     /**
2022      * @see View#isSupportsRequestOverrideOfReadOnlyFields()
2023      */
2024     public void setSupportsRequestOverrideOfReadOnlyFields(boolean supportsRequestOverrideOfReadOnlyFields) {
2025         checkMutable(true);
2026         this.supportsRequestOverrideOfReadOnlyFields = supportsRequestOverrideOfReadOnlyFields;
2027     }
2028 
2029     /**
2030      * Indicates whether the browser autocomplete functionality should be disabled for the
2031      * entire form (adds autocomplete="off").
2032      *
2033      * <p>The browser's native autocomplete functionality can cause issues with security fields and also fields
2034      * with the UIF suggest widget enabled</p>
2035      *
2036      * @return true if the native autocomplete should be turned off for the form, false if not
2037      */
2038     @BeanTagAttribute
2039     public boolean isDisableNativeAutocomplete() {
2040         return disableNativeAutocomplete;
2041     }
2042 
2043     /**
2044      * @see View#isDisableNativeAutocomplete()
2045      */
2046     public void setDisableNativeAutocomplete(boolean disableNativeAutocomplete) {
2047         checkMutable(true);
2048         this.disableNativeAutocomplete = disableNativeAutocomplete;
2049     }
2050 
2051     /**
2052      * Enables functionality to bust the browsers cache by appending an unique cache key.
2053      *
2054      * <p>Since response headers are unreliable for preventing caching in all browsers, the
2055      * framework uses a technique for updating the URL to include an unique cache key. If the
2056      * HTML 5 History API is supported a parameter can be added to the URL which causes the browser
2057      * to not find the cached page when the user goes back. If not the framework falls back to using
2058      * a hash key and resubmitting using script to pull the latest</p>
2059      *
2060      * @return true if cache for the view should be disabled, false if not
2061      */
2062     @BeanTagAttribute
2063     public boolean isDisableBrowserCache() {
2064         return disableBrowserCache;
2065     }
2066 
2067     /**
2068      * @see View#isDisableBrowserCache()
2069      */
2070     public void setDisableBrowserCache(boolean disableBrowserCache) {
2071         checkMutable(true);
2072         this.disableBrowserCache = disableBrowserCache;
2073     }
2074 
2075     /**
2076      * Script that is executed at the beginning of page load (before any other script).
2077      *
2078      * <p>Many used to set server variables client side</p>
2079      *
2080      * @return pre load script
2081      */
2082     @BeanTagAttribute
2083     public String getPreLoadScript() {
2084         return preLoadScript;
2085     }
2086 
2087     /**
2088      * @see View#getPreLoadScript()
2089      */
2090     public void setPreLoadScript(String preLoadScript) {
2091         checkMutable(true);
2092         this.preLoadScript = preLoadScript;
2093     }
2094 
2095     /**
2096      * The theme which contains stylesheets for this view.
2097      *
2098      * @return ViewTheme
2099      */
2100     @BeanTagAttribute
2101     public ViewTheme getTheme() {
2102         return theme;
2103     }
2104 
2105     /**
2106      * @see View#getTheme()
2107      */
2108     public void setTheme(ViewTheme theme) {
2109         checkMutable(true);
2110         this.theme = theme;
2111     }
2112 
2113     /**
2114      * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to
2115      * determine what field in the model state information is stored in for this view.  Used during View validation.
2116      *
2117      * @return stateObjectBindingPath path to the object storing state information
2118      */
2119     @BeanTagAttribute
2120     public String getStateObjectBindingPath() {
2121         return stateObjectBindingPath;
2122     }
2123 
2124     /**
2125      * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to
2126      * determine what field in the model state information is stored in for this view.  Used during View validation.
2127      *
2128      * @param stateObjectBindingPath
2129      */
2130     public void setStateObjectBindingPath(String stateObjectBindingPath) {
2131         checkMutable(true);
2132         this.stateObjectBindingPath = stateObjectBindingPath;
2133     }
2134 
2135     /**
2136      * Gets the stateMapping.
2137      *
2138      * <p>The state mapping object is used to determine the state information for a view,
2139      * it must include an ordered list of states, and where to find the state information for the view.
2140      * A stateMapping must be set for state based validation to occur.  When stateMapping information is
2141      * not included, the view's model is considered stateless and all constraints will apply regardless of their
2142      * state information or replacements (ie, they will function as they did in version 2.1).</p>
2143      *
2144      * @return information needed for state based validation, if null no state based validation
2145      * functionality will exist and configured constraints will apply regardless of state
2146      * @since 2.2
2147      */
2148     @BeanTagAttribute
2149     public StateMapping getStateMapping() {
2150         return stateMapping;
2151     }
2152 
2153     /**
2154      * @see View#getStateMapping()
2155      */
2156     public void setStateMapping(StateMapping stateMapping) {
2157         checkMutable(true);
2158         this.stateMapping = stateMapping;
2159     }
2160 
2161     /**
2162      * Returns the general context that is available before the apply model phase (during the
2163      * initialize phase)
2164      *
2165      * @return context map
2166      */
2167     public Map<String, Object> getPreModelContext() {
2168         if (preModelContext == null) {
2169             Map<String, Object> context = new HashMap<String, Object>();
2170 
2171             context.put(UifConstants.ContextVariableNames.VIEW, this);
2172             context.put(UifConstants.ContextVariableNames.VIEW_HELPER, viewHelperService);
2173 
2174             ViewTheme theme = getTheme();
2175             if (theme != null) {
2176                 context.put(UifConstants.ContextVariableNames.THEME_IMAGES, theme.getImageDirectory());
2177             }
2178 
2179             Map<String, String> properties = CoreApiServiceLocator.getKualiConfigurationService().getAllProperties();
2180             context.put(UifConstants.ContextVariableNames.CONFIG_PROPERTIES, properties);
2181             context.put(UifConstants.ContextVariableNames.CONSTANTS, KRADConstants.class);
2182             context.put(UifConstants.ContextVariableNames.UIF_CONSTANTS, UifConstants.class);
2183             context.put(UifConstants.ContextVariableNames.USER_SESSION, GlobalVariables.getUserSession());
2184 
2185             preModelContext = Collections.unmodifiableMap(context);
2186         }
2187 
2188         return preModelContext;
2189     }
2190 
2191     /**
2192      * @see org.kuali.rice.krad.uif.component.ComponentBase#clone()
2193      */
2194     @Override
2195     public View clone() throws CloneNotSupportedException {
2196         View viewCopy = (View) super.clone();
2197         viewCopy.viewIndex = new ViewIndex();
2198         return viewCopy;
2199     }
2200 
2201     /**
2202      * {@inheritDoc}
2203      */
2204     @Override
2205     public void completeValidation(ValidationTrace tracer) {
2206         tracer.addBean(this);
2207 
2208         // Check for the presence of a valid item with an not-null EntryPageId
2209         boolean validPageId = false;
2210         if (getEntryPageId() != null) {
2211             for (int i = 0; i < getItems().size(); i++) {
2212                 if (getEntryPageId().compareTo(getItems().get(i).getId()) == 0) {
2213                     validPageId = true;
2214                 }
2215             }
2216         } else {
2217             validPageId = true;
2218         }
2219         if (!validPageId) {
2220             String currentValues[] = {"entryPageId = " + getEntryPageId()};
2221             tracer.createError("Items must contain an item with a matching id to entryPageId", currentValues);
2222         }
2223 
2224         // Check to insure the view as not already been set
2225         if (tracer.getValidationStage() == ValidationTrace.START_UP) {
2226             if (getViewStatus().compareTo(ViewStatus.CREATED) != 0) {
2227                 String currentValues[] = {"viewStatus = " + getViewStatus()};
2228                 tracer.createError("ViewStatus should not be set", currentValues);
2229             }
2230         }
2231 
2232         // Check to insure the binding object path is a valid property
2233         boolean validDefaultBindingObjectPath = false;
2234         if (getDefaultBindingObjectPath() == null) {
2235             validDefaultBindingObjectPath = true;
2236         } else if (DataDictionary.isPropertyOf(getFormClass(), getDefaultBindingObjectPath())) {
2237             validDefaultBindingObjectPath = true;
2238         }
2239         if (!validDefaultBindingObjectPath) {
2240             String currentValues[] =
2241                     {"formClass = " + getFormClass(), "defaultBindingPath = " + getDefaultBindingObjectPath()};
2242             tracer.createError("DefaultBingdingObjectPath must be a valid property of the formClass", currentValues);
2243         }
2244 
2245         // Check to insure the page is set if the view is a single page
2246         if (isSinglePageView()) {
2247             if (getPage() == null) {
2248                 String currentValues[] = {"singlePageView = " + isSinglePageView(), "page = " + getPage()};
2249                 tracer.createError("Page must be set if singlePageView is true", currentValues);
2250             }
2251             for (int i = 0; i < getItems().size(); i++) {
2252                 if (getItems().get(i).getClass() == PageGroup.class) {
2253                     String currentValues[] =
2254                             {"singlePageView = " + isSinglePageView(), "items(" + i + ") = " + getItems().get(i)
2255                                     .getClass()};
2256                     tracer.createError("Items cannot be pageGroups if singlePageView is true", currentValues);
2257                 }
2258             }
2259         }
2260 
2261         // Checks to insure the Growls are set if growl messaging is enabled
2262         if (isGrowlMessagingEnabled() == true && getGrowls() == null) {
2263             if (Validator.checkExpressions(this, "growls")) {
2264                 String currentValues[] =
2265                         {"growlMessagingEnabled = " + isGrowlMessagingEnabled(), "growls = " + getGrowls()};
2266                 tracer.createError("Growls cannot be null if Growl Messaging is enabled", currentValues);
2267             }
2268         }
2269 
2270         // Checks that there are items present if the view is not a single page
2271         if (!isSinglePageView()) {
2272             if (getItems().size() == 0) {
2273                 String currentValues[] =
2274                         {"singlePageView = " + isSinglePageView(), "items.size = " + getItems().size()};
2275                 tracer.createWarning("Items cannot be empty if singlePageView is false", currentValues);
2276             } else {
2277                 for (int i = 0; i < getItems().size(); i++) {
2278                     if (getItems().get(i).getClass() != PageGroup.class) {
2279                         String currentValues[] =
2280                                 {"singlePageView = " + isSinglePageView(), "items(" + i + ") = " + getItems().get(i)
2281                                         .getClass()};
2282                         tracer.createError("Items must be pageGroups if singlePageView is false", currentValues);
2283                     }
2284                 }
2285             }
2286         }
2287         super.completeValidation(tracer.getCopy());
2288     }
2289 
2290 }