Coverage Report - org.kuali.rice.kns.uif.service.impl.ViewHelperServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ViewHelperServiceImpl
0%
0/304
0%
0/156
3.324
 
 1  
 /*
 2  
  * Copyright 2007 The Kuali Foundation Licensed under the Educational Community
 3  
  * License, Version 1.0 (the "License"); you may not use this file except in
 4  
  * compliance with the License. You may obtain a copy of the License at
 5  
  * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
 6  
  * or agreed to in writing, software distributed under the License is
 7  
  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 8  
  * KIND, either express or implied. See the License for the specific language
 9  
  * governing permissions and limitations under the License.
 10  
  */
 11  
 package org.kuali.rice.kns.uif.service.impl;
 12  
 
 13  
 import org.apache.commons.lang.StringUtils;
 14  
 import org.kuali.rice.kim.bo.Person;
 15  
 import org.kuali.rice.kns.datadictionary.AttributeDefinition;
 16  
 import org.kuali.rice.kns.inquiry.Inquirable;
 17  
 import org.kuali.rice.kns.lookup.valuefinder.ValueFinder;
 18  
 import org.kuali.rice.kns.service.DataDictionaryService;
 19  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 20  
 import org.kuali.rice.kns.service.KNSServiceLocatorWeb;
 21  
 import org.kuali.rice.kns.uif.UifConstants;
 22  
 import org.kuali.rice.kns.uif.authorization.Authorizer;
 23  
 import org.kuali.rice.kns.uif.authorization.PresentationController;
 24  
 import org.kuali.rice.kns.uif.container.CollectionGroup;
 25  
 import org.kuali.rice.kns.uif.container.Container;
 26  
 import org.kuali.rice.kns.uif.container.View;
 27  
 import org.kuali.rice.kns.uif.core.BindingInfo;
 28  
 import org.kuali.rice.kns.uif.core.Component;
 29  
 import org.kuali.rice.kns.uif.core.DataBinding;
 30  
 import org.kuali.rice.kns.uif.core.PropertyReplacer;
 31  
 import org.kuali.rice.kns.uif.core.RequestParameter;
 32  
 import org.kuali.rice.kns.uif.field.AttributeField;
 33  
 import org.kuali.rice.kns.uif.layout.LayoutManager;
 34  
 import org.kuali.rice.kns.uif.modifier.ComponentModifier;
 35  
 import org.kuali.rice.kns.uif.service.ExpressionEvaluatorService;
 36  
 import org.kuali.rice.kns.uif.service.ViewDictionaryService;
 37  
 import org.kuali.rice.kns.uif.service.ViewHelperService;
 38  
 import org.kuali.rice.kns.uif.util.BooleanMap;
 39  
 import org.kuali.rice.kns.uif.util.CloneUtils;
 40  
 import org.kuali.rice.kns.uif.util.ComponentFactory;
 41  
 import org.kuali.rice.kns.uif.util.ComponentUtils;
 42  
 import org.kuali.rice.kns.uif.util.ObjectPropertyUtils;
 43  
 import org.kuali.rice.kns.uif.util.ViewModelUtils;
 44  
 import org.kuali.rice.kns.uif.widget.Inquiry;
 45  
 import org.kuali.rice.kns.util.GlobalVariables;
 46  
 import org.kuali.rice.kns.util.KNSConstants;
 47  
 import org.kuali.rice.kns.util.ObjectUtils;
 48  
 import org.kuali.rice.kns.web.spring.form.UifFormBase;
 49  
 import org.springframework.util.MethodInvoker;
 50  
 
 51  
 import java.lang.reflect.Field;
 52  
 import java.util.Collection;
 53  
 import java.util.HashMap;
 54  
 import java.util.HashSet;
 55  
 import java.util.List;
 56  
 import java.util.Map;
 57  
 import java.util.Map.Entry;
 58  
 import java.util.Properties;
 59  
 import java.util.Set;
 60  
 
 61  
 /**
 62  
  * Default Implementation of <code>ViewHelperService</code>
 63  
  * 
 64  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 65  
  */
 66  0
 public class ViewHelperServiceImpl implements ViewHelperService {
 67  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ViewHelperServiceImpl.class);
 68  
 
 69  
     private transient DataDictionaryService dataDictionaryService;
 70  
     private transient ExpressionEvaluatorService expressionEvaluatorService;
 71  
     private transient ViewDictionaryService viewDictionaryService;
 72  
 
 73  
     /**
 74  
      * Uses reflection to find all fields defined on the <code>View</code>
 75  
      * instance that have the <code>RequestParameter</code> annotation (which
 76  
      * indicates the field may be populated by the request). For each field
 77  
      * found, if there is a corresponding key/value pair in the request
 78  
      * parameters, the value is used to populate the field. In addition, any
 79  
      * conditional properties of <code>PropertyReplacers</code> configured for
 80  
      * the field are cleared so that the request parameter value does not get
 81  
      * overridden by the dictionary conditional logic
 82  
      * 
 83  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#populateViewFromRequestParameters(org.kuali.rice.kns.uif.container.View,
 84  
      *      java.util.Map)
 85  
      */
 86  
     @Override
 87  
     public void populateViewFromRequestParameters(View view, Map<String, String> parameters) {
 88  
         // build set of view properties that can be populated
 89  0
         Set<String> fieldNamesToPopulate = new HashSet<String>();
 90  
 
 91  0
         Field[] fields = CloneUtils.getFields(view.getClass(), true);
 92  0
         for (int i = 0; i < fields.length; i++) {
 93  0
             Field field = fields[i];
 94  
 
 95  0
             RequestParameter requestParameter = field.getAnnotation(RequestParameter.class);
 96  0
             if (requestParameter != null) {
 97  
                 // use specified parameter name if given, else use field name
 98  0
                 if (StringUtils.isNotBlank(requestParameter.parameterName())) {
 99  0
                     fieldNamesToPopulate.add(requestParameter.parameterName());
 100  
                 } else {
 101  0
                     fieldNamesToPopulate.add(field.getName());
 102  
                 }
 103  
             }
 104  
         }
 105  
 
 106  
         // build Map of property replacers by property name
 107  0
         Map<String, Set<PropertyReplacer>> viewPropertyReplacers = new HashMap<String, Set<PropertyReplacer>>();
 108  0
         for (PropertyReplacer replacer : view.getPropertyReplacers()) {
 109  0
             Set<PropertyReplacer> propertyReplacers = new HashSet<PropertyReplacer>();
 110  0
             if (viewPropertyReplacers.containsKey(replacer.getPropertyName())) {
 111  0
                 propertyReplacers = viewPropertyReplacers.get(replacer.getPropertyName());
 112  
             }
 113  0
             propertyReplacers.add(replacer);
 114  
 
 115  0
             viewPropertyReplacers.put(replacer.getPropertyName(), propertyReplacers);
 116  0
         }
 117  
 
 118  
         // build map of view parameter key/values and populate view fields
 119  0
         Map<String, String> viewRequestParameters = new HashMap<String, String>();
 120  0
         for (String fieldToPopulate : fieldNamesToPopulate) {
 121  0
             if (parameters.containsKey(fieldToPopulate)) {
 122  0
                 String fieldValue = parameters.get(fieldToPopulate);
 123  
 
 124  0
                 if (StringUtils.isNotBlank(fieldValue)) {
 125  0
                     viewRequestParameters.put(fieldToPopulate, fieldValue);
 126  0
                     ObjectPropertyUtils.setPropertyValue(view, fieldToPopulate, fieldValue);
 127  
 
 128  
                     // remove any conditional configuration so value is not
 129  
                     // overridden later during the apply model phase
 130  0
                     String conditionalProperty = StringUtils.substring(fieldToPopulate, 0, 1).toLowerCase()
 131  
                             + StringUtils.substring(fieldToPopulate, 1, fieldToPopulate.length());
 132  0
                     conditionalProperty = UifConstants.EL_CONDITIONAL_PROPERTY_PREFIX + conditionalProperty;
 133  0
                     ObjectPropertyUtils.setPropertyValue(view, conditionalProperty, fieldValue, true);
 134  
 
 135  0
                     if (viewPropertyReplacers.containsKey(fieldToPopulate)) {
 136  0
                         Set<PropertyReplacer> propertyReplacers = viewPropertyReplacers.get(fieldToPopulate);
 137  0
                         for (PropertyReplacer replacer : propertyReplacers) {
 138  0
                             view.getPropertyReplacers().remove(replacer);
 139  
                         }
 140  
                     }
 141  
                 }
 142  0
             }
 143  
         }
 144  
 
 145  0
         view.setViewRequestParameters(viewRequestParameters);
 146  0
     }
 147  
 
 148  
     /**
 149  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performInitialization(org.kuali.rice.kns.uif.container.View,
 150  
      *      java.util.Map)
 151  
      */
 152  
     @Override
 153  
     public void performInitialization(View view) {
 154  
         // process component ids for duplicates
 155  0
         ComponentUtils.processIds(view, new HashMap<String, Integer>());
 156  
 
 157  0
         performComponentInitialization(view, view);
 158  0
     }
 159  
     
 160  
     /**
 161  
      * Performs the complete component lifecycle on the component passed in, in this order:
 162  
      * performComponentInitialization, performComponentApplyModel, and performComponentFinalize.
 163  
      * 
 164  
      * @see {@link #performComponentInitialization(View, Component)}
 165  
      * @see {@link #performComponentApplyModel(View, Component, Object)}
 166  
      * @see {@link #performComponentFinalize(View, Component, Object, Component)}
 167  
      * @param form
 168  
      * @param component
 169  
      */
 170  
     public void performComponentLifecycle(UifFormBase form, Component component, String origId){
 171  0
         Component origComponent = form.getView().getViewIndex().getComponentById(origId);
 172  
         
 173  0
         Component parent = (Component) origComponent.getContext().get(UifConstants.ContextVariableNames.PARENT);
 174  0
         component.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, parent);
 175  
         
 176  0
         performComponentInitialization(form.getView(), component);
 177  0
         performComponentApplyModel(form.getView(), component, form);
 178  0
         performComponentFinalize(form.getView(), component, form, parent);
 179  
         
 180  0
         component.setId(origId);
 181  0
     }
 182  
 
 183  
     /**
 184  
      * Performs initialization of a component by these steps:
 185  
      * 
 186  
      * <ul>
 187  
      * <li>For <code>AttributeField</code> instances, set defaults from the data
 188  
      * dictionary.</li>
 189  
      * <li>Invoke the initialize method on the component. Here the component can
 190  
      * setup defaults and do other initialization that is specific to that
 191  
      * component.</li>
 192  
      * <li>Invoke any configured <code>ComponentModifier</code> instances for
 193  
      * the component.</li>
 194  
      * <li>Call the component to get the List of components that are nested
 195  
      * within and recursively call this method to initialize those components.</li>
 196  
      * <li>Call custom initialize hook for service overrides</li>
 197  
      * </ul>
 198  
      * 
 199  
      * <p>
 200  
      * Note the order various initialize points are called, this can sometimes
 201  
      * be an important factor to consider when initializing a component
 202  
      * </p>
 203  
      * 
 204  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performComponentInitialization(org.kuali.rice.kns.uif.container.View,
 205  
      *      org.kuali.rice.kns.uif.core.Component)
 206  
      */
 207  
     public void performComponentInitialization(View view, Component component) {
 208  0
         if (component == null) {
 209  0
             return;
 210  
         }
 211  
 
 212  0
         LOG.debug("Initializing component: " + component.getId() + " with type: " + component.getClass());
 213  
 
 214  
         // invoke component to initialize itself after properties have been set
 215  0
         component.performInitialization(view);
 216  
 
 217  
         // for attribute fields, set defaults from dictionary entry
 218  0
         if (component instanceof AttributeField) {
 219  0
             initializeAttributeFieldFromDataDictionary(view, (AttributeField) component);
 220  
         }
 221  
 
 222  
         // for collection groups set defaults from dictionary entry
 223  0
         if (component instanceof CollectionGroup) {
 224  
             // TODO: initialize from dictionary
 225  
         }
 226  
 
 227  
         // invoke component initializers setup to run in the initialize phase
 228  0
         runComponentModifiers(view, component, null, UifConstants.ViewPhases.INITIALIZE);
 229  
 
 230  
         // initialize nested components
 231  0
         for (Component nestedComponent : component.getNestedComponents()) {
 232  0
             performComponentInitialization(view, nestedComponent);
 233  
         }
 234  
 
 235  
         // initialize property replacements (if components)
 236  0
         for (PropertyReplacer replacer : component.getPropertyReplacers()) {
 237  0
             if (Component.class.isAssignableFrom(replacer.getReplacement().getClass())) {
 238  0
                 performComponentInitialization(view, (Component) replacer.getReplacement());
 239  
             }
 240  
         }
 241  
 
 242  
         // invoke initialize service hook
 243  0
         performCustomInitialization(view, component);
 244  0
     }
 245  
 
 246  
     /**
 247  
      * Sets properties of the <code>AttributeField</code> (if blank) to the
 248  
      * corresponding attribute entry in the data dictionary
 249  
      * 
 250  
      * @param view
 251  
      *            - view instance containing the field
 252  
      * @param field
 253  
      *            - field instance to initialize
 254  
      */
 255  
     protected void initializeAttributeFieldFromDataDictionary(View view, AttributeField field) {
 256  0
         AttributeDefinition attributeDefinition = null;
 257  
 
 258  0
         String dictionaryAttributeName = field.getDictionaryAttributeName();
 259  0
         String dictionaryObjectEntry = field.getDictionaryObjectEntry();
 260  
 
 261  
         // if entry given but not attribute name, use field name as attribute
 262  
         // name
 263  0
         if (StringUtils.isNotBlank(dictionaryObjectEntry) && StringUtils.isBlank(dictionaryAttributeName)) {
 264  0
             dictionaryAttributeName = field.getPropertyName();
 265  
         }
 266  
 
 267  
         // if dictionary entry and attribute set, attempt to find definition
 268  0
         if (StringUtils.isNotBlank(dictionaryAttributeName) && StringUtils.isNotBlank(dictionaryObjectEntry)) {
 269  0
             attributeDefinition = getDataDictionaryService().getAttributeDefinition(dictionaryObjectEntry,
 270  
                     dictionaryAttributeName);
 271  
         }
 272  
 
 273  
         // if definition not found, recurse through path
 274  0
         if (attributeDefinition == null) {
 275  0
             String propertyPath = field.getBindingInfo().getBindingPath();
 276  0
             if (StringUtils.isNotBlank(field.getBindingInfo().getCollectionPath())) {
 277  0
                 propertyPath = field.getBindingInfo().getCollectionPath();
 278  0
                 if (StringUtils.isNotBlank(field.getBindingInfo().getBindByNamePrefix())) {
 279  0
                     propertyPath += "." + field.getBindingInfo().getBindByNamePrefix();
 280  
                 }
 281  0
                 propertyPath += "." + field.getBindingInfo().getBindingName();
 282  
             }
 283  
             
 284  0
             attributeDefinition = findNestedDictionaryAttribute(view, field, null, propertyPath);
 285  
         }
 286  
 
 287  
         // if a definition was found, initialize field from definition
 288  0
         if (attributeDefinition != null) {
 289  0
             field.copyFromAttributeDefinition(attributeDefinition);
 290  
         }
 291  
 
 292  0
         if (field.getControl() == null) {
 293  0
             field.setControl(ComponentFactory.getTextControl());
 294  
         }
 295  0
     }
 296  
     
 297  
     /**
 298  
      * Recursively drills down the property path (if nested) to find an
 299  
      * AttributeDefinition, the first attribute definition found will be
 300  
      * returned
 301  
      * 
 302  
      * <p>
 303  
      * e.g. suppose parentPath is 'document' and propertyPath is
 304  
      * 'account.subAccount.name', first the property type for document will be
 305  
      * retrieved using the view metadata and used as the dictionary entry, with
 306  
      * the propertyPath as the dictionary attribute, if an attribute definition
 307  
      * exists it will be returned. Else, the first part of the property path is
 308  
      * added to the parent, making the parentPath 'document.account' and the
 309  
      * propertyPath 'subAccount.name', the method is then called again to
 310  
      * perform the process with those parameters. The recursion continues until
 311  
      * an attribute field is found, or the propertyPath is no longer nested
 312  
      * </p>
 313  
      * 
 314  
      * @param view
 315  
      *            - view instance containing the field
 316  
      * @param field
 317  
      *            - field we are attempting to find a supporting attribute
 318  
      *            definition for
 319  
      * @param parentPath
 320  
      *            - parent path to use for getting the dictionary entry
 321  
      * @param propertyPath
 322  
      *            - path of the property relative to the parent, to use as
 323  
      *            dictionary attribute and to drill down on
 324  
      * @return AttributeDefinition if found, or Null
 325  
      */
 326  
     protected AttributeDefinition findNestedDictionaryAttribute(View view, AttributeField field, String parentPath,
 327  
             String propertyPath) {
 328  0
         AttributeDefinition attributeDefinition = null;
 329  
 
 330  
         // attempt to find definition for parent and property
 331  0
         String dictionaryAttributeName = propertyPath;
 332  0
         String dictionaryObjectEntry = null;
 333  
         
 334  0
         if (field.getBindingInfo().isBindToMap()) {
 335  0
             parentPath = "";
 336  0
             if (!field.getBindingInfo().isBindToForm() && StringUtils.isNotBlank(field.getBindingInfo().getBindingObjectPath())) {
 337  0
                 parentPath = field.getBindingInfo().getBindingObjectPath();
 338  
             }
 339  0
             if (StringUtils.isNotBlank(field.getBindingInfo().getBindByNamePrefix())) {
 340  0
                 if (StringUtils.isNotBlank(parentPath)) {
 341  0
                     parentPath += "." + field.getBindingInfo().getBindByNamePrefix();
 342  
                 }
 343  
                 else {
 344  0
                     parentPath = field.getBindingInfo().getBindByNamePrefix();
 345  
                 }
 346  
             }
 347  
             
 348  0
             dictionaryAttributeName = field.getBindingInfo().getBindingName();
 349  
         }
 350  
 
 351  0
         if (StringUtils.isNotBlank(parentPath)) {
 352  0
             Class<?> dictionaryModelClass = ViewModelUtils.getPropertyType(view, parentPath);
 353  0
             if (dictionaryModelClass != null) {
 354  0
                 dictionaryObjectEntry = dictionaryModelClass.getName();
 355  
 
 356  0
                 attributeDefinition = getDataDictionaryService().getAttributeDefinition(dictionaryObjectEntry,
 357  
                         dictionaryAttributeName);
 358  
             }
 359  
         }
 360  
 
 361  
         // if definition not found and property is still nested, recurse down
 362  
         // one level
 363  0
         if ((attributeDefinition == null) && StringUtils.contains(propertyPath, ".")) {
 364  0
             String nextParentPath = StringUtils.substringBefore(propertyPath, ".");
 365  0
             if (StringUtils.isNotBlank(parentPath)) {
 366  0
                 nextParentPath = parentPath + "." + nextParentPath;
 367  
             }
 368  0
             String nextPropertyPath = StringUtils.substringAfter(propertyPath, ".");
 369  
 
 370  0
             return findNestedDictionaryAttribute(view, field, nextParentPath, nextPropertyPath);
 371  
         }
 372  
 
 373  
         // if a definition was found, update the fields dictionary properties
 374  0
         if (attributeDefinition != null) {
 375  0
             field.setDictionaryAttributeName(dictionaryAttributeName);
 376  0
             field.setDictionaryObjectEntry(dictionaryObjectEntry);
 377  
         }
 378  
 
 379  0
         return attributeDefinition;
 380  
     }
 381  
 
 382  
     /**
 383  
      * Determines the dictionary class that is associated with the given
 384  
      * <code>AttributeField</code>
 385  
      * 
 386  
      * @param view
 387  
      *            - view instance for field
 388  
      * @param field
 389  
      *            - field instance to determine dictionary class for
 390  
      * @return Class<?> dictionary class or null if not found
 391  
      */
 392  
     protected Class<?> getDictionaryModelClass(View view, AttributeField field) {
 393  0
         return ViewModelUtils.getParentObjectClassForMetadata(view, field);
 394  
     }
 395  
 
 396  
     /**
 397  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performApplyModel(org.kuali.rice.kns.uif.container.View,
 398  
      *      java.lang.Object)
 399  
      */
 400  
     @Override
 401  
     public void performApplyModel(View view, Object model) {
 402  
         // get action flag and edit modes from authorizer/presentation
 403  
         // controller
 404  0
         invokeAuthorizerPresentationController(view, (UifFormBase) model);
 405  
 
 406  
         // set view context for conditional expressions
 407  0
         setViewContext(view, model);
 408  
 
 409  0
         performComponentApplyModel(view, view, model);
 410  0
     }
 411  
 
 412  
     /**
 413  
      * Invokes the configured <code>PresentationController</code> and
 414  
      * </code>Authorizer</code> for the view to get the exported action flags
 415  
      * and edit modes that can be used in conditional logic
 416  
      * 
 417  
      * @param view
 418  
      *            - view instance that is being built and
 419  
      *            presentation/authorizer pulled for
 420  
      * @param model
 421  
      *            - Object that contains the model data
 422  
      */
 423  
     protected void invokeAuthorizerPresentationController(View view, UifFormBase model) {
 424  0
         PresentationController presentationController = ObjectUtils.newInstance(view.getPresentationControllerClass());
 425  0
         Authorizer authorizer = ObjectUtils.newInstance(view.getAuthorizerClass());
 426  
 
 427  0
         Person user = GlobalVariables.getUserSession().getPerson();
 428  
 
 429  0
         Set<String> actionFlags = presentationController.getActionFlags(model);
 430  0
         actionFlags = authorizer.getActionFlags(model, user, actionFlags);
 431  
         
 432  0
         view.setActionFlags(new BooleanMap(actionFlags));
 433  
 
 434  0
         Set<String> editModes = presentationController.getEditModes(model);
 435  0
         editModes = authorizer.getEditModes(model, user, editModes);
 436  
         
 437  0
         view.setEditModes(new BooleanMap(editModes));
 438  0
     }
 439  
 
 440  
     /**
 441  
      * Sets up the view context which will be available to other components
 442  
      * through their context for conditional logic evaluation
 443  
      * 
 444  
      * @param view
 445  
      *            - view instance to set context for
 446  
      * @param model
 447  
      *            - object containing the view data
 448  
      */
 449  
     protected void setViewContext(View view, Object model) {
 450  0
         view.getContext().putAll(getPreModelContext(view));
 451  
 
 452  
         // evaluate view expressions for further context
 453  0
         for (Entry<String, String> variableExpression : view.getExpressionVariables().entrySet()) {
 454  0
             String variableName = variableExpression.getKey();
 455  0
             Object value = getExpressionEvaluatorService().evaluateExpression(model, view.getContext(),
 456  
                     variableExpression.getValue());
 457  0
             view.pushObjectToContext(variableName, value);
 458  0
         }
 459  0
     }
 460  
     
 461  
     /**
 462  
      * Returns the general context that is available before the apply model
 463  
      * phase (during the initialize phase)
 464  
      * 
 465  
      * @param view
 466  
      *            - view instance for context
 467  
      * @return Map<String, Object> context map
 468  
      */
 469  
     protected Map<String, Object> getPreModelContext(View view) {
 470  0
         Map<String, Object> context = new HashMap<String, Object>();
 471  
 
 472  0
         context.put(UifConstants.ContextVariableNames.VIEW, view);
 473  
 
 474  0
         Properties properties = KNSServiceLocator.getKualiConfigurationService().getAllProperties();
 475  0
         context.put(UifConstants.ContextVariableNames.CONFIG_PROPERTIES, properties);
 476  0
         context.put(UifConstants.ContextVariableNames.CONSTANTS, KNSConstants.class);
 477  
 
 478  0
         return context;
 479  
     }
 480  
 
 481  
     /**
 482  
      * Applies the model data to a component of the View instance
 483  
      * 
 484  
      * <p>
 485  
      * The component is invoked to to apply the model data. Here the component
 486  
      * can generate any additional fields needed or alter the configured fields.
 487  
      * After the component is invoked a hook for custom helper service
 488  
      * processing is invoked. Finally the method is recursively called for all
 489  
      * the component children
 490  
      * </p>
 491  
      * 
 492  
      * @param view
 493  
      *            - view instance the component belongs to
 494  
      * @param component
 495  
      *            - the component instance the model should be applied to
 496  
      * @param model
 497  
      *            - top level object containing the data
 498  
      */
 499  
     protected void performComponentApplyModel(View view, Component component, Object model) {
 500  0
         if (component == null) {
 501  0
             return;
 502  
         }
 503  
 
 504  
         // evaluate expressions on properties
 505  0
         component.getContext().putAll(getCommonContext(view, component));
 506  0
         getExpressionEvaluatorService().evaluateObjectProperties(component, model, component.getContext());
 507  
 
 508  0
         if (component instanceof Container) {
 509  0
             LayoutManager layoutManager = ((Container) component).getLayoutManager();
 510  
 
 511  0
             if (layoutManager != null) {
 512  0
                 layoutManager.getContext().putAll(getCommonContext(view, component));
 513  0
                 layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
 514  0
                 layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.MANAGER, layoutManager);
 515  0
                 getExpressionEvaluatorService().evaluateObjectProperties(layoutManager, model,
 516  
                         layoutManager.getContext());
 517  
             }
 518  
         }
 519  
 
 520  0
         if (component instanceof DataBinding) {
 521  0
             BindingInfo bindingInfo = ((DataBinding) component).getBindingInfo();
 522  0
             getExpressionEvaluatorService().evaluateObjectProperties(bindingInfo, model, component.getContext());
 523  
         }
 524  
 
 525  
         // invoke component to perform its conditional logic
 526  0
         component.performApplyModel(view, model);
 527  
 
 528  
         // invoke service override hook
 529  0
         performCustomApplyModel(view, component, model);
 530  
 
 531  
         // invoke component modifiers configured to run in the apply model phase
 532  0
         runComponentModifiers(view, component, model, UifConstants.ViewPhases.APPLY_MODEL);
 533  
 
 534  
         // get children and recursively perform conditional logic
 535  0
         for (Component nestedComponent : component.getNestedComponents()) {
 536  0
             if (nestedComponent != null) {
 537  0
                 nestedComponent.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
 538  
             }
 539  
 
 540  0
             performComponentApplyModel(view, nestedComponent, model);
 541  
         }
 542  0
     }
 543  
 
 544  
     /**
 545  
      * Runs any configured <code>ComponentModifiers</code> for the given
 546  
      * component that match the given run phase and who run condition evaluation
 547  
      * succeeds
 548  
      * 
 549  
      * @param view
 550  
      *            - view instance for context
 551  
      * @param component
 552  
      *            - component instance whose modifiers should be run
 553  
      * @param model
 554  
      *            - model object for context
 555  
      * @param runPhase
 556  
      *            - current phase to match on
 557  
      */
 558  
     protected void runComponentModifiers(View view, Component component, Object model, String runPhase) {
 559  0
         for (ComponentModifier modifier : component.getComponentModifiers()) {
 560  
             // check run phase matches
 561  0
             if (StringUtils.equals(modifier.getRunPhase(), runPhase)) {
 562  
                 // check condition (if set) evaluates to true
 563  0
                 boolean runModifier = true;
 564  0
                 if (StringUtils.isNotBlank(modifier.getRunCondition())) {
 565  0
                     Map<String, Object> context = new HashMap<String, Object>();
 566  0
                     context.put(UifConstants.ContextVariableNames.COMPONENT, component);
 567  0
                     context.put(UifConstants.ContextVariableNames.VIEW, view);
 568  
 
 569  0
                     String conditionEvaluation = getExpressionEvaluatorService().evaluateExpressionTemplate(model,
 570  
                             context, modifier.getRunCondition());
 571  0
                     runModifier = Boolean.parseBoolean(conditionEvaluation);
 572  
                 }
 573  
 
 574  0
                 if (runModifier) {
 575  0
                     if (StringUtils.equals(runPhase, UifConstants.ViewPhases.APPLY_MODEL)
 576  
                             || StringUtils.equals(runPhase, UifConstants.ViewPhases.FINALIZE)) {
 577  0
                         modifier.performModification(view, model, component);
 578  
                     } else {
 579  0
                         modifier.performModification(view, component);
 580  
                     }
 581  
                 }
 582  0
             }
 583  
         }
 584  0
     }
 585  
 
 586  
     /**
 587  
      * Gets global objects for the context map and pushes them to the context
 588  
      * for the component
 589  
      * 
 590  
      * @param view
 591  
      *            - view instance for component
 592  
      * @param component
 593  
      *            - component instance to push context to
 594  
      */
 595  
     protected Map<String, Object> getCommonContext(View view, Component component) {
 596  0
         Map<String, Object> context = new HashMap<String, Object>();
 597  
 
 598  0
         context.putAll(view.getContext());
 599  0
         context.put(UifConstants.ContextVariableNames.COMPONENT, component);
 600  
 
 601  0
         return context;
 602  
     }
 603  
 
 604  
     /**
 605  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performFinalize(org.kuali.rice.kns.uif.container.View,
 606  
      *      java.lang.Object)
 607  
      */
 608  
     @Override
 609  
     public void performFinalize(View view, Object model) {
 610  0
         performComponentFinalize(view, view, model, null);
 611  0
     }
 612  
 
 613  
     /**
 614  
      * Update state of the given component and does final preparation for
 615  
      * rendering
 616  
      * 
 617  
      * @param view
 618  
      *            - view instance the component belongs to
 619  
      * @param component
 620  
      *            - the component instance that should be updated
 621  
      * @param model
 622  
      *            - top level object containing the data
 623  
      * @param parent
 624  
      *            - Parent component for the component being finalized
 625  
      */
 626  
     protected void performComponentFinalize(View view, Component component, Object model, Component parent) {
 627  0
         if (component == null) {
 628  0
             return;
 629  
         }
 630  
         
 631  
         // invoke configured method finalizers
 632  0
         invokeMethodFinalizer(view, component);
 633  
         
 634  
         // invoke component to update its state
 635  0
         component.performFinalize(view, model, parent);
 636  
 
 637  
         // invoke service override hook
 638  0
         performCustomFinalize(view, component, model, parent);
 639  
 
 640  
         // invoke component initializers setup to run in the finalize phase
 641  0
         runComponentModifiers(view, component, model, UifConstants.ViewPhases.FINALIZE);
 642  
 
 643  
         // get components children and recursively update state
 644  0
         for (Component nestedComponent : component.getNestedComponents()) {
 645  0
             performComponentFinalize(view, nestedComponent, model, component);
 646  
         }
 647  0
     }
 648  
     
 649  
     /**
 650  
      * Invokes the finalize method for the component (if configured) and sets
 651  
      * the render output for the component to the returned method string (if
 652  
      * method is not a void type)
 653  
      * 
 654  
      * @param view
 655  
      *            - view instance that contains the component
 656  
      * @param component
 657  
      *            - component to run finalize method for
 658  
      */
 659  
     protected void invokeMethodFinalizer(View view, Component component) {
 660  0
         String finalizeMethodToCall = component.getFinalizeMethodToCall();
 661  0
         MethodInvoker finalizeMethodInvoker = component.getFinalizeMethodInvoker();
 662  
 
 663  0
         if (StringUtils.isBlank(finalizeMethodToCall) && (finalizeMethodInvoker == null)) {
 664  0
             return;
 665  
         }
 666  
 
 667  0
         if (finalizeMethodInvoker == null) {
 668  0
             finalizeMethodInvoker = new MethodInvoker();
 669  
         }
 670  
 
 671  
         // if method not set on invoker, use renderingMethodToCall, note staticMethod could be set(don't know since
 672  
         // there is not a getter), if so it will override the target method in prepare
 673  0
         if (StringUtils.isBlank(finalizeMethodInvoker.getTargetMethod())) {
 674  0
             finalizeMethodInvoker.setTargetMethod(finalizeMethodToCall);
 675  
         }
 676  
 
 677  
         // if target class or object not set, use view helper service
 678  0
         if ((finalizeMethodInvoker.getTargetClass() == null) && (finalizeMethodInvoker.getTargetObject() == null)) {
 679  0
             finalizeMethodInvoker.setTargetObject(view.getViewHelperService());
 680  
         }
 681  
 
 682  
         // add the component instance as an argument
 683  0
         Object[] arguments = new Object[1];
 684  0
         arguments[0] = component;
 685  0
         finalizeMethodInvoker.setArguments(arguments);
 686  
 
 687  
         // invoke method and get render output
 688  
         try {
 689  0
             LOG.debug("Invoking render method: " + finalizeMethodInvoker.getTargetMethod() + " for component: "
 690  
                     + component.getId());
 691  0
             finalizeMethodInvoker.prepare();
 692  
 
 693  0
             Class<?> methodReturnType = finalizeMethodInvoker.getPreparedMethod().getReturnType();
 694  0
             if (StringUtils.equals("void", methodReturnType.getName())) {
 695  0
                 finalizeMethodInvoker.invoke();
 696  
             } else {
 697  0
                 String renderOutput = (String) finalizeMethodInvoker.invoke();
 698  
 
 699  0
                 component.setSelfRendered(true);
 700  0
                 component.setRenderOutput(renderOutput);
 701  
             }
 702  0
         } catch (Exception e) {
 703  0
             LOG.error("Error invoking rendering method for component: " + component.getId(), e);
 704  0
             throw new RuntimeException("Error invoking rendering method for component: " + component.getId(), e);
 705  0
         }
 706  0
     }
 707  
 
 708  
     /**
 709  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#processCollectionAddLine(org.kuali.rice.kns.uif.container.View,
 710  
      *      java.lang.Object, java.lang.String)
 711  
      */
 712  
     @Override
 713  
     public void processCollectionAddLine(View view, Object model, String collectionPath) {
 714  
         // get the collection group from the view
 715  0
         CollectionGroup collectionGroup = view.getViewIndex().getCollectionGroupByPath(collectionPath);
 716  0
         if (collectionGroup == null) {
 717  0
             logAndThrowRuntime("Unable to get collection group component for path: " + collectionPath);
 718  
         }
 719  
 
 720  
         // get the collection instance for adding the new line
 721  0
         Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath);
 722  0
         if (collection == null) {
 723  0
             logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath);
 724  
         }
 725  
 
 726  
         // now get the new line we need to add
 727  0
         String addLinePath = collectionGroup.getAddLineBindingInfo().getBindingPath();
 728  0
         Object addLine = ObjectPropertyUtils.getPropertyValue(model, addLinePath);
 729  0
         if (addLine == null) {
 730  0
             logAndThrowRuntime("Add line instance not found for path: " + addLinePath);
 731  
         }
 732  
 
 733  0
         processBeforeAddLine(view, collectionGroup, model, addLine);
 734  
 
 735  
         // validate the line to make sure it is ok to add
 736  0
         boolean isValidLine = performAddLineValidation(view, collectionGroup, model, addLine);
 737  0
         if (isValidLine) {
 738  
             // TODO: should check to see if there is an add line method on the
 739  
             // collection parent and if so call that instead of just adding to
 740  
             // the collection (so that sequence can be set)
 741  0
             collection.add(addLine);
 742  
 
 743  
             // make a new instance for the add line
 744  0
             collectionGroup.initializeNewCollectionLine(view, model, collectionGroup, true);
 745  
         }
 746  
 
 747  0
         processAfterAddLine(view, collectionGroup, model, addLine);
 748  0
     }
 749  
 
 750  
     /**
 751  
      * Performs validation on the new collection line before it is added to the
 752  
      * corresponding collection
 753  
      * 
 754  
      * @param view
 755  
      *            - view instance that the action was taken on
 756  
      * @param collectionGroup
 757  
      *            - collection group component for the collection
 758  
      * @param addLine
 759  
      *            - new line instance to validate
 760  
      * @param model
 761  
      *            - object instance that contain's the views data
 762  
      * @return boolean true if the line is valid and it should be added to the
 763  
      *         collection, false if it was not valid and should not be added to
 764  
      *         the collection
 765  
      */
 766  
     protected boolean performAddLineValidation(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
 767  0
         boolean isValid = true;
 768  
 
 769  
         // TODO: this should invoke rules, sublclasses like the document view
 770  
         // should create the document add line event
 771  
 
 772  0
         return isValid;
 773  
     }
 774  
 
 775  
     /**
 776  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#processCollectionDeleteLine(org.kuali.rice.kns.uif.container.View,
 777  
      *      java.lang.Object, java.lang.String, int)
 778  
      */
 779  
     public void processCollectionDeleteLine(View view, Object model, String collectionPath, int lineIndex) {
 780  
         // get the collection group from the view
 781  0
         CollectionGroup collectionGroup = view.getViewIndex().getCollectionGroupByPath(collectionPath);
 782  0
         if (collectionGroup == null) {
 783  0
             logAndThrowRuntime("Unable to get collection group component for path: " + collectionPath);
 784  
         }
 785  
 
 786  
         // get the collection instance for adding the new line
 787  0
         Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath);
 788  0
         if (collection == null) {
 789  0
             logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath);
 790  
         }
 791  
 
 792  
         // TODO: look into other ways of identifying a line so we can deal with
 793  
         // unordered collections
 794  0
         if (collection instanceof List) {
 795  0
             Object deleteLine = ((List<Object>) collection).get(lineIndex);
 796  
 
 797  
             // validate the delete action is allowed for this line
 798  0
             boolean isValid = performDeleteLineValidation(view, collectionGroup, deleteLine);
 799  0
             if (isValid) {
 800  0
                 ((List<Object>) collection).remove(lineIndex);
 801  
             }
 802  0
         } else {
 803  0
             logAndThrowRuntime("Only List collection implementations are supported for the delete by index method");
 804  
         }
 805  0
     }
 806  
 
 807  
     /**
 808  
      * Performs validation on the collection line before it is removed from the
 809  
      * corresponding collection
 810  
      * 
 811  
      * @param view
 812  
      *            - view instance that the action was taken on
 813  
      * @param collectionGroup
 814  
      *            - collection group component for the collection
 815  
      * @param deleteLine
 816  
      *            - line that will be removed
 817  
      * @return boolean true if the action is allowed and the line should be
 818  
      *         removed, false if the line should not be removed
 819  
      */
 820  
     protected boolean performDeleteLineValidation(View view, CollectionGroup collectionGroup, Object deleteLine) {
 821  0
         boolean isValid = true;
 822  
 
 823  
         // TODO: this should invoke rules, sublclasses like the document view
 824  
         // should create the document delete line event
 825  
 
 826  0
         return isValid;
 827  
     }
 828  
 
 829  
     /**
 830  
      * Finds the <code>Inquirable</code> configured for the given data object
 831  
      * class and delegates to it for building the inquiry URL
 832  
      * 
 833  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#buildInquiryLink(java.lang.Object,
 834  
      *      java.lang.String, org.kuali.rice.kns.uif.widget.Inquiry)
 835  
      */
 836  
     public void buildInquiryLink(Object dataObject, String propertyName, Inquiry inquiry) {
 837  0
         Inquirable inquirable = getViewDictionaryService().getInquirable(dataObject.getClass(), inquiry.getViewName());
 838  0
         if (inquirable != null) {
 839  0
             inquirable.buildInquirableLink(dataObject, propertyName, inquiry);
 840  
         } else {
 841  
             // inquirable not found, no inquiry link can be set
 842  0
             inquiry.setRender(false);
 843  
         }
 844  0
     }
 845  
 
 846  
     /**
 847  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#applyDefaultValues(org.kuali.rice.kns.uif.container.View,
 848  
      *      org.kuali.rice.kns.web.spring.form.UifFormBase)
 849  
      */
 850  
     public void applyDefaultValues(View view, UifFormBase model) {
 851  
         // retrieve all attribute fields for the view and apply their configured
 852  
         // default value to the model
 853  0
         Map<String, AttributeField> attributeFields = view.getViewIndex().getAttributeFieldIndex();
 854  0
         for (Entry<String, AttributeField> attributeFieldEntry : attributeFields.entrySet()) {
 855  0
             String bindingPath = attributeFieldEntry.getKey();
 856  0
             AttributeField attributeField = attributeFieldEntry.getValue();
 857  
 
 858  0
             populateDefaultValueForField(view, model, attributeField, bindingPath);
 859  0
         }
 860  
         
 861  
         // update form indicator
 862  0
         model.setDefaultsApplied(true);
 863  0
     }
 864  
     
 865  
     /**
 866  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#applyDefaultValuesForCollectionLine(org.kuali.rice.kns.uif.container.View,
 867  
      *      java.lang.Object, org.kuali.rice.kns.uif.container.CollectionGroup,
 868  
      *      java.lang.Object)
 869  
      */
 870  
     public void applyDefaultValuesForCollectionLine(View view, Object model, CollectionGroup collectionGroup,
 871  
             Object line) {
 872  
         // retrieve all attribute fields for the collection line
 873  0
         List<AttributeField> attributeFields = ComponentUtils.getComponentsOfTypeDeep(collectionGroup.getAddLineFields(),
 874  
                 AttributeField.class);
 875  0
         for (AttributeField attributeField : attributeFields) {
 876  0
             String bindingPath = "";
 877  0
             if (StringUtils.isNotBlank(attributeField.getBindingInfo().getBindByNamePrefix())) {
 878  0
                 bindingPath = attributeField.getBindingInfo().getBindByNamePrefix() + ".";
 879  
             }
 880  0
             bindingPath += attributeField.getBindingInfo().getBindingName();
 881  
 
 882  0
             populateDefaultValueForField(view, line, attributeField, bindingPath);
 883  0
         }
 884  0
     }
 885  
 
 886  
     /**
 887  
      * Applies the default value configured for the given field (if any) to the
 888  
      * line given object property that is determined by the given binding path
 889  
      * 
 890  
      * <p>
 891  
      * Checks for a configured default value or default value class for the
 892  
      * field. If both are given, the configured static default value will win.
 893  
      * In addition, if the default value contains an el expression it is
 894  
      * evaluated against the initial context
 895  
      * </p>
 896  
      * 
 897  
      * @param view
 898  
      *            - view instance the field belongs to
 899  
      * @param model
 900  
      *            - object that should be populated
 901  
      * @param attributeField
 902  
      *            - field to check for configured default value
 903  
      * @param bindingPath
 904  
      *            - path to the property on the object that should be populated
 905  
      */
 906  
     protected void populateDefaultValueForField(View view, Object object, AttributeField attributeField,
 907  
             String bindingPath) {
 908  
         // check for configured default value
 909  0
         String defaultValue = attributeField.getDefaultValue();
 910  0
         if (StringUtils.isBlank(defaultValue) && (attributeField.getDefaultValueFinderClass() != null)) {
 911  0
             ValueFinder defaultValueFinder = ObjectUtils.newInstance(attributeField.getDefaultValueFinderClass());
 912  0
             defaultValue = defaultValueFinder.getValue();
 913  
         }
 914  
 
 915  
         // populate default value if given and path is valid
 916  0
         if (StringUtils.isNotBlank(defaultValue) && ObjectPropertyUtils.isWritableProperty(object, bindingPath)) {
 917  0
             if (getExpressionEvaluatorService().containsElPlaceholder(defaultValue)) {
 918  0
                 Map<String, Object> context = getPreModelContext(view);
 919  0
                 defaultValue = getExpressionEvaluatorService().evaluateExpressionTemplate(null, context, defaultValue);
 920  
             }
 921  
 
 922  
             // TODO: this should go through our formatters
 923  0
             ObjectPropertyUtils.setPropertyValue(object, bindingPath, defaultValue);
 924  
         }
 925  0
     }
 926  
 
 927  
     /**
 928  
      * Hook for service overrides to perform custom initialization on the
 929  
      * component
 930  
      * 
 931  
      * @param view
 932  
      *            - view instance containing the component
 933  
      * @param component
 934  
      *            - component instance to initialize
 935  
      */
 936  
     protected void performCustomInitialization(View view, Component component) {
 937  
 
 938  0
     }
 939  
 
 940  
     /**
 941  
      * Hook for service overrides to perform custom apply model logic on the
 942  
      * component
 943  
      * 
 944  
      * @param view
 945  
      *            - view instance containing the component
 946  
      * @param component
 947  
      *            - component instance to apply model to
 948  
      * @param model
 949  
      *            - Top level object containing the data (could be the form or a
 950  
      *            top level business object, dto)
 951  
      */
 952  
     protected void performCustomApplyModel(View view, Component component, Object model) {
 953  
 
 954  0
     }
 955  
 
 956  
     /**
 957  
      * Hook for service overrides to perform custom component finalization
 958  
      * 
 959  
      * @param view
 960  
      *            - view instance containing the component
 961  
      * @param component
 962  
      *            - component instance to update
 963  
      * @param model
 964  
      *            - Top level object containing the data
 965  
      * @param parent
 966  
      *            - Parent component for the component being finalized
 967  
      */
 968  
     protected void performCustomFinalize(View view, Component component, Object model, Component parent) {
 969  
 
 970  0
     }
 971  
 
 972  
     /**
 973  
      * Hook for service overrides to process the new collection line before it
 974  
      * is added to the collection
 975  
      * 
 976  
      * @param view
 977  
      *            - view instance that is being presented (the action was taken
 978  
      *            on)
 979  
      * @param collectionGroup
 980  
      *            - collection group component for the collection the line will
 981  
      *            be added to
 982  
      * @param model
 983  
      *            - object instance that contain's the views data
 984  
      * @param addLine
 985  
      *            - the new line instance to be processed
 986  
      */
 987  
     protected void processBeforeAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
 988  
 
 989  0
     }
 990  
 
 991  
     /**
 992  
      * Hook for service overrides to process the new collection line after it
 993  
      * has been added to the collection
 994  
      * 
 995  
      * @param view
 996  
      *            - view instance that is being presented (the action was taken
 997  
      *            on)
 998  
      * @param collectionGroup
 999  
      *            - collection group component for the collection the line that
 1000  
      *            was added
 1001  
      * @param model
 1002  
      *            - object instance that contain's the views data
 1003  
      * @param addLine
 1004  
      *            - the new line that was added
 1005  
      */
 1006  
     protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
 1007  
 
 1008  0
     }
 1009  
 
 1010  
     protected void logAndThrowRuntime(String message) {
 1011  0
         LOG.error(message);
 1012  0
         throw new RuntimeException(message);
 1013  
     }
 1014  
 
 1015  
     protected DataDictionaryService getDataDictionaryService() {
 1016  0
         if (this.dataDictionaryService == null) {
 1017  0
             this.dataDictionaryService = KNSServiceLocatorWeb.getDataDictionaryService();
 1018  
         }
 1019  
 
 1020  0
         return this.dataDictionaryService;
 1021  
     }
 1022  
 
 1023  
     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
 1024  0
         this.dataDictionaryService = dataDictionaryService;
 1025  0
     }
 1026  
 
 1027  
     protected ExpressionEvaluatorService getExpressionEvaluatorService() {
 1028  0
         if (this.expressionEvaluatorService == null) {
 1029  0
             this.expressionEvaluatorService = KNSServiceLocatorWeb.getExpressionEvaluatorService();
 1030  
         }
 1031  
 
 1032  0
         return this.expressionEvaluatorService;
 1033  
     }
 1034  
 
 1035  
     public void setExpressionEvaluatorService(ExpressionEvaluatorService expressionEvaluatorService) {
 1036  0
         this.expressionEvaluatorService = expressionEvaluatorService;
 1037  0
     }
 1038  
 
 1039  
     public ViewDictionaryService getViewDictionaryService() {
 1040  0
         if (this.viewDictionaryService == null) {
 1041  0
             this.viewDictionaryService = KNSServiceLocatorWeb.getViewDictionaryService();
 1042  
         }
 1043  0
         return this.viewDictionaryService;
 1044  
     }
 1045  
 
 1046  
     public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) {
 1047  0
         this.viewDictionaryService = viewDictionaryService;
 1048  0
     }
 1049  
 }