Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CompareFieldCreateModifier |
|
| 2.769230769230769;2.769 |
1 | /* | |
2 | * Copyright 2011 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.modifier; | |
12 | ||
13 | import org.apache.commons.lang.StringUtils; | |
14 | import org.apache.log4j.Logger; | |
15 | import org.kuali.rice.krad.uif.UifPropertyPaths; | |
16 | import org.kuali.rice.krad.uif.container.Group; | |
17 | import org.kuali.rice.krad.uif.container.View; | |
18 | import org.kuali.rice.krad.uif.core.Component; | |
19 | import org.kuali.rice.krad.uif.field.AttributeField; | |
20 | import org.kuali.rice.krad.uif.field.HeaderField; | |
21 | import org.kuali.rice.krad.uif.util.ComponentUtils; | |
22 | import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; | |
23 | ||
24 | import java.util.ArrayList; | |
25 | import java.util.HashSet; | |
26 | import java.util.List; | |
27 | import java.util.Set; | |
28 | ||
29 | /** | |
30 | * Generates <code>Field</code> instances to produce a comparison view among | |
31 | * objects of the same type | |
32 | * | |
33 | * <p> | |
34 | * Modifier is initialized with a List of <code>ComparableInfo</code> instances. | |
35 | * For each comparable info, a copy of the configured group field is made and | |
36 | * adjusted to the binding object path for the comparable. The comparison fields | |
37 | * are ordered based on the configured order property of the comparable. In | |
38 | * addition, a <code>HeaderField<code> can be generated to label each group | |
39 | * of comparison fields. | |
40 | * </p> | |
41 | * | |
42 | * @author Kuali Rice Team (rice.collab@kuali.org) | |
43 | */ | |
44 | public class CompareFieldCreateModifier extends ComponentModifierBase { | |
45 | 0 | private static final Logger LOG = Logger.getLogger(CompareFieldCreateModifier.class); |
46 | ||
47 | private static final long serialVersionUID = -6285531580512330188L; | |
48 | ||
49 | private int defaultOrderSequence; | |
50 | private boolean generateCompareHeaders; | |
51 | ||
52 | private HeaderField headerFieldPrototype; | |
53 | private List<ComparableInfo> comparables; | |
54 | ||
55 | 0 | public CompareFieldCreateModifier() { |
56 | 0 | defaultOrderSequence = 1; |
57 | 0 | generateCompareHeaders = true; |
58 | ||
59 | 0 | comparables = new ArrayList<ComparableInfo>(); |
60 | 0 | } |
61 | ||
62 | /** | |
63 | * Generates the comparison fields | |
64 | * | |
65 | * <p> | |
66 | * First the configured List of <code>ComparableInfo</code> instances are | |
67 | * sorted based on their order property. Then if generateCompareHeaders is | |
68 | * set to true, a <code>HeaderField</code> is created for each comparable | |
69 | * using the headerFieldPrototype and the headerText given by the | |
70 | * comparable. Finally for each field configured on the <code>Group</code>, | |
71 | * a corresponding comparison field is generated for each comparable and | |
72 | * adjusted to the binding object path given by the comparable in addition | |
73 | * to suffixing the id and setting the readOnly property | |
74 | * </p> | |
75 | * | |
76 | * @see org.kuali.rice.krad.uif.modifier.ComponentModifier#performModification(org.kuali.rice.krad.uif.container.View, | |
77 | * java.lang.Object, org.kuali.rice.krad.uif.core.Component) | |
78 | */ | |
79 | @SuppressWarnings("unchecked") | |
80 | @Override | |
81 | public void performModification(View view, Object model, Component component) { | |
82 | 0 | if ((component != null) && !(component instanceof Group)) { |
83 | 0 | throw new IllegalArgumentException("Compare field initializer only support Group components, found type: " |
84 | + component.getClass()); | |
85 | } | |
86 | ||
87 | 0 | if (component == null) { |
88 | 0 | return; |
89 | } | |
90 | ||
91 | // list to hold the generated compare items | |
92 | 0 | List<Component> comparisonItems = new ArrayList<Component>(); |
93 | ||
94 | // sort comparables by their order property | |
95 | 0 | List<ComparableInfo> groupComparables = |
96 | (List<ComparableInfo>) ComponentUtils.sort(comparables, defaultOrderSequence); | |
97 | ||
98 | // generate compare header | |
99 | 0 | if (isGenerateCompareHeaders()) { |
100 | 0 | for (ComparableInfo comparable : groupComparables) { |
101 | 0 | HeaderField compareHeaderField = ComponentUtils.copy(headerFieldPrototype, comparable.getIdSuffix()); |
102 | 0 | compareHeaderField.setHeaderText(comparable.getHeaderText()); |
103 | ||
104 | 0 | comparisonItems.add(compareHeaderField); |
105 | 0 | } |
106 | } | |
107 | ||
108 | // find the comparable to use for comparing value changes (if | |
109 | // configured) | |
110 | 0 | boolean performValueChangeComparison = false; |
111 | 0 | String compareValueObjectBindingPath = null; |
112 | 0 | for (ComparableInfo comparable : groupComparables) { |
113 | 0 | if (comparable.isCompareToForValueChange()) { |
114 | 0 | performValueChangeComparison = true; |
115 | 0 | compareValueObjectBindingPath = comparable.getBindingObjectPath(); |
116 | } | |
117 | } | |
118 | ||
119 | // generate the compare items from the configured group | |
120 | 0 | Group group = (Group) component; |
121 | 0 | for (Component item : group.getItems()) { |
122 | 0 | int defaultSuffix = 0; |
123 | 0 | for (ComparableInfo comparable : groupComparables) { |
124 | 0 | String idSuffix = comparable.getIdSuffix(); |
125 | 0 | if (StringUtils.isBlank(idSuffix)) { |
126 | 0 | idSuffix = "_c" + defaultSuffix; |
127 | } | |
128 | ||
129 | 0 | Component compareItem = ComponentUtils.copy(item, idSuffix); |
130 | ||
131 | 0 | ComponentUtils.setComponentPropertyDeep(compareItem, UifPropertyPaths.BIND_OBJECT_PATH, |
132 | comparable.getBindingObjectPath()); | |
133 | 0 | if (comparable.isReadOnly()) { |
134 | 0 | compareItem.setReadOnly(true); |
135 | 0 | compareItem.setConditionalReadOnly(""); |
136 | } | |
137 | ||
138 | // do value comparison | |
139 | 0 | if (performValueChangeComparison && comparable.isHighlightValueChange() && |
140 | !comparable.isCompareToForValueChange()) { | |
141 | 0 | performValueComparison(group, compareItem, model, compareValueObjectBindingPath); |
142 | } | |
143 | ||
144 | 0 | comparisonItems.add(compareItem); |
145 | 0 | defaultSuffix++; |
146 | 0 | } |
147 | 0 | } |
148 | ||
149 | // update the group's list of components | |
150 | 0 | group.setItems(comparisonItems); |
151 | 0 | } |
152 | ||
153 | /** | |
154 | * For each attribute field in the compare item, retrieves the field value and compares against the value for the | |
155 | * main comparable. If the value is different, adds script to the field on ready event to add the change icon to | |
156 | * the field and the containing group header | |
157 | * | |
158 | * @param group - group that contains the item and whose header will be highlighted for changes | |
159 | * @param compareItem - the compare item being generated and to pull attribute fields from | |
160 | * @param model - object containing the data | |
161 | * @param compareValueObjectBindingPath - object path for the comparison item | |
162 | */ | |
163 | protected void performValueComparison(Group group, Component compareItem, Object model, | |
164 | String compareValueObjectBindingPath) { | |
165 | // get any attribute fields for the item so we can compare the values | |
166 | 0 | List<AttributeField> itemFields = ComponentUtils.getComponentsOfTypeDeep(compareItem, AttributeField.class); |
167 | 0 | for (AttributeField field : itemFields) { |
168 | 0 | String fieldBindingPath = field.getBindingInfo().getBindingPath(); |
169 | 0 | Object fieldValue = ObjectPropertyUtils.getPropertyValue(model, fieldBindingPath); |
170 | ||
171 | 0 | String compareBindingPath = StringUtils |
172 | .replaceOnce(fieldBindingPath, field.getBindingInfo().getBindingObjectPath(), | |
173 | compareValueObjectBindingPath); | |
174 | 0 | Object compareValue = ObjectPropertyUtils.getPropertyValue(model, compareBindingPath); |
175 | ||
176 | 0 | boolean valueChanged = false; |
177 | 0 | if (!((fieldValue == null) && (compareValue == null))) { |
178 | // if one is null then value changed | |
179 | 0 | if ((fieldValue == null) || (compareValue == null)) { |
180 | 0 | valueChanged = true; |
181 | } else { | |
182 | // both not null, compare values | |
183 | 0 | valueChanged = !fieldValue.equals(compareValue); |
184 | } | |
185 | } | |
186 | ||
187 | // add script to show change icon | |
188 | 0 | if (valueChanged) { |
189 | 0 | String onReadyScript = "showChangeIcon('" + field.getId() + "');"; |
190 | ||
191 | // add icon to group header | |
192 | 0 | Component headerField = group.getHeader(); |
193 | 0 | onReadyScript += "showChangeIconOnHeader('" + headerField.getId() + "');"; |
194 | ||
195 | 0 | field.setOnDocumentReadyScript(onReadyScript); |
196 | } | |
197 | ||
198 | // TODO: add script for value changed? | |
199 | 0 | } |
200 | 0 | } |
201 | ||
202 | ||
203 | /** | |
204 | * Generates an id suffix for the comparable item | |
205 | * | |
206 | * <p> | |
207 | * If the idSuffix to use if configured on the <code>ComparableInfo</code> | |
208 | * it will be used, else the given integer index will be used with an | |
209 | * underscore | |
210 | * </p> | |
211 | * | |
212 | * @param comparable | |
213 | * - comparable info to check for id suffix | |
214 | * @param index | |
215 | * - sequence integer | |
216 | * @return String id suffix | |
217 | * @see org.kuali.rice.krad.uif.modifier.ComparableInfo.getIdSuffix() | |
218 | */ | |
219 | protected String getIdSuffix(ComparableInfo comparable, int index) { | |
220 | 0 | String idSuffix = comparable.getIdSuffix(); |
221 | 0 | if (StringUtils.isBlank(idSuffix)) { |
222 | 0 | idSuffix = "_" + index; |
223 | } | |
224 | ||
225 | 0 | return idSuffix; |
226 | } | |
227 | ||
228 | /** | |
229 | * @see org.kuali.rice.krad.uif.modifier.ComponentModifier#getSupportedComponents() | |
230 | */ | |
231 | @Override | |
232 | public Set<Class<? extends Component>> getSupportedComponents() { | |
233 | 0 | Set<Class<? extends Component>> components = new HashSet<Class<? extends Component>>(); |
234 | 0 | components.add(Group.class); |
235 | ||
236 | 0 | return components; |
237 | } | |
238 | ||
239 | /** | |
240 | * Indicates the starting integer sequence value to use for | |
241 | * <code>ComparableInfo</code> instances that do not have the order property | |
242 | * set | |
243 | * | |
244 | * @return int default sequence starting value | |
245 | */ | |
246 | public int getDefaultOrderSequence() { | |
247 | 0 | return this.defaultOrderSequence; |
248 | } | |
249 | ||
250 | /** | |
251 | * Setter for the default sequence starting value | |
252 | * | |
253 | * @param defaultOrderSequence | |
254 | */ | |
255 | public void setDefaultOrderSequence(int defaultOrderSequence) { | |
256 | 0 | this.defaultOrderSequence = defaultOrderSequence; |
257 | 0 | } |
258 | ||
259 | /** | |
260 | * Indicates whether a <code>HeaderField</code> should be created for each | |
261 | * group of comparison fields | |
262 | * | |
263 | * <p> | |
264 | * If set to true, for each group of comparison fields a header field will | |
265 | * be created using the headerFieldPrototype configured on the modifier with | |
266 | * the headerText property of the comparable | |
267 | * </p> | |
268 | * | |
269 | * @return boolean true if the headers should be created, false if no | |
270 | * headers should be created | |
271 | */ | |
272 | public boolean isGenerateCompareHeaders() { | |
273 | 0 | return this.generateCompareHeaders; |
274 | } | |
275 | ||
276 | /** | |
277 | * Setter for the generate comparison headers indicator | |
278 | * | |
279 | * @param generateCompareHeaders | |
280 | */ | |
281 | public void setGenerateCompareHeaders(boolean generateCompareHeaders) { | |
282 | 0 | this.generateCompareHeaders = generateCompareHeaders; |
283 | 0 | } |
284 | ||
285 | /** | |
286 | * Prototype instance to use for creating the <code>HeaderField</code> for | |
287 | * each group of comparison fields (if generateCompareHeaders is true) | |
288 | * | |
289 | * @return HeaderField header field prototype | |
290 | */ | |
291 | public HeaderField getHeaderFieldPrototype() { | |
292 | 0 | return this.headerFieldPrototype; |
293 | } | |
294 | ||
295 | /** | |
296 | * Setter for the header field prototype | |
297 | * | |
298 | * @param headerFieldPrototype | |
299 | */ | |
300 | public void setHeaderFieldPrototype(HeaderField headerFieldPrototype) { | |
301 | 0 | this.headerFieldPrototype = headerFieldPrototype; |
302 | 0 | } |
303 | ||
304 | /** | |
305 | * List of <code>ComparableInfo</code> instances the compare fields should | |
306 | * be generated for | |
307 | * | |
308 | * <p> | |
309 | * For each comparable, a copy of the fields configured for the | |
310 | * <code>Group</code> will be created for the comparison view | |
311 | * </p> | |
312 | * | |
313 | * @return List<ComparableInfo> comparables to generate fields for | |
314 | */ | |
315 | public List<ComparableInfo> getComparables() { | |
316 | 0 | return this.comparables; |
317 | } | |
318 | ||
319 | /** | |
320 | * Setter for the list of comparable info instances | |
321 | * | |
322 | * @param comparables | |
323 | */ | |
324 | public void setComparables(List<ComparableInfo> comparables) { | |
325 | 0 | this.comparables = comparables; |
326 | 0 | } |
327 | ||
328 | } |