View Javadoc

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