View Javadoc

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