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 | } |