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