Coverage Report - org.kuali.rice.kns.uif.service.impl.ViewHelperServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ViewHelperServiceImpl
0%
0/204
0%
0/98
2.767
 
 1  
 /*
 2  
  * Copyright 2007 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community License, Version 1.0 (the
 5  
  * "License"); you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  * http://www.opensource.org/licenses/ecl1.php
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 12  
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 13  
  * License for the specific language governing permissions and limitations under
 14  
  * the License.
 15  
  */
 16  
 package org.kuali.rice.kns.uif.service.impl;
 17  
 
 18  
 import java.lang.reflect.Field;
 19  
 import java.util.Collection;
 20  
 import java.util.HashMap;
 21  
 import java.util.HashSet;
 22  
 import java.util.List;
 23  
 import java.util.Map;
 24  
 import java.util.Map.Entry;
 25  
 import java.util.Properties;
 26  
 import java.util.Set;
 27  
 
 28  
 import org.apache.commons.lang.StringUtils;
 29  
 import org.kuali.rice.kim.bo.Person;
 30  
 import org.kuali.rice.kns.datadictionary.AttributeDefinition;
 31  
 import org.kuali.rice.kns.inquiry.Inquirable;
 32  
 import org.kuali.rice.kns.service.DataDictionaryService;
 33  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 34  
 import org.kuali.rice.kns.service.KNSServiceLocatorWeb;
 35  
 import org.kuali.rice.kns.uif.UifConstants;
 36  
 import org.kuali.rice.kns.uif.authorization.Authorizer;
 37  
 import org.kuali.rice.kns.uif.authorization.PresentationController;
 38  
 import org.kuali.rice.kns.uif.container.CollectionGroup;
 39  
 import org.kuali.rice.kns.uif.container.Container;
 40  
 import org.kuali.rice.kns.uif.container.View;
 41  
 import org.kuali.rice.kns.uif.core.Component;
 42  
 import org.kuali.rice.kns.uif.core.PropertyReplacer;
 43  
 import org.kuali.rice.kns.uif.core.RequestParameter;
 44  
 import org.kuali.rice.kns.uif.field.AttributeField;
 45  
 import org.kuali.rice.kns.uif.layout.LayoutManager;
 46  
 import org.kuali.rice.kns.uif.modifier.ComponentModifier;
 47  
 import org.kuali.rice.kns.uif.service.ExpressionEvaluatorService;
 48  
 import org.kuali.rice.kns.uif.service.ViewDictionaryService;
 49  
 import org.kuali.rice.kns.uif.service.ViewHelperService;
 50  
 import org.kuali.rice.kns.uif.util.CloneUtils;
 51  
 import org.kuali.rice.kns.uif.util.ObjectPropertyUtils;
 52  
 import org.kuali.rice.kns.uif.util.ViewModelUtils;
 53  
 import org.kuali.rice.kns.uif.widget.Inquiry;
 54  
 import org.kuali.rice.kns.util.GlobalVariables;
 55  
 import org.kuali.rice.kns.util.KNSConstants;
 56  
 import org.kuali.rice.kns.util.ObjectUtils;
 57  
 import org.kuali.rice.kns.web.spring.form.UifFormBase;
 58  
 
 59  
 /**
 60  
  * Default Implementation of <code>ViewHelperService</code>
 61  
  * 
 62  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 63  
  */
 64  0
 public class ViewHelperServiceImpl implements ViewHelperService {
 65  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ViewHelperServiceImpl.class);
 66  
 
 67  
     private transient DataDictionaryService dataDictionaryService;
 68  
     private transient ExpressionEvaluatorService expressionEvaluatorService;
 69  
     private transient ViewDictionaryService viewDictionaryService;
 70  
 
 71  
     /**
 72  
      * Uses reflection to find all fields defined on the <code>View</code>
 73  
      * instance that have the <code>RequestParameter</code> annotation (which
 74  
      * indicates the field may be populated by the request). For each field
 75  
      * found, if there is a corresponding key/value pair in the request
 76  
      * parameters, the value is used to populate the field. In addition, any
 77  
      * conditional properties of <code>PropertyReplacers</code> configured for
 78  
      * the field are cleared so that the request parameter value does not get
 79  
      * overridden by the dictionary conditional logic
 80  
      * 
 81  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#populateViewFromRequestParameters(org.kuali.rice.kns.uif.container.View,
 82  
      *      java.util.Map)
 83  
      */
 84  
     @Override
 85  
     public void populateViewFromRequestParameters(View view, Map<String, String> parameters) {
 86  
         // build set of view properties that can be populated
 87  0
         Set<String> fieldNamesToPopulate = new HashSet<String>();
 88  
 
 89  0
         Field[] fields = CloneUtils.getFields(view.getClass(), true);
 90  0
         for (int i = 0; i < fields.length; i++) {
 91  0
             Field field = fields[i];
 92  
 
 93  0
             RequestParameter requestParameter = field.getAnnotation(RequestParameter.class);
 94  0
             if (requestParameter != null) {
 95  
                 // use specified parameter name if given, else use field name
 96  0
                 if (StringUtils.isNotBlank(requestParameter.parameterName())) {
 97  0
                     fieldNamesToPopulate.add(requestParameter.parameterName());
 98  
                 }
 99  
                 else {
 100  0
                     fieldNamesToPopulate.add(field.getName());
 101  
                 }
 102  
             }
 103  
         }
 104  
 
 105  
         // build Map of property replacers by property name
 106  0
         Map<String, Set<PropertyReplacer>> viewPropertyReplacers = new HashMap<String, Set<PropertyReplacer>>();
 107  0
         for (PropertyReplacer replacer : view.getPropertyReplacers()) {
 108  0
             Set<PropertyReplacer> propertyReplacers = new HashSet<PropertyReplacer>();
 109  0
             if (viewPropertyReplacers.containsKey(replacer.getPropertyName())) {
 110  0
                 propertyReplacers = viewPropertyReplacers.get(replacer.getPropertyName());
 111  
             }
 112  0
             propertyReplacers.add(replacer);
 113  
 
 114  0
             viewPropertyReplacers.put(replacer.getPropertyName(), propertyReplacers);
 115  0
         }
 116  
 
 117  
         // build map of view parameter key/values and populate view fields
 118  0
         Map<String, String> viewRequestParameters = new HashMap<String, String>();
 119  0
         for (String fieldToPopulate : fieldNamesToPopulate) {
 120  0
             if (parameters.containsKey(fieldToPopulate)) {
 121  0
                 String fieldValue = parameters.get(fieldToPopulate);
 122  
 
 123  0
                 if (StringUtils.isNotBlank(fieldValue)) {
 124  0
                     viewRequestParameters.put(fieldToPopulate, fieldValue);
 125  0
                     ObjectPropertyUtils.setPropertyValue(view, fieldToPopulate, fieldValue);
 126  
 
 127  
                     // remove any conditional configuration so value is not
 128  
                     // overridden later during the apply model phase
 129  0
                     String conditionalProperty = StringUtils.substring(fieldToPopulate, 0, 1).toLowerCase()
 130  
                             + StringUtils.substring(fieldToPopulate, 1, fieldToPopulate.length());
 131  0
                     conditionalProperty = UifConstants.EL_CONDITIONAL_PROPERTY_PREFIX + conditionalProperty;
 132  0
                     ObjectPropertyUtils.setPropertyValue(view, conditionalProperty, fieldValue, true);
 133  
 
 134  0
                     if (viewPropertyReplacers.containsKey(fieldToPopulate)) {
 135  0
                         Set<PropertyReplacer> propertyReplacers = viewPropertyReplacers.get(fieldToPopulate);
 136  0
                         for (PropertyReplacer replacer : propertyReplacers) {
 137  0
                             view.getPropertyReplacers().remove(replacer);
 138  
                         }
 139  
                     }
 140  
                 }
 141  0
             }
 142  
         }
 143  
 
 144  0
         view.setViewRequestParameters(viewRequestParameters);
 145  0
     }
 146  
 
 147  
     /**
 148  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performInitialization(org.kuali.rice.kns.uif.container.View,
 149  
      *      java.util.Map)
 150  
      */
 151  
     @Override
 152  
     public void performInitialization(View view) {
 153  0
         performComponentInitialization(view, view);
 154  0
     }
 155  
 
 156  
     /**
 157  
      * Performs initialization of a component by these steps:
 158  
      * 
 159  
      * <ul>
 160  
      * <li>For <code>AttributeField</code> instances, set defaults from the data
 161  
      * dictionary.</li>
 162  
      * <li>Invoke the initialize method on the component. Here the component can
 163  
      * setup defaults and do other initialization that is specific to that
 164  
      * component.</li>
 165  
      * <li>Invoke any configured <code>ComponentModifier</code> instances for
 166  
      * the component.</li>
 167  
      * <li>Call the component to get the List of components that are nested
 168  
      * within and recursively call this method to initialize those components.</li>
 169  
      * <li>Call custom initialize hook for service overrides</li>
 170  
      * </ul>
 171  
      * 
 172  
      * <p>
 173  
      * Note the order various initialize points are called, this can sometimes
 174  
      * be an important factor to consider when initializing a component
 175  
      * </p>
 176  
      * 
 177  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performComponentInitialization(org.kuali.rice.kns.uif.container.View,
 178  
      *      org.kuali.rice.kns.uif.core.Component)
 179  
      */
 180  
     public void performComponentInitialization(View view, Component component) {
 181  0
         if (component == null) {
 182  0
             return;
 183  
         }
 184  
 
 185  0
         LOG.debug("Initializing component: " + component.getId() + " with type: " + component.getClass());
 186  
 
 187  
         // invoke component to initialize itself after properties have been set
 188  0
         component.performInitialization(view);
 189  
 
 190  
         // for attribute fields, set defaults from dictionary entry
 191  0
         if (component instanceof AttributeField) {
 192  0
             initializeAttributeFieldFromDataDictionary(view, (AttributeField) component);
 193  
 
 194  
             // add attribute field to the view's index
 195  0
             view.getViewIndex().addAttributeField((AttributeField) component);
 196  
         }
 197  
 
 198  
         // for collection groups set defaults from dictionary entry
 199  0
         if (component instanceof CollectionGroup) {
 200  
             // TODO: initialize from dictionary
 201  
 
 202  
             // add collection group to the view's index
 203  0
             view.getViewIndex().addCollection((CollectionGroup) component);
 204  
         }
 205  
 
 206  
         // invoke component initializers setup to run in the initialize phase
 207  0
         runComponentModifiers(view, component, null, UifConstants.ViewPhases.INITIALIZE);
 208  
 
 209  
         // initialize nested components
 210  0
         for (Component nestedComponent : component.getNestedComponents()) {
 211  0
             performComponentInitialization(view, nestedComponent);
 212  
         }
 213  
 
 214  
         // initialize property replacements (if components)
 215  0
         for (PropertyReplacer replacer : component.getPropertyReplacers()) {
 216  0
             if (Component.class.isAssignableFrom(replacer.getReplacement().getClass())) {
 217  0
                 performComponentInitialization(view, (Component) replacer.getReplacement());
 218  
             }
 219  
         }
 220  
 
 221  
         // invoke initialize service hook
 222  0
         performCustomInitialization(view, component);
 223  0
     }
 224  
 
 225  
     /**
 226  
      * Sets properties of the <code>AttributeField</code> (if blank) to the
 227  
      * corresponding attribute entry in the data dictionary
 228  
      * 
 229  
      * @param view
 230  
      *            - view instance containing the field
 231  
      * @param field
 232  
      *            - field instance to initialize
 233  
      */
 234  
     protected void initializeAttributeFieldFromDataDictionary(View view, AttributeField field) {
 235  
         // determine attribute name and entry within the dictionary to lookup
 236  0
         String dictionaryAttributeName = field.getDictionaryAttributeName();
 237  0
         String dictionaryObjectEntry = field.getDictionaryObjectEntry();
 238  
 
 239  
         // if entry given but not attribute name, use field name as attribute
 240  
         // name
 241  0
         if (StringUtils.isNotBlank(dictionaryObjectEntry) && StringUtils.isBlank(dictionaryAttributeName)) {
 242  0
             dictionaryAttributeName = field.getPropertyName();
 243  
         }
 244  
 
 245  
         // if both dictionary names not given and the field is from a model,
 246  
         // determine class based on the View and use field name as attribute
 247  
         // name
 248  0
         if (StringUtils.isBlank(dictionaryAttributeName) && StringUtils.isBlank(dictionaryObjectEntry)
 249  
                 && !field.getBindingInfo().isBindToForm()) {
 250  0
             dictionaryAttributeName = field.getPropertyName();
 251  0
             Class<?> dictionaryModelClass = getDictionaryModelClass(view, field);
 252  0
             if (dictionaryModelClass != null) {
 253  0
                 dictionaryObjectEntry = dictionaryModelClass.getName();
 254  
             }
 255  
         }
 256  
 
 257  
         // if we were able to find a dictionary attribute and object, call
 258  
         // data dictionary service to get AttributeDefinition
 259  0
         if (StringUtils.isNotBlank(dictionaryAttributeName) && StringUtils.isNotBlank(dictionaryObjectEntry)) {
 260  0
             AttributeDefinition attributeDefinition = getDataDictionaryService().getAttributeDefinition(
 261  
                     dictionaryObjectEntry, dictionaryAttributeName);
 262  0
             if (attributeDefinition != null) {
 263  0
                 field.copyFromAttributeDefinition(attributeDefinition);
 264  
             }
 265  
 
 266  
             // update field with attribute and entry name
 267  0
             field.setDictionaryAttributeName(dictionaryAttributeName);
 268  0
             field.setDictionaryObjectEntry(dictionaryObjectEntry);
 269  
         }
 270  0
     }
 271  
 
 272  
     /**
 273  
      * Determines the dictionary class that is associated with the given
 274  
      * <code>AttributeField</code>
 275  
      * 
 276  
      * @param view
 277  
      *            - view instance for field
 278  
      * @param field
 279  
      *            - field instance to determine dictionary class for
 280  
      * @return Class<?> dictionary class or null if not found
 281  
      */
 282  
     protected Class<?> getDictionaryModelClass(View view, AttributeField field) {
 283  0
         return ViewModelUtils.getParentObjectClassForMetadata(view, field);
 284  
     }
 285  
 
 286  
     /**
 287  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performApplyModel(org.kuali.rice.kns.uif.container.View,
 288  
      *      java.lang.Object)
 289  
      */
 290  
     @Override
 291  
     public void performApplyModel(View view, Object model) {
 292  
         // get action flag and edit modes from authorizer/presentation
 293  
         // controller
 294  0
         invokeAuthorizerPresentationController(view, (UifFormBase) model);
 295  
 
 296  
         // set view context for conditional expressions
 297  0
         setViewContext(view, model);
 298  
 
 299  0
         performComponentApplyModel(view, view, model);
 300  0
     }
 301  
 
 302  
     /**
 303  
      * Invokes the configured <code>PresentationController</code> and
 304  
      * </code>Authorizer</code> for the view to get the exported action flags
 305  
      * and edit modes that can be used in conditional logic
 306  
      * 
 307  
      * @param view
 308  
      *            - view instance that is being built and
 309  
      *            presentation/authorizer pulled for
 310  
      * @param model
 311  
      *            - Object that contains the model data
 312  
      */
 313  
     protected void invokeAuthorizerPresentationController(View view, UifFormBase model) {
 314  0
         PresentationController presentationController = ObjectUtils.newInstance(view.getPresentationControllerClass());
 315  0
         Authorizer authorizer = ObjectUtils.newInstance(view.getAuthorizerClass());
 316  
 
 317  0
         Person user = GlobalVariables.getUserSession().getPerson();
 318  
 
 319  0
         Set<String> actionFlags = presentationController.getActionFlags(model);
 320  0
         actionFlags = authorizer.getActionFlags(model, user, actionFlags);
 321  0
         view.setActionFlags(actionFlags);
 322  
 
 323  0
         Set<String> editModes = presentationController.getEditModes(model);
 324  0
         editModes = authorizer.getEditModes(model, user, editModes);
 325  0
         view.setEditModes(editModes);
 326  0
     }
 327  
 
 328  
     /**
 329  
      * Sets up the view context which will be available to other components
 330  
      * through their context for conditional logic evaluation
 331  
      * 
 332  
      * @param view
 333  
      *            - view instance to set context for
 334  
      * @param model
 335  
      *            - object containing the view data
 336  
      */
 337  
     protected void setViewContext(View view, Object model) {
 338  0
         view.pushObjectToContext(UifConstants.ContextVariableNames.VIEW, view);
 339  
 
 340  0
         Properties properties = KNSServiceLocator.getKualiConfigurationService().getAllProperties();
 341  0
         view.pushObjectToContext(UifConstants.ContextVariableNames.CONFIG_PROPERTIES, properties);
 342  
         
 343  0
         view.pushObjectToContext(UifConstants.ContextVariableNames.CONSTANTS, KNSConstants.class);
 344  
 
 345  
         // evaluate view expressions for further context
 346  0
         for (Entry<String, String> variableExpression : view.getExpressionVariables().entrySet()) {
 347  0
             String variableName = variableExpression.getKey();
 348  0
             Object value = getExpressionEvaluatorService().evaluateExpression(model, view.getContext(),
 349  
                     variableExpression.getValue());
 350  0
             view.pushObjectToContext(variableName, value);
 351  0
         }
 352  0
     }
 353  
 
 354  
     /**
 355  
      * Applies the model data to a component of the View instance
 356  
      * 
 357  
      * <p>
 358  
      * The component is invoked to to apply the model data. Here the component
 359  
      * can generate any additional fields needed or alter the configured fields.
 360  
      * After the component is invoked a hook for custom helper service
 361  
      * processing is invoked. Finally the method is recursively called for all
 362  
      * the component children
 363  
      * </p>
 364  
      * 
 365  
      * @param view
 366  
      *            - view instance the component belongs to
 367  
      * @param component
 368  
      *            - the component instance the model should be applied to
 369  
      * @param model
 370  
      *            - top level object containing the data
 371  
      */
 372  
     protected void performComponentApplyModel(View view, Component component, Object model) {
 373  0
         if (component == null) {
 374  0
             return;
 375  
         }
 376  
 
 377  
         // evaluate properties
 378  0
         component.getContext().putAll(getCommonContext(view, component));
 379  0
         getExpressionEvaluatorService().evaluateObjectProperties(component, model, component.getContext());
 380  
 
 381  0
         if (component instanceof Container) {
 382  0
             LayoutManager layoutManager = ((Container) component).getLayoutManager();
 383  
 
 384  0
             if (layoutManager != null) {
 385  0
                 layoutManager.getContext().putAll(getCommonContext(view, component));
 386  0
                 layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
 387  0
                 layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.MANAGER, layoutManager);
 388  0
                 getExpressionEvaluatorService().evaluateObjectProperties(layoutManager, model,
 389  
                         layoutManager.getContext());
 390  
             }
 391  
         }
 392  
 
 393  
         // invoke component to perform its conditional logic
 394  0
         component.performApplyModel(view, model);
 395  
 
 396  
         // invoke service override hook
 397  0
         performCustomApplyModel(view, component, model);
 398  
 
 399  
         // invoke component modifiers configured to run in the apply model phase
 400  0
         runComponentModifiers(view, component, model, UifConstants.ViewPhases.APPLY_MODEL);
 401  
 
 402  
         // get children and recursively perform conditional logic
 403  0
         for (Component nestedComponent : component.getNestedComponents()) {
 404  0
             if (nestedComponent != null) {
 405  0
                 nestedComponent.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
 406  
             }
 407  
 
 408  0
             performComponentApplyModel(view, nestedComponent, model);
 409  
         }
 410  0
     }
 411  
 
 412  
     /**
 413  
      * Runs any configured <code>ComponentModifiers</code> for the given
 414  
      * component that match the given run phase and who run condition evaluation
 415  
      * succeeds
 416  
      * 
 417  
      * @param view
 418  
      *            - view instance for context
 419  
      * @param component
 420  
      *            - component instance whose modifiers should be run
 421  
      * @param model
 422  
      *            - model object for context
 423  
      * @param runPhase
 424  
      *            - current phase to match on
 425  
      */
 426  
     protected void runComponentModifiers(View view, Component component, Object model, String runPhase) {
 427  0
         for (ComponentModifier modifier : component.getComponentModifiers()) {
 428  
             // check run phase matches
 429  0
             if (StringUtils.equals(modifier.getRunPhase(), runPhase)) {
 430  
                 // check condition (if set) evaluates to true
 431  0
                 boolean runModifier = true;
 432  0
                 if (StringUtils.isNotBlank(modifier.getRunCondition())) {
 433  0
                     Map<String, Object> context = new HashMap<String, Object>();
 434  0
                     context.put(UifConstants.ContextVariableNames.COMPONENT, component);
 435  0
                     context.put(UifConstants.ContextVariableNames.VIEW, view);
 436  
 
 437  0
                     String conditionEvaluation = getExpressionEvaluatorService().evaluateExpressionTemplate(model,
 438  
                             context, modifier.getRunCondition());
 439  0
                     runModifier = Boolean.parseBoolean(conditionEvaluation);
 440  
                 }
 441  
 
 442  0
                 if (runModifier) {
 443  0
                     modifier.performModification(view, component);
 444  
                 }
 445  0
             }
 446  
         }
 447  0
     }
 448  
 
 449  
     /**
 450  
      * Gets global objects for the context map and pushes them to the context
 451  
      * for the component
 452  
      * 
 453  
      * @param view
 454  
      *            - view instance for component
 455  
      * @param component
 456  
      *            - component instance to push context to
 457  
      */
 458  
     protected Map<String, Object> getCommonContext(View view, Component component) {
 459  0
         Map<String, Object> context = new HashMap<String, Object>();
 460  
 
 461  0
         context.putAll(view.getContext());
 462  0
         context.put(UifConstants.ContextVariableNames.COMPONENT, component);
 463  
 
 464  0
         return context;
 465  
     }
 466  
 
 467  
     /**
 468  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#performFinalize(org.kuali.rice.kns.uif.container.View,
 469  
      *      java.lang.Object)
 470  
      */
 471  
     @Override
 472  
     public void performFinalize(View view, Object model) {
 473  0
         performComponentFinalize(view, view, model, null);
 474  0
     }
 475  
 
 476  
     /**
 477  
      * Update state of the given component and does final preparation for
 478  
      * rendering
 479  
      * 
 480  
      * @param view
 481  
      *            - view instance the component belongs to
 482  
      * @param component
 483  
      *            - the component instance that should be updated
 484  
      * @param model
 485  
      *            - top level object containing the data
 486  
      * @param parent
 487  
      *            - Parent component for the component being finalized
 488  
      */
 489  
     protected void performComponentFinalize(View view, Component component, Object model, Component parent) {
 490  0
         if (component == null) {
 491  0
             return;
 492  
         }
 493  
 
 494  
         // invoke component to update its state
 495  0
         component.performFinalize(view, model, parent);
 496  
 
 497  
         // invoke service override hook
 498  0
         performCustomFinalize(view, component, model, parent);
 499  
 
 500  
         // invoke component initializers setup to run in the finalize phase
 501  0
         runComponentModifiers(view, component, model, UifConstants.ViewPhases.FINALIZE);
 502  
 
 503  
         // get components children and recursively update state
 504  0
         for (Component nestedComponent : component.getNestedComponents()) {
 505  0
             performComponentFinalize(view, nestedComponent, model, component);
 506  
         }
 507  0
     }
 508  
 
 509  
     /**
 510  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#processCollectionAddLine(org.kuali.rice.kns.uif.container.View,
 511  
      *      java.lang.Object, java.lang.String)
 512  
      */
 513  
     @Override
 514  
     public void processCollectionAddLine(View view, Object model, String collectionPath) {
 515  
         // get the collection group from the view
 516  0
         CollectionGroup collectionGroup = view.getViewIndex().getCollectionGroupByPath(collectionPath);
 517  0
         if (collectionGroup == null) {
 518  0
             logAndThrowRuntime("Unable to get collection group component for path: " + collectionPath);
 519  
         }
 520  
 
 521  
         // get the collection instance for adding the new line
 522  0
         Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath);
 523  0
         if (collection == null) {
 524  0
             logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath);
 525  
         }
 526  
 
 527  
         // now get the new line we need to add
 528  0
         String addLinePath = collectionGroup.getAddLineBindingInfo().getBindingPath();
 529  0
         Object addLine = ObjectPropertyUtils.getPropertyValue(model, addLinePath);
 530  0
         if (addLine == null) {
 531  0
             logAndThrowRuntime("Add line instance not found for path: " + addLinePath);
 532  
         }
 533  
 
 534  0
         processBeforeAddLine(view, collectionGroup, model, addLine);
 535  
 
 536  
         // validate the line to make sure it is ok to add
 537  0
         boolean isValidLine = performAddLineValidation(view, collectionGroup, model, addLine);
 538  0
         if (isValidLine) {
 539  
             // TODO: should check to see if there is an add line method on the
 540  
             // collection parent and if so call that instead of just adding to
 541  
             // the collection (so that sequence can be set)
 542  0
             collection.add(addLine);
 543  
 
 544  
             // make a new instance for the add line
 545  0
             collectionGroup.initializeNewCollectionLine(view, model, collectionGroup, true);
 546  
         }
 547  
 
 548  0
         processAfterAddLine(view, collectionGroup, model, addLine);
 549  0
     }
 550  
 
 551  
     /**
 552  
      * Performs validation on the new collection line before it is added to the
 553  
      * corresponding collection
 554  
      * 
 555  
      * @param view
 556  
      *            - view instance that the action was taken on
 557  
      * @param collectionGroup
 558  
      *            - collection group component for the collection
 559  
      * @param addLine
 560  
      *            - new line instance to validate
 561  
      * @param model
 562  
      *            - object instance that contain's the views data
 563  
      * @return boolean true if the line is valid and it should be added to the
 564  
      *         collection, false if it was not valid and should not be added to
 565  
      *         the collection
 566  
      */
 567  
     protected boolean performAddLineValidation(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
 568  0
         boolean isValid = true;
 569  
 
 570  
         // TODO: this should invoke rules, sublclasses like the document view
 571  
         // should create the document add line event
 572  
 
 573  0
         return isValid;
 574  
     }
 575  
 
 576  
     /**
 577  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#processCollectionDeleteLine(org.kuali.rice.kns.uif.container.View,
 578  
      *      java.lang.Object, java.lang.String, int)
 579  
      */
 580  
     public void processCollectionDeleteLine(View view, Object model, String collectionPath, int lineIndex) {
 581  
         // get the collection group from the view
 582  0
         CollectionGroup collectionGroup = view.getViewIndex().getCollectionGroupByPath(collectionPath);
 583  0
         if (collectionGroup == null) {
 584  0
             logAndThrowRuntime("Unable to get collection group component for path: " + collectionPath);
 585  
         }
 586  
 
 587  
         // get the collection instance for adding the new line
 588  0
         Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath);
 589  0
         if (collection == null) {
 590  0
             logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath);
 591  
         }
 592  
 
 593  
         // TODO: look into other ways of identifying a line so we can deal with
 594  
         // unordered collections
 595  0
         if (collection instanceof List) {
 596  0
             Object deleteLine = ((List<Object>) collection).get(lineIndex);
 597  
 
 598  
             // validate the delete action is allowed for this line
 599  0
             boolean isValid = performDeleteLineValidation(view, collectionGroup, deleteLine);
 600  0
             if (isValid) {
 601  0
                 ((List<Object>) collection).remove(lineIndex);
 602  
             }
 603  0
         }
 604  
         else {
 605  0
             logAndThrowRuntime("Only List collection implementations are supported for the delete by index method");
 606  
         }
 607  0
     }
 608  
 
 609  
     /**
 610  
      * Performs validation on the collection line before it is removed from the
 611  
      * corresponding collection
 612  
      * 
 613  
      * @param view
 614  
      *            - view instance that the action was taken on
 615  
      * @param collectionGroup
 616  
      *            - collection group component for the collection
 617  
      * @param deleteLine
 618  
      *            - line that will be removed
 619  
      * @return boolean true if the action is allowed and the line should be
 620  
      *         removed, false if the line should not be removed
 621  
      */
 622  
     protected boolean performDeleteLineValidation(View view, CollectionGroup collectionGroup, Object deleteLine) {
 623  0
         boolean isValid = true;
 624  
 
 625  
         // TODO: this should invoke rules, sublclasses like the document view
 626  
         // should create the document delete line event
 627  
 
 628  0
         return isValid;
 629  
     }
 630  
 
 631  
     /**
 632  
      * Finds the <code>Inquirable</code> configured for the given data object
 633  
      * class and delegates to it for building the inquiry URL
 634  
      * 
 635  
      * @see org.kuali.rice.kns.uif.service.ViewHelperService#buildInquiryLink(java.lang.Object,
 636  
      *      java.lang.String, org.kuali.rice.kns.uif.widget.Inquiry)
 637  
      */
 638  
     public void buildInquiryLink(Object dataObject, String propertyName, Inquiry inquiry) {
 639  0
         Inquirable inquirable = getViewDictionaryService().getInquirable(dataObject.getClass(),
 640  
                 inquiry.getViewName());
 641  0
         if (inquirable != null) {
 642  0
             inquirable.buildInquirableLink(dataObject, propertyName, inquiry);
 643  
         }
 644  
         else {
 645  
             // inquirable not found, no inquiry link can be set
 646  0
             inquiry.setRender(false);
 647  
         }
 648  0
     }
 649  
 
 650  
     /**
 651  
      * Hook for service overrides to perform custom initialization on the
 652  
      * component
 653  
      * 
 654  
      * @param view
 655  
      *            - view instance containing the component
 656  
      * @param component
 657  
      *            - component instance to initialize
 658  
      */
 659  
     protected void performCustomInitialization(View view, Component component) {
 660  
 
 661  0
     }
 662  
 
 663  
     /**
 664  
      * Hook for service overrides to perform custom apply model logic on the
 665  
      * component
 666  
      * 
 667  
      * @param view
 668  
      *            - view instance containing the component
 669  
      * @param component
 670  
      *            - component instance to apply model to
 671  
      * @param model
 672  
      *            - Top level object containing the data (could be the form or a
 673  
      *            top level business object, dto)
 674  
      */
 675  
     protected void performCustomApplyModel(View view, Component component, Object model) {
 676  
 
 677  0
     }
 678  
 
 679  
     /**
 680  
      * Hook for service overrides to perform custom component finalization
 681  
      * 
 682  
      * @param view
 683  
      *            - view instance containing the component
 684  
      * @param component
 685  
      *            - component instance to update
 686  
      * @param model
 687  
      *            - Top level object containing the data
 688  
      * @param parent
 689  
      *            - Parent component for the component being finalized
 690  
      */
 691  
     protected void performCustomFinalize(View view, Component component, Object model, Component parent) {
 692  
 
 693  0
     }
 694  
 
 695  
     /**
 696  
      * Hook for service overrides to process the new collection line before it
 697  
      * is added to the collection
 698  
      * 
 699  
      * @param view
 700  
      *            - view instance that is being presented (the action was taken
 701  
      *            on)
 702  
      * @param collectionGroup
 703  
      *            - collection group component for the collection the line will
 704  
      *            be added to
 705  
      * @param model
 706  
      *            - object instance that contain's the views data
 707  
      * @param addLine
 708  
      *            - the new line instance to be processed
 709  
      */
 710  
     protected void processBeforeAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
 711  
 
 712  0
     }
 713  
 
 714  
     /**
 715  
      * Hook for service overrides to process the new collection line after it
 716  
      * has been added to the collection
 717  
      * 
 718  
      * @param view
 719  
      *            - view instance that is being presented (the action was taken
 720  
      *            on)
 721  
      * @param collectionGroup
 722  
      *            - collection group component for the collection the line that
 723  
      *            was added
 724  
      * @param model
 725  
      *            - object instance that contain's the views data
 726  
      * @param addLine
 727  
      *            - the new line that was added
 728  
      */
 729  
     protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
 730  
 
 731  0
     }
 732  
 
 733  
     protected void logAndThrowRuntime(String message) {
 734  0
         LOG.error(message);
 735  0
         throw new RuntimeException(message);
 736  
     }
 737  
 
 738  
     protected DataDictionaryService getDataDictionaryService() {
 739  0
         if (this.dataDictionaryService == null) {
 740  0
             this.dataDictionaryService = KNSServiceLocatorWeb.getDataDictionaryService();
 741  
         }
 742  
 
 743  0
         return this.dataDictionaryService;
 744  
     }
 745  
 
 746  
     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
 747  0
         this.dataDictionaryService = dataDictionaryService;
 748  0
     }
 749  
 
 750  
     protected ExpressionEvaluatorService getExpressionEvaluatorService() {
 751  0
         if (this.expressionEvaluatorService == null) {
 752  0
             this.expressionEvaluatorService = KNSServiceLocatorWeb.getExpressionEvaluatorService();
 753  
         }
 754  
 
 755  0
         return this.expressionEvaluatorService;
 756  
     }
 757  
 
 758  
     public void setExpressionEvaluatorService(ExpressionEvaluatorService expressionEvaluatorService) {
 759  0
         this.expressionEvaluatorService = expressionEvaluatorService;
 760  0
     }
 761  
 
 762  
     public ViewDictionaryService getViewDictionaryService() {
 763  0
         if (this.viewDictionaryService == null) {
 764  0
             this.viewDictionaryService = KNSServiceLocatorWeb.getViewDictionaryService();
 765  
         }
 766  0
         return this.viewDictionaryService;
 767  
     }
 768  
 
 769  
     public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) {
 770  0
         this.viewDictionaryService = viewDictionaryService;
 771  0
     }
 772  
 
 773  
 }