View Javadoc

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