001    /**
002     * Copyright 2005-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.uif.view;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.krad.datadictionary.DataDictionary;
020    import org.kuali.rice.krad.datadictionary.parse.BeanTag;
021    import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
022    import org.kuali.rice.krad.datadictionary.parse.BeanTags;
023    import org.kuali.rice.krad.datadictionary.state.StateMapping;
024    import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
025    import org.kuali.rice.krad.datadictionary.validator.Validator;
026    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
027    import org.kuali.rice.krad.uif.UifConstants;
028    import org.kuali.rice.krad.uif.UifConstants.ViewStatus;
029    import org.kuali.rice.krad.uif.UifConstants.ViewType;
030    import org.kuali.rice.krad.uif.component.Component;
031    import org.kuali.rice.krad.uif.component.ReferenceCopy;
032    import org.kuali.rice.krad.uif.component.RequestParameter;
033    import org.kuali.rice.krad.uif.container.Container;
034    import org.kuali.rice.krad.uif.container.ContainerBase;
035    import org.kuali.rice.krad.uif.container.Group;
036    import org.kuali.rice.krad.uif.container.NavigationGroup;
037    import org.kuali.rice.krad.uif.container.PageGroup;
038    import org.kuali.rice.krad.uif.element.Header;
039    import org.kuali.rice.krad.uif.element.Link;
040    import org.kuali.rice.krad.uif.layout.LayoutManager;
041    import org.kuali.rice.krad.uif.service.ViewHelperService;
042    import org.kuali.rice.krad.uif.util.BooleanMap;
043    import org.kuali.rice.krad.uif.util.ClientValidationUtils;
044    import org.kuali.rice.krad.uif.widget.BlockUI;
045    import org.kuali.rice.krad.uif.widget.BreadCrumbs;
046    import org.kuali.rice.krad.uif.widget.Growls;
047    import org.kuali.rice.krad.util.ObjectUtils;
048    import org.kuali.rice.krad.web.form.UifFormBase;
049    
050    import java.util.ArrayList;
051    import java.util.HashMap;
052    import java.util.HashSet;
053    import java.util.List;
054    import java.util.Map;
055    import java.util.Set;
056    
057    /**
058     * Root of the component tree which encompasses a set of related
059     * <code>GroupContainer</code> instances tied together with a common page layout
060     * and navigation.
061     *
062     * <p>
063     * The <code>View</code> component ties together all the components and
064     * configuration of the User Interface for a piece of functionality. In Rice
065     * applications the view is typically associated with a <code>Document</code>
066     * instance.
067     * </p>
068     *
069     * <p>
070     * The view template lays out the common header, footer, and navigation for the
071     * related pages. In addition the view renders the HTML head element bringing in
072     * common script files and style sheets, along with optionally rendering a form
073     * element for pages that need to post data back to the server.
074     * </p>
075     *
076     * <p>
077     * Configuration of UIF features such as model validation is also done through
078     * the <code>View</code>
079     * </p>
080     *
081     * @author Kuali Rice Team (rice.collab@kuali.org)
082     */
083    @BeanTags({@BeanTag(name = "view-bean", parent = "Uif-View"), @BeanTag(name = "view-knsTheme-bean", parent = "Uif-View-KnsTheme")
084    })
085    public class View extends ContainerBase {
086        private static final long serialVersionUID = -1220009725554576953L;
087    
088        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(View.class);
089    
090        private String namespaceCode;
091        private String viewName;
092        private ViewTheme theme;
093    
094        private int idSequence;
095    
096        private String stateObjectBindingPath;
097        private StateMapping stateMapping;
098    
099        // application
100        private Header applicationHeader;
101        private Group applicationFooter;
102    
103        // Breadcrumbs
104        private BreadCrumbs breadcrumbs;
105        private String breadcrumbTitlePropertyName;
106        private String breadcrumbTitleDisplayOption;
107    
108        private boolean renderBreadcrumbsInView;
109    
110        // Growls support
111        private Growls growls;
112        private boolean growlMessagingEnabled;
113    
114        private BlockUI refreshBlockUI;
115        private BlockUI navigationBlockUI;
116    
117        private String entryPageId;
118    
119        @RequestParameter
120        private String currentPageId;
121    
122        private Group navigation;
123    
124        private Class<?> formClass;
125        private String defaultBindingObjectPath;
126        private Map<String, Class<?>> objectPathToConcreteClassMapping;
127    
128        private List<String> additionalScriptFiles;
129        private List<String> additionalCssFiles;
130    
131        private ViewType viewTypeName;
132    
133        private String viewStatus;
134        private ViewIndex viewIndex;
135        private Map<String, String> viewRequestParameters;
136    
137        private boolean persistFormToSession;
138    
139        private ViewPresentationController presentationController;
140        private ViewAuthorizer authorizer;
141    
142        private BooleanMap actionFlags;
143        private BooleanMap editModes;
144    
145        private Map<String, String> expressionVariables;
146    
147        private boolean singlePageView;
148        private boolean mergeWithPageItems;
149        private PageGroup page;
150    
151        private List<? extends Group> items;
152        private List<? extends Group> dialogs;
153    
154        private Link viewMenuLink;
155        private String viewMenuGroupName;
156    
157        private boolean applyDirtyCheck;
158        private boolean translateCodesOnReadOnlyDisplay;
159        private boolean supportsRequestOverrideOfReadOnlyFields;
160    
161        private String preLoadScript;
162    
163        private int preloadPoolSize;
164    
165        private List<String> viewTemplates;
166    
167        private Class<? extends ViewHelperService> viewHelperServiceClass;
168    
169        @ReferenceCopy
170        private ViewHelperService viewHelperService;
171    
172        public View() {
173            singlePageView = false;
174            mergeWithPageItems = true;
175            translateCodesOnReadOnlyDisplay = false;
176            viewTypeName = ViewType.DEFAULT;
177            viewStatus = UifConstants.ViewStatus.CREATED;
178            formClass = UifFormBase.class;
179            renderBreadcrumbsInView = true;
180            supportsRequestOverrideOfReadOnlyFields = true;
181            persistFormToSession = true;
182    
183            idSequence = 0;
184            this.viewIndex = new ViewIndex();
185            preloadPoolSize = 0;
186    
187            additionalScriptFiles = new ArrayList<String>();
188            additionalCssFiles = new ArrayList<String>();
189            items = new ArrayList<Group>();
190            objectPathToConcreteClassMapping = new HashMap<String, Class<?>>();
191            viewRequestParameters = new HashMap<String, String>();
192            expressionVariables = new HashMap<String, String>();
193    
194            dialogs = new ArrayList<Group>();
195            viewTemplates = new ArrayList<String>();
196        }
197    
198        /**
199         * The following initialization is performed:
200         *
201         * <ul>
202         * <li>If a single paged view, set items in page group and put the page in
203         * the items list</li>
204         * </ul>
205         *
206         * @see org.kuali.rice.krad.uif.container.ContainerBase#performInitialization(View, java.lang.Object)
207         */
208        @SuppressWarnings("unchecked")
209        @Override
210        public void performInitialization(View view, Object model) {
211            super.performInitialization(view, model);
212    
213            // populate items on page for single paged view
214            if (singlePageView) {
215                if (page != null) {
216                    // remove default sections of page when requested
217                    if (!mergeWithPageItems) {
218                        page.setItems(new ArrayList<Group>());
219                    }
220    
221                    view.assignComponentIds(page);
222    
223                    // add the items configured on the view to the page items, and set as the
224                    // new page items
225                    List<Component> newItems = (List<Component>) page.getItems();
226                    newItems.addAll(items);
227                    page.setItems(newItems);
228    
229                    // reset the items list to include the one page
230                    items = new ArrayList<Group>();
231                    ((List<Group>) items).add(page);
232                } else {
233                    throw new RuntimeException("For single paged views the page Group must be set.");
234                }
235            }
236    
237            // make sure all the pages have ids before selecting the current page
238            for (Group group : this.getItems()) {
239                if (StringUtils.isBlank(group.getId())) {
240                    group.setId(view.getNextId());
241                }
242            }
243        }
244    
245        /**
246         * The following updates are done here:
247         *
248         * <ul>
249         * <li>Invoke expression evaluation on view theme</li>
250         * </ul>
251         */
252        public void performApplyModel(View view, Object model, Component parent) {
253            super.performApplyModel(view, model, parent);
254    
255            if (theme != null) {
256                KRADServiceLocatorWeb.getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, theme, model,
257                        getContext());
258            }
259        }
260    
261        /**
262         * The following is performed:
263         *
264         * <ul>
265         * <li>Adds to its document ready script the setupValidator js function for setting
266         * up the validator for this view</li>
267         * </ul>
268         *
269         * @see org.kuali.rice.krad.uif.container.ContainerBase#performFinalize(View,
270         *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
271         */
272        @Override
273        public void performFinalize(View view, Object model, Component parent) {
274            super.performFinalize(view, model, parent);
275    
276            String preLoadScript = "";
277            if (this.getPreLoadScript() != null) {
278                preLoadScript = this.getPreLoadScript();
279            }
280    
281            // Retrieve Growl and BlockUI settings
282            Growls gw = view.getGrowls();
283            if (!gw.getTemplateOptions().isEmpty()) {
284                preLoadScript += "setGrowlDefaults(" + gw.getTemplateOptionsJSString() + ");";
285            }
286    
287            BlockUI navBlockUI = view.getNavigationBlockUI();
288            if (!navBlockUI.getTemplateOptions().isEmpty()) {
289                preLoadScript += "setBlockUIDefaults("
290                        + navBlockUI.getTemplateOptionsJSString()
291                        + ", '"
292                        + UifConstants.BLOCKUI_NAVOPTS
293                        + "');";
294            }
295    
296            BlockUI refBlockUI = view.getRefreshBlockUI();
297            if (!refBlockUI.getTemplateOptions().isEmpty()) {
298                preLoadScript += "setBlockUIDefaults("
299                        + refBlockUI.getTemplateOptionsJSString()
300                        + ", '"
301                        + UifConstants.BLOCKUI_REFRESHOPTS
302                        + "');";
303            }
304    
305            this.setPreLoadScript(preLoadScript);
306    
307            String onReadyScript = "";
308            if (this.getOnDocumentReadyScript() != null) {
309                onReadyScript = this.getOnDocumentReadyScript();
310            }
311    
312            this.setOnDocumentReadyScript(onReadyScript + "jQuery.extend(jQuery.validator.messages, " +
313                    ClientValidationUtils.generateValidatorMessagesOption() + ");");
314        }
315    
316        /**
317         * Assigns an id to the component if one was not configured
318         *
319         * @param component - component instance to assign id to
320         */
321        public void assignComponentIds(Component component) {
322            if (component == null) {
323                return;
324            }
325    
326            Integer currentSequenceVal = idSequence;
327    
328            // assign ID if necessary
329            if (StringUtils.isBlank(component.getId())) {
330                component.setId(UifConstants.COMPONENT_ID_PREFIX + getNextId());
331            }
332    
333            // capture current sequence value for component refreshes
334            getViewIndex().addSequenceValueToSnapshot(component.getId(), currentSequenceVal);
335    
336            if (component instanceof Container) {
337                LayoutManager layoutManager = ((Container) component).getLayoutManager();
338                if (layoutManager != null) {
339                    if (StringUtils.isBlank(layoutManager.getId())) {
340                        layoutManager.setId(UifConstants.COMPONENT_ID_PREFIX + getNextId());
341                    }
342                }
343            }
344    
345            // assign id to nested components
346            List<Component> allNested = new ArrayList<Component>(component.getComponentsForLifecycle());
347            allNested.addAll(component.getComponentPrototypes());
348            for (Component nestedComponent : allNested) {
349                assignComponentIds(nestedComponent);
350            }
351        }
352    
353        /**
354         * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
355         */
356        @Override
357        public List<Component> getComponentsForLifecycle() {
358            List<Component> components = new ArrayList<Component>();
359    
360            components.add(applicationHeader);
361            components.add(applicationFooter);
362            components.add(navigation);
363            components.add(breadcrumbs);
364            components.add(growls);
365            components.addAll(dialogs);
366            components.add(viewMenuLink);
367            components.add(navigationBlockUI);
368            components.add(refreshBlockUI);
369    
370            // Note super items should be added after navigation and other view components so
371            // conflicting ids between nav and page do not occur on page navigation via ajax
372            components.addAll(super.getComponentsForLifecycle());
373    
374            // remove all pages that are not the current page
375            if (!singlePageView) {
376                for (Group group : this.getItems()) {
377                    if ((group instanceof PageGroup) && !StringUtils.equals(group.getId(), getCurrentPageId()) && components
378                            .contains(group)) {
379                        components.remove(group);
380                    }
381                }
382            }
383    
384            return components;
385        }
386    
387        /**
388         * @see org.kuali.rice.krad.uif.container.Container#getSupportedComponents()
389         */
390        @Override
391        public Set<Class<? extends Component>> getSupportedComponents() {
392            Set<Class<? extends Component>> supportedComponents = new HashSet<Class<? extends Component>>();
393            supportedComponents.add(Group.class);
394    
395            return supportedComponents;
396        }
397    
398        /**
399         * @see org.kuali.rice.krad.uif.component.Component#getComponentTypeName()
400         */
401        @Override
402        public String getComponentTypeName() {
403            return "view";
404        }
405    
406        /**
407         * Iterates through the contained page items and returns the Page that
408         * matches the set current page id
409         *
410         * @return Page instance
411         */
412        public PageGroup getCurrentPage() {
413            for (Group pageGroup : this.getItems()) {
414                if (StringUtils.equals(pageGroup.getId(), getCurrentPageId()) && pageGroup instanceof PageGroup) {
415                    return (PageGroup) pageGroup;
416                }
417            }
418    
419            return null;
420        }
421    
422        /**
423         * Override sort method to prevent sorting in the case of a single page view, since the items
424         * will get pushed into the configured page and sorted through the page
425         */
426        @Override
427        protected void sortItems(View view, Object model) {
428            if (!singlePageView) {
429                super.sortItems(view, model);
430            }
431        }
432    
433        /**
434         * Namespace code the view should be associated with
435         *
436         * <p>
437         * The namespace code is used within the framework in such places as permission checks and parameter
438         * retrieval
439         * </p>
440         *
441         * @return String namespace code
442         */
443        @BeanTagAttribute(name = "namespaceCode")
444        public String getNamespaceCode() {
445            return namespaceCode;
446        }
447    
448        /**
449         * Setter for the view's namespace code
450         *
451         * @param namespaceCode
452         */
453        public void setNamespaceCode(String namespaceCode) {
454            this.namespaceCode = namespaceCode;
455        }
456    
457        /**
458         * View name provides an identifier for a view within a type. That is if a
459         * set of <code>View</code> instances have the same values for the
460         * properties that are used to retrieve them by their type, the name can be
461         * given to further qualify the view that should be retrieved.
462         * <p>
463         * A view type like the <code>LookupView</code> might have several views for
464         * the same object class, but one that is the 'default' lookup and another
465         * that is the 'advanced' lookup. Therefore the name on the first could be
466         * set to 'default', and likewise the name for the second 'advanced'.
467         * </p>
468         *
469         * @return String name of view
470         */
471        @BeanTagAttribute(name = "viewName")
472        public String getViewName() {
473            return this.viewName;
474        }
475    
476        /**
477         * Setter for the view's name
478         *
479         * @param viewName
480         */
481        public void setViewName(String viewName) {
482            this.viewName = viewName;
483        }
484    
485        /**
486         * Header for the application containing the view
487         *
488         * <p>
489         * When deploying outside a portal, the application header and footer property can be configured to
490         * display a consistent header/footer across all views. Here application logos, menus, login controls
491         * and so on can be rendered.
492         * </p>
493         *
494         * @return HeaderField application header
495         */
496        @BeanTagAttribute(name = "applicationHeader", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
497        public Header getApplicationHeader() {
498            return applicationHeader;
499        }
500    
501        /**
502         * Setter for the application header
503         *
504         * @param applicationHeader
505         */
506        public void setApplicationHeader(Header applicationHeader) {
507            this.applicationHeader = applicationHeader;
508        }
509    
510        /**
511         * Footer for the application containing the view
512         *
513         * <p>
514         * When deploying outside a portal, the application header and footer property can be configured to
515         * display a consistent header/footer across all views. Here such things as application links, copyrights
516         * and so on can be rendered.
517         * </p>
518         *
519         * @return Group application footer
520         */
521        @BeanTagAttribute(name = "applicationFooter", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
522        public Group getApplicationFooter() {
523            return applicationFooter;
524        }
525    
526        /**
527         * Setter for the application footer
528         *
529         * @param applicationFooter
530         */
531        public void setApplicationFooter(Group applicationFooter) {
532            this.applicationFooter = applicationFooter;
533        }
534    
535        /**
536         * Current sequence value for id assignment
537         *
538         * @return int id sequence
539         */
540        public int getIdSequence() {
541            return idSequence;
542        }
543    
544        /**
545         * Setter for the current id sequence value
546         *
547         * @param idSequence
548         */
549        public void setIdSequence(int idSequence) {
550            this.idSequence = idSequence;
551        }
552    
553        /**
554         * Returns the next unique id available for components within the view instance
555         *
556         * @return String next id available
557         */
558        public String getNextId() {
559            idSequence += 1;
560            return Integer.toString(idSequence);
561        }
562    
563        /**
564         * Specifies what page should be rendered by default. This is the page that
565         * will be rendered when the <code>View</code> is first rendered or when the
566         * current page is not set
567         *
568         * @return String id of the page to render by default
569         */
570        @BeanTagAttribute(name = "entryPageId")
571        public String getEntryPageId() {
572            return this.entryPageId;
573        }
574    
575        /**
576         * Setter for default Page id
577         *
578         * @param entryPageId
579         */
580        public void setEntryPageId(String entryPageId) {
581            this.entryPageId = entryPageId;
582        }
583    
584        /**
585         * The id for the page within the view that should be displayed in the UI.
586         * Other pages of the view will not be rendered
587         *
588         * <p>
589         * If current page id is not set, it is set to the configured entry page or first item in list id
590         * </p>
591         *
592         * @return String id of the page that should be displayed
593         */
594        public String getCurrentPageId() {
595            // default current page if not set
596            if (StringUtils.isBlank(currentPageId)) {
597                if (StringUtils.isNotBlank(entryPageId)) {
598                    currentPageId = entryPageId;
599                } else if ((getItems() != null) && !getItems().isEmpty()) {
600                    Group firstPageGroup = getItems().get(0);
601                    currentPageId = firstPageGroup.getId();
602                }
603            }
604    
605            return this.currentPageId;
606        }
607    
608        /**
609         * Setter for the page id to display
610         *
611         * @param currentPageId
612         */
613        public void setCurrentPageId(String currentPageId) {
614            this.currentPageId = currentPageId;
615        }
616    
617        /**
618         * <code>NavigationGroup</code> instance for the <code>View</code>
619         * <p>
620         * Provides configuration necessary to render the navigation. This includes
621         * navigation items in addition to configuration for the navigation
622         * renderer.
623         * </p>
624         *
625         * @return NavigationGroup
626         */
627        @BeanTagAttribute(name = "navigation", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
628        public Group getNavigation() {
629            return this.navigation;
630        }
631    
632        /**
633         * Setter for the View's <code>NavigationGroup</code>
634         *
635         * @param navigation
636         */
637        public void setNavigation(Group navigation) {
638            this.navigation = navigation;
639        }
640    
641        /**
642         * Class of the Form that should be used with the <code>View</code>
643         * instance. The form is the top level object for all the view's data and is
644         * used to present and accept data in the user interface. All form classes
645         * should extend UifFormBase
646         *
647         * @return Class<?> class for the view's form
648         * @see org.kuali.rice.krad.web.form.UifFormBase
649         */
650        @BeanTagAttribute(name = "formClass")
651        public Class<?> getFormClass() {
652            return this.formClass;
653        }
654    
655        /**
656         * Setter for the form class
657         *
658         * @param formClass
659         */
660        public void setFormClass(Class<?> formClass) {
661            this.formClass = formClass;
662        }
663    
664        /**
665         * For <code>View</code> types that work primarily with one nested object of
666         * the form (for instance document, or bo) the default binding object path
667         * can be set for each of the views <code>DataBinding</code> components. If
668         * the component does not set its own binding object path it will inherit
669         * the default
670         *
671         * @return String binding path to the object from the form
672         */
673        @BeanTagAttribute(name = "defaultObjectPath")
674        public String getDefaultBindingObjectPath() {
675            return this.defaultBindingObjectPath;
676        }
677    
678        /**
679         * Setter for the default binding object path to use for the view
680         *
681         * @param defaultBindingObjectPath
682         */
683        public void setDefaultBindingObjectPath(String defaultBindingObjectPath) {
684            this.defaultBindingObjectPath = defaultBindingObjectPath;
685        }
686    
687        /**
688         * Configures the concrete classes that will be used for properties in the
689         * form object graph that have an abstract or interface type
690         *
691         * <p>
692         * For properties that have an abstract or interface type, it is not
693         * possible to perform operations like getting/settings property values and
694         * getting information from the dictionary. When these properties are
695         * encountered in the object graph, this <code>Map</code> will be consulted
696         * to determine the concrete type to use.
697         * </p>
698         *
699         * <p>
700         * e.g. Suppose we have a property document.accountingLine.accountNumber and
701         * the accountingLine property on the document instance has an interface
702         * type 'AccountingLine'. We can then put an entry into this map with key
703         * 'document.accountingLine', and value
704         * 'org.kuali.rice.sampleapp.TravelAccountingLine'. When getting the
705         * property type or an entry from the dictionary for accountNumber, the
706         * TravelAccountingLine class will be used.
707         * </p>
708         *
709         * @return Map<String, Class> of class implementations keyed by path
710         */
711        @BeanTagAttribute(name = "objectPathConcreteClassMapping", type = BeanTagAttribute.AttributeType.MAPVALUE)
712        public Map<String, Class<?>> getObjectPathToConcreteClassMapping() {
713            return this.objectPathToConcreteClassMapping;
714        }
715    
716        /**
717         * Setter for the Map of class implementations keyed by path
718         *
719         * @param objectPathToConcreteClassMapping
720         */
721        public void setObjectPathToConcreteClassMapping(Map<String, Class<?>> objectPathToConcreteClassMapping) {
722            this.objectPathToConcreteClassMapping = objectPathToConcreteClassMapping;
723        }
724    
725        /**
726         * Declares additional script files that should be included with the
727         * <code>View</code>. These files are brought into the HTML page along with
728         * common script files configured for the Rice application. Each entry
729         * contain the path to the CSS file, either a relative path, path from web
730         * root, or full URI
731         * <p>
732         * e.g. '/krad/scripts/myScript.js', '../scripts/myScript.js',
733         * 'http://my.edu/web/myScript.js'
734         * </p>
735         *
736         * @return List<String> script file locations
737         */
738        @BeanTagAttribute(name = "additionalScriptFiles", type = BeanTagAttribute.AttributeType.LISTVALUE)
739        public List<String> getAdditionalScriptFiles() {
740            return this.additionalScriptFiles;
741        }
742    
743        /**
744         * Setter for the List of additional script files to included with the
745         * <code>View</code>
746         *
747         * @param additionalScriptFiles
748         */
749        public void setAdditionalScriptFiles(List<String> additionalScriptFiles) {
750            this.additionalScriptFiles = additionalScriptFiles;
751        }
752    
753        /**
754         * Declares additional CSS files that should be included with the
755         * <code>View</code>. These files are brought into the HTML page along with
756         * common CSS files configured for the Rice application. Each entry should
757         * contain the path to the CSS file, either a relative path, path from web
758         * root, or full URI
759         * <p>
760         * e.g. '/krad/css/stacked-view.css', '../css/stacked-view.css',
761         * 'http://my.edu/web/stacked-view.css'
762         * </p>
763         *
764         * @return List<String> CSS file locations
765         */
766        @BeanTagAttribute(name = "additionalCssFiles", type = BeanTagAttribute.AttributeType.LISTVALUE)
767        public List<String> getAdditionalCssFiles() {
768            return this.additionalCssFiles;
769        }
770    
771        /**
772         * Setter for the List of additional CSS files to included with the
773         * <code>View</code>
774         *
775         * @param additionalCssFiles
776         */
777        public void setAdditionalCssFiles(List<String> additionalCssFiles) {
778            this.additionalCssFiles = additionalCssFiles;
779        }
780    
781        /**
782         * Specifies the size of the pool that will contain pre-loaded views
783         *
784         * <p>
785         * The spring loading of some views can take a few seconds which hurts performance. The framework supports
786         * pre-loading of view instances so they are available right away when a request is made. This property configures
787         * how many view instances will be pre-loaded. A value of 0 (the default) means no view instances will be
788         * pre-loaded
789         * </p>
790         *
791         * @return int number of view instances to pre-load
792         */
793        @BeanTagAttribute(name = "preloadPoolSize")
794        public int getPreloadPoolSize() {
795            return preloadPoolSize;
796        }
797    
798        /**
799         * Setter for the preloaded view pool size
800         *
801         * @param preloadPoolSize
802         */
803        public void setPreloadPoolSize(int preloadPoolSize) {
804            this.preloadPoolSize = preloadPoolSize;
805        }
806    
807        /**
808         * List of templates that are used to render the view
809         *
810         * <p>
811         * This list will be populated by unique template names as the components of the view are being processed.
812         * Additional templates can be added in the view configuration if desired. At the beginning of the the view
813         * rendering, each template in the list will then be included or processed by the template language
814         * </p>
815         *
816         * <p>
817         * Note the user of this depends on the template language being used for rendering. Some languages might require
818         * including the template for each component instance (for example JSP templates). While others might simply
819         * include markup that is then available for rendering each component instance (for example FreeMarker which has
820         * a macro for each component that the template defines)
821         * </p>
822         *
823         * @return List<String> list of template names that should be included for rendering the view
824         */
825        public List<String> getViewTemplates() {
826            return viewTemplates;
827        }
828    
829        /**
830         * Setter for the the list of template names that should be included to render the view
831         *
832         * @param viewTemplates
833         */
834        public void setViewTemplates(List<String> viewTemplates) {
835            this.viewTemplates = viewTemplates;
836        }
837    
838        /**
839         * View type name the view is associated with the view instance
840         *
841         * <p>
842         * Views that share common features and functionality can be grouped by the
843         * view type. Usually view types extend the <code>View</code> class to
844         * provide additional configuration and to set defaults. View types can also
845         * implement the <code>ViewTypeService</code> to add special indexing and
846         * retrieval of views.
847         * </p>
848         *
849         * @return String view type name for the view
850         */
851        @BeanTagAttribute(name = "viewTypeName", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
852        public ViewType getViewTypeName() {
853            return this.viewTypeName;
854        }
855    
856        /**
857         * Setter for the view's type name
858         *
859         * @param viewTypeName
860         */
861        public void setViewTypeName(ViewType viewTypeName) {
862            this.viewTypeName = viewTypeName;
863        }
864    
865        /**
866         * Class name of the <code>ViewHelperService</code> that handles the various
867         * phases of the Views lifecycle
868         *
869         * @return Class for the spring bean
870         * @see org.kuali.rice.krad.uif.service.ViewHelperService
871         */
872        @BeanTagAttribute(name = "viewHelperServiceClass")
873        public Class<? extends ViewHelperService> getViewHelperServiceClass() {
874            return this.viewHelperServiceClass;
875        }
876    
877        /**
878         * Setter for the <code>ViewHelperService</code> class name
879         *
880         * @param viewHelperServiceClass
881         */
882        public void setViewHelperServiceClass(Class<? extends ViewHelperService> viewHelperServiceClass) {
883            this.viewHelperServiceClass = viewHelperServiceClass;
884        }
885    
886        /**
887         * Creates the <code>ViewHelperService</code> associated with the View
888         *
889         * @return ViewHelperService instance
890         */
891        @BeanTagAttribute(name = "viewHelperService", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
892        public ViewHelperService getViewHelperService() {
893            if ((this.viewHelperService == null) && (this.viewHelperServiceClass != null)) {
894                viewHelperService = ObjectUtils.newInstance(viewHelperServiceClass);
895            }
896    
897            return viewHelperService;
898        }
899    
900        /**
901         * Invoked to produce a ViewIndex of the current view's components
902         */
903        public void index() {
904            if (this.viewIndex == null) {
905                this.viewIndex = new ViewIndex();
906            }
907            this.viewIndex.index(this);
908        }
909    
910        /**
911         * Holds field indexes of the <code>View</code> instance for retrieval
912         *
913         * @return ViewIndex instance
914         */
915        @BeanTagAttribute(name = "viewIndex", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
916        public ViewIndex getViewIndex() {
917            return this.viewIndex;
918        }
919    
920        /**
921         * Map of parameters from the request that set view options, used to rebuild
922         * the view on each post
923         * <p>
924         * Views can be configured by parameters. These might impact which parts of
925         * the view are rendered or how the view behaves. Generally these would get
926         * passed in when a new view is requested (by request parameters). These
927         * will be used to initially populate the view properties. In addition, on a
928         * post the view will be rebuilt and properties reset again by the allow
929         * request parameters.
930         * </p>
931         * <p>
932         * Example parameter would be for MaintenaceView whether a New, Edit, or
933         * Copy was requested (maintenance mode)
934         * </p>
935         *
936         * @return
937         */
938        public Map<String, String> getViewRequestParameters() {
939            return this.viewRequestParameters;
940        }
941    
942        /**
943         * Setter for the view's request parameters map
944         *
945         * @param viewRequestParameters
946         */
947        public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
948            this.viewRequestParameters = viewRequestParameters;
949        }
950    
951        /**
952         * Indicates whether the form (model) associated with the view should be stored in the user session
953         *
954         * <p>
955         * The form class (or model) is used to hold the data that backs the view along with the built view object. Storing
956         * the form instance in session allows many things:
957         *
958         * <ul>
959         * <li>Data does not need to be rebuilt for each server request (for example a collection)</li>
960         * <li>Data that does not need to go to the user can remain on the form, reducing the size of the response and
961         * improving security</li>
962         * <li>Data can be keep around in a 'pre-save' state. When requested by the user changes can then be persisted to
963         * the database</li>
964         * <li>Certain information about the view that was rendered, such as input fields, collection paths, and refresh
965         * components can be kept on the form to support UI interaction</li>
966         * </ul>
967         *
968         * Setting this flag to false will prevent the form from being kept in session and as a result will limit what can
969         * be done by the framework. In almost all cases this is not recommended.
970         * </p>
971         *
972         * <p>
973         * Note all forms will be cleared when the user session expires (based on the rice configuration). In addition, the
974         * framework enables clear points on certain actions to remove the form when it is no longer needed
975         * </p>
976         *
977         * @return boolean true if the form should be stored in the user session, false if only request based
978         */
979        @BeanTagAttribute(name = "persistFormToSession")
980        public boolean isPersistFormToSession() {
981            return persistFormToSession;
982        }
983    
984        /**
985         * Setter for the persist form to session indicator
986         *
987         * @param persistFormToSession
988         */
989        public void setPersistFormToSession(boolean persistFormToSession) {
990            this.persistFormToSession = persistFormToSession;
991        }
992    
993        /**
994         * PresentationController that should be used for the <code>View</code> instance
995         *
996         * <p>
997         * The presentation controller is consulted to determine component (group,
998         * field) state such as required, read-only, and hidden. The presentation
999         * controller does not take into account user permissions. The presentation
1000         * controller can also output action flags and edit modes that will be set
1001         * onto the view instance and can be referred to by conditional expressions
1002         * </p>
1003         *
1004         * @return PresentationController
1005         */
1006        @BeanTagAttribute(name = "presentationController", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1007        public ViewPresentationController getPresentationController() {
1008            return this.presentationController;
1009        }
1010    
1011        /**
1012         * Setter for the view's presentation controller
1013         *
1014         * @param presentationController
1015         */
1016        public void setPresentationController(ViewPresentationController presentationController) {
1017            this.presentationController = presentationController;
1018        }
1019    
1020        /**
1021         * Setter for the view's presentation controller by class
1022         *
1023         * @param presentationControllerClass
1024         */
1025        public void setPresentationControllerClass(
1026                Class<? extends ViewPresentationController> presentationControllerClass) {
1027            this.presentationController = ObjectUtils.newInstance(presentationControllerClass);
1028        }
1029    
1030        /**
1031         * Authorizer that should be used for the <code>View</code> instance
1032         *
1033         * <p>
1034         * The authorizer class is consulted to determine component (group, field)
1035         * state such as required, read-only, and hidden based on the users
1036         * permissions. It typically communicates with the Kuali Identity Management
1037         * system to determine roles and permissions. It is used with the
1038         * presentation controller and dictionary conditional logic to determine the
1039         * final component state. The authorizer can also output action flags and
1040         * edit modes that will be set onto the view instance and can be referred to
1041         * by conditional expressions
1042         * </p>
1043         *
1044         * @return Authorizer
1045         */
1046        @BeanTagAttribute(name = "authorizer", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1047        public ViewAuthorizer getAuthorizer() {
1048            return this.authorizer;
1049        }
1050    
1051        /**
1052         * Setter for the view's authorizer
1053         *
1054         * @param authorizer
1055         */
1056        public void setAuthorizer(ViewAuthorizer authorizer) {
1057            this.authorizer = authorizer;
1058        }
1059    
1060        /**
1061         * Setter for the view's authorizer by class
1062         *
1063         * @param authorizerClass
1064         */
1065        public void setAuthorizerClass(Class<? extends ViewAuthorizer> authorizerClass) {
1066            this.authorizer = ObjectUtils.newInstance(authorizerClass);
1067        }
1068    
1069        /**
1070         * Map of strings that flag what actions can be taken in the UI
1071         * <p>
1072         * These can be used in conditional expressions in the dictionary or by
1073         * other UI logic
1074         * </p>
1075         *
1076         * @return BooleanMap action flags
1077         */
1078        @BeanTagAttribute(name = "actionFlags", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1079        public BooleanMap getActionFlags() {
1080            return this.actionFlags;
1081        }
1082    
1083        /**
1084         * Setter for the action flags Map
1085         *
1086         * @param actionFlags
1087         */
1088        public void setActionFlags(BooleanMap actionFlags) {
1089            this.actionFlags = actionFlags;
1090        }
1091    
1092        /**
1093         * Map of edit modes that enabled for the view
1094         * <p>
1095         * These can be used in conditional expressions in the dictionary or by
1096         * other UI logic
1097         * </p>
1098         *
1099         * @return BooleanMap edit modes
1100         */
1101        @BeanTagAttribute(name = "editModes", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1102        public BooleanMap getEditModes() {
1103            return this.editModes;
1104        }
1105    
1106        /**
1107         * Setter for the edit modes Map
1108         *
1109         * @param editModes
1110         */
1111        public void setEditModes(BooleanMap editModes) {
1112            this.editModes = editModes;
1113        }
1114    
1115        /**
1116         * Map that contains expressions to evaluate and make available as variables
1117         * for conditional expressions within the view
1118         * <p>
1119         * Each Map entry contains one expression variables, where the map key gives
1120         * the name for the variable, and the map value gives the variable
1121         * expression. The variables expressions will be evaluated before
1122         * conditional logic is run and made available as variables for other
1123         * conditional expressions. Variable expressions can be based on the model
1124         * and any object contained in the view's context
1125         * </p>
1126         *
1127         * @return Map<String, String> variable expressions
1128         */
1129        @BeanTagAttribute(name = "expressionVariables", type = BeanTagAttribute.AttributeType.MAPVALUE)
1130        public Map<String, String> getExpressionVariables() {
1131            return this.expressionVariables;
1132        }
1133    
1134        /**
1135         * Setter for the view's map of variable expressions
1136         *
1137         * @param expressionVariables
1138         */
1139        public void setExpressionVariables(Map<String, String> expressionVariables) {
1140            this.expressionVariables = expressionVariables;
1141        }
1142    
1143        /**
1144         * Indicates whether the <code>View</code> only has a single page
1145         * <code>Group</code> or contains multiple page <code>Group</code>
1146         * instances. In the case of a single page it is assumed the group's items
1147         * list contains the section groups for the page, and the page itself is
1148         * given by the page property ({@link #getPage()}. This is for convenience
1149         * of configuration and also can drive other configuration like styling.
1150         *
1151         * @return boolean true if the view only contains one page group, false if
1152         *         it contains multple pages
1153         */
1154        @BeanTagAttribute(name = "singlePageView")
1155        public boolean isSinglePageView() {
1156            return this.singlePageView;
1157        }
1158    
1159        /**
1160         * Setter for the single page indicator
1161         *
1162         * @param singlePageView
1163         */
1164        public void setSinglePageView(boolean singlePageView) {
1165            this.singlePageView = singlePageView;
1166        }
1167    
1168        /**
1169         * Indicates whether the default sections specified in the page items list
1170         * should be included for this view.  This only applies to single paged views.
1171         *
1172         * @return boolean true if the view should contain the default sections
1173         *         specified in the page
1174         */
1175        public boolean isMergeWithPageItems() {
1176            return mergeWithPageItems;
1177        }
1178    
1179        /**
1180         * Setter for the include page default sections indicator
1181         *
1182         * @param mergeWithPageItems
1183         */
1184        public void setMergeWithPageItems(boolean mergeWithPageItems) {
1185            this.mergeWithPageItems = mergeWithPageItems;
1186        }
1187    
1188        /**
1189         * For single paged views ({@link #isSinglePageView()}, gives the page
1190         * <code>Group</code> the view should render. The actual items for the page
1191         * is taken from the group's items list ({@link #getItems()}, and set onto
1192         * the give page group. This is for convenience of configuration.
1193         *
1194         * @return Group page group for single page views
1195         */
1196        @BeanTagAttribute(name = "page", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1197        public PageGroup getPage() {
1198            return this.page;
1199        }
1200    
1201        /**
1202         * Setter for the page group for single page views
1203         *
1204         * @param page
1205         */
1206        public void setPage(PageGroup page) {
1207            this.page = page;
1208        }
1209    
1210        /**
1211         * @see org.kuali.rice.krad.uif.container.ContainerBase#getItems()
1212         */
1213        @Override
1214        @BeanTagAttribute(name = "items", type = BeanTagAttribute.AttributeType.LISTBEAN)
1215        public List<? extends Group> getItems() {
1216            return this.items;
1217        }
1218    
1219        /**
1220         * Setter for the view's <code>Group</code> instances
1221         *
1222         * @param items
1223         */
1224        @Override
1225        public void setItems(List<? extends Component> items) {
1226            // TODO: fix this generic issue
1227            this.items = (List<? extends Group>) items;
1228        }
1229    
1230        /**
1231         * Provide a list of dialog groups associated with this view
1232         *
1233         * @return List of dialog Groups
1234         */
1235        @BeanTagAttribute(name = "dialogs", type = BeanTagAttribute.AttributeType.LISTBEAN)
1236        public List<? extends Group> getDialogs() {
1237            return dialogs;
1238        }
1239    
1240        /**
1241         * Sets the list of dialog groups for this view
1242         *
1243         * @param dialogs - List of dialog groups
1244         */
1245        public void setDialogs(List<? extends Group> dialogs) {
1246            this.dialogs = dialogs;
1247        }
1248    
1249        /**
1250         * Provides configuration for displaying a link to the view from an
1251         * application menu
1252         *
1253         * @return Link view link field
1254         */
1255        @BeanTagAttribute(name = "viewMenuLink", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1256        public Link getViewMenuLink() {
1257            return this.viewMenuLink;
1258        }
1259    
1260        /**
1261         * Setter for the views link field
1262         *
1263         * @param viewMenuLink
1264         */
1265        public void setViewMenuLink(Link viewMenuLink) {
1266            this.viewMenuLink = viewMenuLink;
1267        }
1268    
1269        /**
1270         * Provides a grouping string for the view to group its menu link (within a
1271         * portal for instance)
1272         *
1273         * @return String menu grouping
1274         */
1275        @BeanTagAttribute(name = "viewMenuGroupName")
1276        public String getViewMenuGroupName() {
1277            return this.viewMenuGroupName;
1278        }
1279    
1280        /**
1281         * Setter for the views menu grouping
1282         *
1283         * @param viewMenuGroupName
1284         */
1285        public void setViewMenuGroupName(String viewMenuGroupName) {
1286            this.viewMenuGroupName = viewMenuGroupName;
1287        }
1288    
1289        /**
1290         * Indicates what lifecycle phase the View instance is in
1291         * <p>
1292         * The view lifecycle begins with the CREATED status. In this status a new
1293         * instance of the view has been retrieved from the dictionary, but no
1294         * further processing has been done. After the initialize phase has been run
1295         * the status changes to INITIALIZED. After the model has been applied and
1296         * the view is ready for render the status changes to FINAL
1297         * </p>
1298         *
1299         * @return String view status
1300         * @see org.kuali.rice.krad.uif.UifConstants.ViewStatus
1301         */
1302        public String getViewStatus() {
1303            return this.viewStatus;
1304        }
1305    
1306        /**
1307         * Setter for the view status
1308         *
1309         * @param viewStatus
1310         */
1311        public void setViewStatus(String viewStatus) {
1312            this.viewStatus = viewStatus;
1313        }
1314    
1315        /**
1316         * Indicates whether the view has been initialized
1317         *
1318         * @return boolean true if the view has been initialized, false if not
1319         */
1320        public boolean isInitialized() {
1321            return StringUtils.equals(viewStatus, ViewStatus.INITIALIZED) || StringUtils.equals(viewStatus,
1322                    ViewStatus.FINAL);
1323        }
1324    
1325        /**
1326         * Indicates whether the view has been updated from the model and final
1327         * updates made
1328         *
1329         * @return boolean true if the view has been updated, false if not
1330         */
1331        public boolean isFinal() {
1332            return StringUtils.equals(viewStatus, ViewStatus.FINAL);
1333        }
1334    
1335        /**
1336         * Breadcrumb widget used for displaying homeward path and history
1337         *
1338         * @return the breadcrumbs
1339         */
1340        @BeanTagAttribute(name = "breadcrumbs", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1341        public BreadCrumbs getBreadcrumbs() {
1342            return this.breadcrumbs;
1343        }
1344    
1345        /**
1346         * @param breadcrumbs the breadcrumbs to set
1347         */
1348        public void setBreadcrumbs(BreadCrumbs breadcrumbs) {
1349            this.breadcrumbs = breadcrumbs;
1350        }
1351    
1352        /**
1353         * Indicates whether the breadcrumbs should be rendered in the view or if they have been rendered in
1354         * the application header
1355         *
1356         * <p>
1357         * For layout purposes it is sometimes necessary to render the breadcrumbs in the application header. This flag
1358         * indicates that is being done (by setting to false) and therefore should not be rendered in the view template.
1359         * </p>
1360         *
1361         * @return boolean true if breadcrumbs should be rendered in the view, false if not (are rendered in the
1362         *         application header)
1363         */
1364        @BeanTagAttribute(name = "renderBreadcrumbsInView")
1365        public boolean isRenderBreadcrumbsInView() {
1366            return renderBreadcrumbsInView;
1367        }
1368    
1369        /**
1370         * Setter for the render breadcrumbs in view indicator
1371         *
1372         * @param renderBreadcrumbsInView
1373         */
1374        public void setRenderBreadcrumbsInView(boolean renderBreadcrumbsInView) {
1375            this.renderBreadcrumbsInView = renderBreadcrumbsInView;
1376        }
1377    
1378        /**
1379         * Growls widget which sets up global settings for the growls used in this
1380         * view and its pages
1381         *
1382         * @return the growls
1383         */
1384        @BeanTagAttribute(name = "growls", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1385        public Growls getGrowls() {
1386            return this.growls;
1387        }
1388    
1389        /**
1390         * @param growls the growls to set
1391         */
1392        public void setGrowls(Growls growls) {
1393            this.growls = growls;
1394        }
1395    
1396        /**
1397         * Set the refresh BlockUI used with single element blocking
1398         * (such as ajax based element loading/updates)
1399         *
1400         * @param refreshBlockUI
1401         */
1402        public void setRefreshBlockUI(BlockUI refreshBlockUI) {
1403            this.refreshBlockUI = refreshBlockUI;
1404        }
1405    
1406        /**
1407         * @return BlockUI returns the refresh block object
1408         */
1409        @BeanTagAttribute(name = "refreshBlockUI", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1410        public BlockUI getRefreshBlockUI() {
1411            return refreshBlockUI;
1412        }
1413    
1414        /**
1415         * Set the navigation BlockUI used with single page blocking
1416         * (such as full page loading/saving)
1417         *
1418         * @param navigationBlockUI
1419         */
1420        public void setNavigationBlockUI(BlockUI navigationBlockUI) {
1421            this.navigationBlockUI = navigationBlockUI;
1422        }
1423    
1424        /**
1425         * @return BlockUI returns the navigation block object
1426         */
1427        @BeanTagAttribute(name = "navigationBlockUI", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1428        public BlockUI getNavigationBlockUI() {
1429            return navigationBlockUI;
1430        }
1431    
1432        /**
1433         * whether to use growls to show messages - info, warning and error
1434         *
1435         * <p>Growls use the messages contained in the message map. If enabled, info
1436         * messages in their entirety will be displayed in growls, for warning and
1437         * error messages a growl message will notify the user that these messages
1438         * exist on the page.</p>
1439         *
1440         * <p> If this setting is disabled, it is recommended that
1441         * infoMessage display be enabled for the page ValidationMessages bean in order to
1442         * display relevant information to the user. Note: the growl scripts are
1443         * built out in the PageGroup class.</p>
1444         *
1445         * @return the growlMessagingEnabled
1446         */
1447        @BeanTagAttribute(name = "growlMessagingEnabled")
1448        public boolean isGrowlMessagingEnabled() {
1449            return this.growlMessagingEnabled;
1450        }
1451    
1452        /**
1453         * enable or disable showing of messages using growls
1454         *
1455         * @param growlMessagingEnabled the growlMessagingEnabled to set
1456         */
1457        public void setGrowlMessagingEnabled(boolean growlMessagingEnabled) {
1458            this.growlMessagingEnabled = growlMessagingEnabled;
1459        }
1460    
1461        /**
1462         * Indicates whether the form should be validated for dirtyness
1463         *
1464         * <p>
1465         * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
1466         * InputFields will be validated on refresh, navigate, cancel or close Action or on form
1467         * unload and if dirty, displays a message and user can decide whether to continue with
1468         * the action or stay on the form. For lookup and inquiry, it's not needed to validate.
1469         * </p>
1470         *
1471         * @return true if dirty validation is set
1472         */
1473        @BeanTagAttribute(name = "applyDirtyCheck")
1474        public boolean isApplyDirtyCheck() {
1475            return this.applyDirtyCheck;
1476        }
1477    
1478        /**
1479         * Setter for dirty validation.
1480         */
1481        public void setApplyDirtyCheck(boolean applyDirtyCheck) {
1482            this.applyDirtyCheck = applyDirtyCheck;
1483        }
1484    
1485        /**
1486         * Indicates whether the Name of the Code should be displayed when a property is of type <code>KualiCode</code>
1487         *
1488         * @param translateCodesOnReadOnlyDisplay - indicates whether <code>KualiCode</code>'s name should be included
1489         */
1490        public void setTranslateCodesOnReadOnlyDisplay(boolean translateCodesOnReadOnlyDisplay) {
1491            this.translateCodesOnReadOnlyDisplay = translateCodesOnReadOnlyDisplay;
1492        }
1493    
1494        /**
1495         * Returns whether the current view supports displaying <code>KualiCode</code>'s name as additional display value
1496         *
1497         * @return true if the current view supports
1498         */
1499        @BeanTagAttribute(name = "translateCodesOnReadOnlyDisplay")
1500        public boolean isTranslateCodesOnReadOnlyDisplay() {
1501            return translateCodesOnReadOnlyDisplay;
1502        }
1503    
1504        /**
1505         * The property name to be used to determine what will be used in the
1506         * breadcrumb title of this view
1507         *
1508         * <p>
1509         * The title can be determined from a combination of this and viewLabelFieldbindingInfo: If only
1510         * viewLabelFieldPropertyName is set, the title we be determined against the
1511         * defaultBindingObjectPath. If only viewLabelFieldbindingInfo is set it
1512         * must provide information about what object(bindToForm or explicit) and
1513         * path to use. If both viewLabelFieldbindingInfo and viewLabelFieldPropertyName are set,
1514         * the bindingInfo will be used with a
1515         * the viewLabelFieldPropertyName as its bindingPath. If neither are set,
1516         * the default title attribute from the dataObject's metadata (determined by the
1517         * defaultBindingObjectPath's object) will be used.
1518         * </p>
1519         *
1520         * @return String property name whose value should be displayed in view label
1521         */
1522        @BeanTagAttribute(name = "breadcrumbTitlePropertyName")
1523        public String getBreadcrumbTitlePropertyName() {
1524            return this.breadcrumbTitlePropertyName;
1525        }
1526    
1527        /**
1528         * Setter for the view label property name
1529         *
1530         * @param breadcrumbTitlePropertyName the viewLabelFieldPropertyName to set
1531         */
1532        public void setBreadcrumbTitlePropertyName(String breadcrumbTitlePropertyName) {
1533            this.breadcrumbTitlePropertyName = breadcrumbTitlePropertyName;
1534        }
1535    
1536        /**
1537         * The option to use when appending the view label on the breadcrumb title.
1538         * Available options: 'dash', 'parenthesis', and 'replace'(don't append -
1539         * simply replace the title). MUST be set for the viewLabelField to be used
1540         * in the breadcrumb, if not set no appendage will be added.
1541         *
1542         * @return the appendOption
1543         */
1544        @BeanTagAttribute(name = "breadcrumbTitleDisplayOption")
1545        public String getBreadcrumbTitleDisplayOption() {
1546            return this.breadcrumbTitleDisplayOption;
1547        }
1548    
1549        /**
1550         * Setter for the append option
1551         *
1552         * @param breadcrumbTitleDisplayOption the appendOption to set
1553         */
1554        public void setBreadcrumbTitleDisplayOption(String breadcrumbTitleDisplayOption) {
1555            this.breadcrumbTitleDisplayOption = breadcrumbTitleDisplayOption;
1556        }
1557    
1558        /**
1559         * Indicates whether the view allows read only fields to be specified on the request URL which will
1560         * override the view setting
1561         *
1562         * <p>
1563         * If enabled, the readOnlyFields request parameter can be sent to indicate fields that should be set read only
1564         * </p>
1565         *
1566         * @return boolean true if read only request overrides are allowed, false if not
1567         */
1568        @BeanTagAttribute(name = "supportsRequestOverrideOfReadOnlyFields")
1569        public boolean isSupportsRequestOverrideOfReadOnlyFields() {
1570            return supportsRequestOverrideOfReadOnlyFields;
1571        }
1572    
1573        /**
1574         * Setter for the the read only field override indicator
1575         *
1576         * @param supportsRequestOverrideOfReadOnlyFields
1577         */
1578        public void setSupportsRequestOverrideOfReadOnlyFields(boolean supportsRequestOverrideOfReadOnlyFields) {
1579            this.supportsRequestOverrideOfReadOnlyFields = supportsRequestOverrideOfReadOnlyFields;
1580        }
1581    
1582        /**
1583         * Script that is executed at the beginning of page load (before any other script)
1584         *
1585         * <p>
1586         * Many used to set server variables client side
1587         * </p>
1588         *
1589         * @return String pre load script
1590         */
1591        @BeanTagAttribute(name = "preLoadScript")
1592        public String getPreLoadScript() {
1593            return preLoadScript;
1594        }
1595    
1596        /**
1597         * Setter for the pre load script
1598         *
1599         * @param preLoadScript
1600         */
1601        public void setPreLoadScript(String preLoadScript) {
1602            this.preLoadScript = preLoadScript;
1603        }
1604    
1605        /**
1606         * The theme which contains stylesheets for this view
1607         *
1608         * @return ViewTheme
1609         */
1610        @BeanTagAttribute(name = "theme", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1611        public ViewTheme getTheme() {
1612            return theme;
1613        }
1614    
1615        /**
1616         * Setter for The theme which contains stylesheets for this view
1617         *
1618         * @return
1619         */
1620        public void setTheme(ViewTheme theme) {
1621            this.theme = theme;
1622        }
1623    
1624        /**
1625         * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to
1626         * determine what field in the model state information is stored in for this view.  Used during View validation.
1627         *
1628         * @return stateObjectBindingPath path to the object storing state information
1629         */
1630        @BeanTagAttribute(name = "stateObjectBindingPath")
1631        public String getStateObjectBindingPath() {
1632            return stateObjectBindingPath;
1633        }
1634    
1635        /**
1636         * The stateObject's binding path, this will be used along with the StateMapping's statePropertyName to
1637         * determine what field in the model state information is stored in for this view.  Used during View validation.
1638         *
1639         * @param stateObjectBindingPath
1640         */
1641        public void setStateObjectBindingPath(String stateObjectBindingPath) {
1642            this.stateObjectBindingPath = stateObjectBindingPath;
1643        }
1644    
1645        /**
1646         * Gets the stateMapping.
1647         *
1648         * <p>The state mapping object is used to determine the state information for a view,
1649         * it must include an ordered list of states, and where to find the state information for the view.
1650         * A stateMapping must be set for state based validation to occur.  When stateMapping information is
1651         * not included, the view's model is considered stateless and all constraints will apply regardless of their
1652         * state information or replacements (ie, they will function as they did in version 2.1).</p>
1653         *
1654         * @return StateMapping information needed for state based validation, if null no state based validation
1655         *         functionality
1656         *         will exist and configured constraints will apply regardless of state
1657         * @since 2.2
1658         */
1659        @BeanTagAttribute(name = "stateMapping", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
1660        public StateMapping getStateMapping() {
1661            return stateMapping;
1662        }
1663    
1664        /**
1665         * Set the stateMapping
1666         *
1667         * @param stateMapping
1668         */
1669        public void setStateMapping(StateMapping stateMapping) {
1670            this.stateMapping = stateMapping;
1671        }
1672    
1673        /**
1674         * @see org.kuali.rice.krad.uif.component.Component#completeValidation
1675         */
1676        @Override
1677        public void completeValidation(ValidationTrace tracer) {
1678            tracer.addBean(this);
1679    
1680            // Check for the presence of a valid item with an not-null EntryPageId
1681            boolean validPageId = false;
1682            if (getEntryPageId() != null) {
1683                for (int i = 0; i < getItems().size(); i++) {
1684                    if (getEntryPageId().compareTo(getItems().get(i).getId()) == 0) {
1685                        validPageId = true;
1686                    }
1687                }
1688            } else {
1689                validPageId = true;
1690            }
1691            if (!validPageId) {
1692                String currentValues[] = {"entryPageId = " + getEntryPageId()};
1693                tracer.createError("Items must contain an item with a matching id to entryPageId", currentValues);
1694            }
1695    
1696            // Check to insure the view as not already been set
1697            if (tracer.getValidationStage() == ValidationTrace.START_UP) {
1698                if (getViewStatus().compareTo(UifConstants.ViewStatus.CREATED) != 0) {
1699                    String currentValues[] = {"viewStatus = " + getViewStatus()};
1700                    tracer.createError("ViewStatus should not be set", currentValues);
1701                }
1702            }
1703    
1704            // Check to insure the binding object path is a valid property
1705            boolean validDefaultBindingObjectPath = false;
1706            if (getDefaultBindingObjectPath() == null) {
1707                validDefaultBindingObjectPath = true;
1708            } else if (DataDictionary.isPropertyOf(getFormClass(), getDefaultBindingObjectPath())) {
1709                validDefaultBindingObjectPath = true;
1710            }
1711            if (!validDefaultBindingObjectPath) {
1712                String currentValues[] =
1713                        {"formClass = " + getFormClass(), "defaultBindingPath = " + getDefaultBindingObjectPath()};
1714                tracer.createError("DefaultBingdingObjectPath must be a valid property of the formClass", currentValues);
1715            }
1716    
1717            // Check to insure the page is set if the view is a single page
1718            if (isSinglePageView()) {
1719                if (getPage() == null) {
1720                    String currentValues[] = {"singlePageView = " + isSinglePageView(), "page = " + getPage()};
1721                    tracer.createError("Page must be set if singlePageView is true", currentValues);
1722                }
1723                for (int i = 0; i < getItems().size(); i++) {
1724                    if (getItems().get(i).getClass() == PageGroup.class) {
1725                        String currentValues[] =
1726                                {"singlePageView = " + isSinglePageView(), "items(" + i + ") = " + getItems().get(i)
1727                                        .getClass()};
1728                        tracer.createError("Items cannot be pageGroups if singlePageView is true", currentValues);
1729                    }
1730                }
1731            }
1732    
1733            // Checks to insure the Growls are set if growl messaging is enabled
1734            if (isGrowlMessagingEnabled() == true && getGrowls() == null) {
1735                if (Validator.checkExpressions(this, "growls")) {
1736                    String currentValues[] =
1737                            {"growlMessagingEnabled = " + isGrowlMessagingEnabled(), "growls = " + getGrowls()};
1738                    tracer.createError("Growls cannot be null if Growl Messaging is enabled", currentValues);
1739                }
1740            }
1741    
1742            // Checks that there are items present if the view is not a single page
1743            if (!isSinglePageView()) {
1744                if (getItems().size() == 0) {
1745                    String currentValues[] =
1746                            {"singlePageView = " + isSinglePageView(), "items.size = " + getItems().size()};
1747                    tracer.createWarning("Items cannot be empty if singlePageView is false", currentValues);
1748                } else {
1749                    for (int i = 0; i < getItems().size(); i++) {
1750                        if (getItems().get(i).getClass() != PageGroup.class) {
1751                            String currentValues[] =
1752                                    {"singlePageView = " + isSinglePageView(), "items(" + i + ") = " + getItems().get(i)
1753                                            .getClass()};
1754                            tracer.createError("Items must be pageGroups if singlePageView is false", currentValues);
1755                        }
1756                    }
1757                }
1758            }
1759            super.completeValidation(tracer.getCopy());
1760        }
1761    
1762    }