Coverage Report - org.kuali.rice.krad.uif.field.RemoteFieldsHolder
 
Classes in this File Line Coverage Branch Coverage Complexity
RemoteFieldsHolder
0%
0/51
0%
0/20
2.182
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krad.uif.field;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.kuali.rice.core.api.uif.RemotableAttributeField;
 20  
 import org.kuali.rice.krad.uif.component.BindingInfo;
 21  
 import org.kuali.rice.krad.uif.component.ComponentBase;
 22  
 import org.kuali.rice.krad.uif.component.DataBinding;
 23  
 import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
 24  
 import org.kuali.rice.krad.uif.container.Container;
 25  
 import org.kuali.rice.krad.uif.util.CloneUtils;
 26  
 import org.kuali.rice.krad.uif.util.ComponentFactory;
 27  
 import org.kuali.rice.krad.uif.view.View;
 28  
 
 29  
 import java.util.ArrayList;
 30  
 import java.util.List;
 31  
 
 32  
 /**
 33  
  * A placeholder in the configuration for a <code>Container</code> list of items that will be invoked to
 34  
  * retrieve a list of {@link RemotableAttributeField} instances which will then be inserted into the containers
 35  
  * list at the position of the holder
 36  
  *
 37  
  * <p>
 38  
  * Since remotable fields are dynamic by nature, the individual fields cannot be configured initially with the
 39  
  * container. Further more the properties for the field are constructed with code. This gives the ability to specify
 40  
  * where that list of fields should be placed, along with configured on how to retrieve the remote fields.
 41  
  * </p>
 42  
  *
 43  
  * <p>
 44  
  * The fetching properties are used to configure what method to invoke that will return the list of remotable fields.
 45  
  * Specifying the {@link #getFetchingMethodToCall()} only assumes the method is on the view helper service for the
 46  
  * contained view. For invoking other classes, such as services or static classes, use {@link
 47  
  * #getFetchingMethodInvoker()}
 48  
  * </p>
 49  
  *
 50  
  * <p>
 51  
  * The list of remotable fields should bind to a Map property on the model. The {@link #getPropertyName()} and
 52  
  * {@link #getBindingInfo()} properties specify the path to this property. The property names configured on the
 53  
  * returned fields are assumed to be keys in that above configured map, with the corresponding map value giving the
 54  
  * actual model value for the remote field.
 55  
  * </p>
 56  
  *
 57  
  * <p>
 58  
  * e.g. configuration
 59  
  * {@code
 60  
  *    <property name="items">
 61  
       <list>
 62  
         <bean parent="RemoteFieldsHolder" p:propertyName="remoteFieldValuesMap"
 63  
               p:fetchingMethodToCall="retrieveRemoteFields"/>
 64  
  *    ...
 65  
  * }
 66  
  *
 67  
  * This example will invoke a method named 'retrieveRemoteFields' on the view helper service, which should return
 68  
  * a list of {@link RemotableAttributeField} instances. The view, model instance, and parent container will be sent
 69  
  * to the method as arguments.
 70  
  *
 71  
  * The returned fields will be translated to {@link InputField} instances that bind to a map property named
 72  
  * 'remoteFieldValuesMap' on the model.
 73  
  * </p>
 74  
  *
 75  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 76  
  */
 77  
 public class RemoteFieldsHolder extends ComponentBase implements DataBinding {
 78  
     private static final long serialVersionUID = -8493923312021633727L;
 79  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RemoteFieldsHolder.class);
 80  
 
 81  
     private String propertyName;
 82  
     private BindingInfo bindingInfo;
 83  
 
 84  
     private String fetchingMethodToCall;
 85  
     private MethodInvokerConfig fetchingMethodInvoker;
 86  
 
 87  
     public RemoteFieldsHolder() {
 88  0
         super();
 89  0
     }
 90  
 
 91  
     /**
 92  
      * Invokes the configured fetching method to retrieve a list of remotable fields, then invoked the
 93  
      * {@code ComponentFactory} to translate the fields, and finally sets up the binding for the attribute fields
 94  
      *
 95  
      * @param view - view instance the container belongs to, sent to the fetching method
 96  
      * @param model - object containing the view data, sent to the fetching method
 97  
      * @param parent - container instance that holder is configured for, sent to the fetching method
 98  
      * @return List<AttributeField> list of attribute fields that should be placed into container, if no remotable
 99  
      * fields were returned from the fetching method the list will be empty
 100  
      */
 101  
     public List<InputField> fetchAndTranslateRemoteFields(View view, Object model, Container parent) {
 102  0
         if (StringUtils.isBlank(fetchingMethodToCall) && (fetchingMethodInvoker == null)) {
 103  0
             throw new RuntimeException("");
 104  
         }
 105  
 
 106  0
         if (fetchingMethodInvoker == null) {
 107  0
             fetchingMethodInvoker = new MethodInvokerConfig();
 108  
         }
 109  
 
 110  
         // if method not set on invoker, use fetchingMethodToCall, note staticMethod could be set(don't know since
 111  
         // there is not a getter), if so it will override the target method in prepare
 112  0
         if (StringUtils.isBlank(fetchingMethodInvoker.getTargetMethod())) {
 113  0
             fetchingMethodInvoker.setTargetMethod(fetchingMethodToCall);
 114  
         }
 115  
 
 116  
         // if target class or object not set, use view helper service
 117  0
         if ((fetchingMethodInvoker.getTargetClass() == null) && (fetchingMethodInvoker.getTargetObject() == null)) {
 118  0
             fetchingMethodInvoker.setTargetObject(view.getViewHelperService());
 119  
         }
 120  
 
 121  0
         Object[] arguments = new Object[3];
 122  0
         arguments[0] = view;
 123  0
         arguments[1] = model;
 124  0
         arguments[2] = parent;
 125  0
         fetchingMethodInvoker.setArguments(arguments);
 126  
 
 127  
         // invoke method
 128  0
         List<RemotableAttributeField> remotableFields = null;
 129  
         try {
 130  0
             LOG.debug("Invoking fetching method: " + fetchingMethodInvoker.getTargetMethod());
 131  0
             fetchingMethodInvoker.prepare();
 132  
 
 133  0
             remotableFields = (List<RemotableAttributeField>) fetchingMethodInvoker.invoke();
 134  
 
 135  0
         } catch (Exception e) {
 136  0
             LOG.error("Error invoking fetching method", e);
 137  0
             throw new RuntimeException("Error invoking fetching method", e);
 138  0
         }
 139  
 
 140  
         // do translation
 141  0
         List<InputField> attributeFields = new ArrayList<InputField>();
 142  0
         if ((remotableFields != null) && !remotableFields.isEmpty()) {
 143  0
             attributeFields = ComponentFactory.translateRemotableFields(remotableFields);
 144  
         }
 145  
 
 146  
         // set binding info on the translated fields
 147  0
         if (bindingInfo == null) {
 148  0
             bindingInfo = new BindingInfo();
 149  
         }
 150  
 
 151  
         // property name should point to a Map that holds attribute name/value pairs
 152  0
         bindingInfo.addToBindByNamePrefix(propertyName);
 153  0
         bindingInfo.setBindToMap(true);
 154  
 
 155  0
         for (InputField field : attributeFields) {
 156  0
             BindingInfo fieldBindingInfo = CloneUtils.deepClone(bindingInfo);
 157  0
             fieldBindingInfo.setDefaults(view, field.getPropertyName());
 158  0
             field.setBindingInfo(fieldBindingInfo);
 159  
 
 160  0
             view.assignComponentIds(field);
 161  0
         }
 162  
 
 163  0
         return attributeFields;
 164  
     }
 165  
 
 166  
     @Override
 167  
     public String getComponentTypeName() {
 168  0
         return "RemoteFieldsHolder";
 169  
     }
 170  
 
 171  
     /**
 172  
      * Path to the Map property that the translated fields bind to
 173  
      *
 174  
      * <p>
 175  
      * It is assumed this property points to a Map where the property names on the returned remotable fields
 176  
      * are keys in that map, with the corresponding map value giving the model value for the field
 177  
      * </p>
 178  
      *
 179  
      * @return String path to property on model
 180  
      */
 181  
     public String getPropertyName() {
 182  0
         return propertyName;
 183  
     }
 184  
 
 185  
     /**
 186  
      * Setter for the property name that points to the binding Map
 187  
      *
 188  
      * @param propertyName
 189  
      */
 190  
     public void setPropertyName(String propertyName) {
 191  0
         this.propertyName = propertyName;
 192  0
     }
 193  
 
 194  
     /**
 195  
      * Can be used to for more complex binding paths
 196  
      *
 197  
      * <p>
 198  
      * Generally not necessary to set on a field level, any default object path or binding prefixes set
 199  
      * on the view or container will be inherited
 200  
      * </p>
 201  
      *
 202  
      * @return BindingInfo instance containing binding information for the Map property
 203  
      */
 204  
     public BindingInfo getBindingInfo() {
 205  0
         return bindingInfo;
 206  
     }
 207  
 
 208  
     /**
 209  
      * Setter for the Map property binding info instance
 210  
      *
 211  
      * @param bindingInfo
 212  
      */
 213  
     public void setBindingInfo(BindingInfo bindingInfo) {
 214  0
         this.bindingInfo = bindingInfo;
 215  0
     }
 216  
 
 217  
     /**
 218  
      * Name of the method to invoke for retrieving the list of remotable fields
 219  
      *
 220  
      * <p>
 221  
      * When only the fetching method to call is configured it is assumed to be a valid method on the view
 222  
      * helper service for the containing view. The method name must accept the view, model object, and parent
 223  
      * container as arguments, and return a list of {@link RemotableAttributeField} instances.
 224  
      * </p>
 225  
      *
 226  
      * <p>
 227  
      * For invoking the method on classes other than the view helper service, see {@link #getFetchingMethodInvoker()}
 228  
      * </p>
 229  
      *
 230  
      * @return String name of method to invoke for fetching remote fields
 231  
      */
 232  
     public String getFetchingMethodToCall() {
 233  0
         return fetchingMethodToCall;
 234  
     }
 235  
 
 236  
     /**
 237  
      * Setter for the fetching method to call
 238  
      *
 239  
      * @param fetchingMethodToCall
 240  
      */
 241  
     public void setFetchingMethodToCall(String fetchingMethodToCall) {
 242  0
         this.fetchingMethodToCall = fetchingMethodToCall;
 243  0
     }
 244  
 
 245  
     /**
 246  
      * Configuration for the method to invoke for retrieving the list of remotable fields
 247  
      *
 248  
      * <p>
 249  
      * Through the method invoker config, a service or static class can be configured along with the
 250  
      * method name that will be invoked. The method name must accept the view, model object, and parent
 251  
      * container as arguments, and return a list of {@link RemotableAttributeField} instances.
 252  
      * </p>
 253  
      *
 254  
      * <p>
 255  
      * Note the {@link org.kuali.rice.krad.uif.component.MethodInvokerConfig#getTargetMethod()} property can
 256  
      * be configured, or the {@link #getFetchingMethodToCall()}. In the case of both configurations, the target
 257  
      * method on the method invoker config will be used
 258  
      * </p>
 259  
      *
 260  
      * @return MethodInvokerConfig instance containing method configuration
 261  
      */
 262  
     public MethodInvokerConfig getFetchingMethodInvoker() {
 263  0
         return fetchingMethodInvoker;
 264  
     }
 265  
 
 266  
     /**
 267  
      * Setter for the fetching method to invoke configuration
 268  
      *
 269  
      * @param fetchingMethodInvoker
 270  
      */
 271  
     public void setFetchingMethodInvoker(MethodInvokerConfig fetchingMethodInvoker) {
 272  0
         this.fetchingMethodInvoker = fetchingMethodInvoker;
 273  0
     }
 274  
 }