View Javadoc

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