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