| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| RemoteFieldsHolder |
|
| 2.1818181818181817;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 | } |