1 /**
2 * Copyright 2005-2012 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.container;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.kuali.rice.krad.uif.component.Component;
20 import org.kuali.rice.krad.uif.component.ComponentBase;
21 import org.kuali.rice.krad.uif.field.FieldGroup;
22 import org.kuali.rice.krad.uif.field.InputField;
23 import org.kuali.rice.krad.uif.field.ErrorsField;
24 import org.kuali.rice.krad.uif.field.HeaderField;
25 import org.kuali.rice.krad.uif.field.MessageField;
26 import org.kuali.rice.krad.uif.layout.LayoutManager;
27 import org.kuali.rice.krad.uif.util.ComponentUtils;
28 import org.kuali.rice.krad.uif.view.View;
29 import org.kuali.rice.krad.uif.widget.Help;
30
31 import java.util.ArrayList;
32 import java.util.List;
33
34 /**
35 * Base <code>Container</code> implementation which container implementations
36 * can extend
37 *
38 * <p>
39 * Provides properties for the basic <code>Container</code> functionality in
40 * addition to default implementation of the lifecycle methods including some
41 * setup of the header, items list, and layout manager
42 * </p>
43 *
44 * @author Kuali Rice Team (rice.collab@kuali.org)
45 */
46 public abstract class ContainerBase extends ComponentBase implements Container {
47 private static final long serialVersionUID = -4182226230601746657L;
48
49 private int itemOrderingSequence;
50
51 private String additionalMessageKeys;
52 private ErrorsField errorsField;
53
54 private Help help;
55 private LayoutManager layoutManager;
56
57 private HeaderField header;
58 private Group footer;
59
60 private String instructionalText;
61 private MessageField instructionalMessageField;
62
63 private boolean fieldContainer;
64
65 /**
66 * Default Constructor
67 */
68 public ContainerBase() {
69 itemOrderingSequence = 1;
70 }
71
72 /**
73 * The following initialization is performed:
74 *
75 * <ul>
76 * <li>Sorts the containers list of components</li>
77 * <li>Initializes LayoutManager</li>
78 * </ul>
79 *
80 * @see org.kuali.rice.krad.uif.component.ComponentBase#performInitialization(org.kuali.rice.krad.uif.view.View, java.lang.Object)
81 */
82 @SuppressWarnings("unchecked")
83 @Override
84 public void performInitialization(View view, Object model) {
85 super.performInitialization(view, model);
86
87 // sort items list by the order property
88 List<? extends Component> sortedItems = (List<? extends Component>) ComponentUtils.sort(getItems(),
89 itemOrderingSequence);
90 setItems(sortedItems);
91
92 if (layoutManager != null) {
93 layoutManager.performInitialization(view, model, this);
94 }
95 }
96
97 /**
98 * @see org.kuali.rice.krad.uif.component.ComponentBase#performApplyModel(org.kuali.rice.krad.uif.view.View,
99 * java.lang.Object, org.kuali.rice.krad.uif.component.Component)
100 */
101 @Override
102 public void performApplyModel(View view, Object model, Component parent) {
103 super.performApplyModel(view, model, parent);
104
105 if (layoutManager != null) {
106 layoutManager.performApplyModel(view, model, this);
107 }
108 }
109
110 /**
111 * The following finalization is performed:
112 *
113 * <ul>
114 * <li>Sets the headerText of the header Group if it is blank</li>
115 * <li>Set the messageText of the summary MessageField if it is blank</li>
116 * <li>Finalizes LayoutManager</li>
117 * </ul>
118 *
119 * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
120 * java.lang.Object, org.kuali.rice.krad.uif.component.Component)
121 */
122 @Override
123 public void performFinalize(View view, Object model, Component parent) {
124 super.performFinalize(view, model, parent);
125
126 // if header title not given, use the container title
127 if (header != null && StringUtils.isBlank(header.getHeaderText())) {
128 header.setHeaderText(this.getTitle());
129 }
130
131 // setup summary message field if necessary
132 if (instructionalMessageField != null && StringUtils.isBlank(instructionalMessageField.getMessageText())) {
133 instructionalMessageField.setMessageText(instructionalText);
134 }
135
136 if (layoutManager != null) {
137 layoutManager.performFinalize(view, model, this);
138 }
139 }
140
141 /**
142 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
143 */
144 @Override
145 public List<Component> getComponentsForLifecycle() {
146 List<Component> components = super.getComponentsForLifecycle();
147
148 components.add(header);
149 components.add(footer);
150 components.add(errorsField);
151 components.add(help);
152 components.add(instructionalMessageField);
153
154 for (Component component : getItems()) {
155 components.add(component);
156 }
157
158 if (layoutManager != null) {
159 components.addAll(layoutManager.getComponentsForLifecycle());
160 }
161
162 return components;
163 }
164
165 /**
166 * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes()
167 */
168 @Override
169 public List<Component> getComponentPrototypes() {
170 List<Component> components = super.getComponentPrototypes();
171
172 if (layoutManager != null) {
173 components.addAll(layoutManager.getComponentPrototypes());
174 }
175
176 return components;
177 }
178
179 /**
180 * Additional keys that should be matching on when gathering errors or other
181 * messages for the <code>Container</code>
182 *
183 * <p>
184 * Messages associated with the container will be displayed with the
185 * container grouping in the user interface. Typically, these are a result
186 * of problems with the containers fields or some other business logic
187 * associated with the containers information. The framework will by default
188 * include all the error keys for fields in the container, and also an
189 * errors associated with the containers id. Keys given here will be matched
190 * in addition to those defaults.
191 * </p>
192 *
193 * <p>
194 * Multple keys can be given using the comma delimiter, the * wildcard is
195 * also allowed in the message key
196 * </p>
197 *
198 * @return String additional message key string
199 */
200 public String getAdditionalMessageKeys() {
201 return this.additionalMessageKeys;
202 }
203
204 /**
205 * Setter for the components additional message key string
206 *
207 * @param additionalMessageKeys
208 */
209 public void setAdditionalMessageKeys(String additionalMessageKeys) {
210 this.additionalMessageKeys = additionalMessageKeys;
211 }
212
213 /**
214 * @see org.kuali.rice.krad.uif.container.Container#getErrorsField()
215 */
216 @Override
217 public ErrorsField getErrorsField() {
218 return this.errorsField;
219 }
220
221 /**
222 * @see org.kuali.rice.krad.uif.container.Container#setErrorsField(org.kuali.rice.krad.uif.field.ErrorsField)
223 */
224 @Override
225 public void setErrorsField(ErrorsField errorsField) {
226 this.errorsField = errorsField;
227 }
228
229 /**
230 * @see org.kuali.rice.krad.uif.container.Container#getHelp()
231 */
232 @Override
233 public Help getHelp() {
234 return this.help;
235 }
236
237 /**
238 * @see org.kuali.rice.krad.uif.container.Container#setHelp(org.kuali.rice.krad.uif.widget.Help)
239 */
240 @Override
241 public void setHelp(Help help) {
242 this.help = help;
243 }
244
245 /**
246 * @see org.kuali.rice.krad.uif.container.Container#getItems()
247 */
248 @Override
249 public abstract List<? extends Component> getItems();
250
251 /**
252 * Setter for the containers list of components
253 *
254 * @param items
255 */
256 public abstract void setItems(List<? extends Component> items);
257
258 /**
259 * For <code>Component</code> instances in the container's items list that
260 * do not have an order set, a default order number will be assigned using
261 * this property. The first component found in the list without an order
262 * will be assigned the configured initial value, and incremented by one for
263 * each component (without an order) found afterwards
264 *
265 * @return int order sequence
266 */
267 public int getItemOrderingSequence() {
268 return this.itemOrderingSequence;
269 }
270
271 /**
272 * Setter for the container's item ordering sequence number (initial value)
273 *
274 * @param itemOrderingSequence
275 */
276 public void setItemOrderingSequence(int itemOrderingSequence) {
277 this.itemOrderingSequence = itemOrderingSequence;
278 }
279
280 /**
281 * @see org.kuali.rice.krad.uif.container.Container#getLayoutManager()
282 */
283 @Override
284 public LayoutManager getLayoutManager() {
285 return this.layoutManager;
286 }
287
288 /**
289 * @see org.kuali.rice.krad.uif.container.Container#setLayoutManager(org.kuali.rice.krad.uif.layout.LayoutManager)
290 */
291 @Override
292 public void setLayoutManager(LayoutManager layoutManager) {
293 this.layoutManager = layoutManager;
294 }
295
296 /**
297 * @see org.kuali.rice.krad.uif.container.Container#getHeader()
298 */
299 @Override
300 public HeaderField getHeader() {
301 return this.header;
302 }
303
304 /**
305 * @see org.kuali.rice.krad.uif.container.Container#setHeader(org.kuali.rice.krad.uif.field.HeaderField)
306 */
307 @Override
308 public void setHeader(HeaderField header) {
309 this.header = header;
310 }
311
312 /**
313 * @see org.kuali.rice.krad.uif.container.Container#getFooter()
314 */
315 @Override
316 public Group getFooter() {
317 return this.footer;
318 }
319
320 /**
321 * @see org.kuali.rice.krad.uif.container.Container#setFooter(org.kuali.rice.krad.uif.container.Group)
322 */
323 @Override
324 public void setFooter(Group footer) {
325 this.footer = footer;
326 }
327
328 /**
329 * Convenience setter for configuration to turn rendering of the header
330 * on/off
331 *
332 * <p>
333 * For nested groups (like Field Groups) it is often necessary to only show
334 * the container body (the contained components). This method allows the
335 * header to not be displayed
336 * </p>
337 *
338 * @param renderHeader
339 */
340 public void setRenderHeader(boolean renderHeader) {
341 if (header != null) {
342 header.setRender(renderHeader);
343 }
344 }
345
346 /**
347 * Convenience setter for configuration to turn rendering of the footer
348 * on/off
349 *
350 * <p>
351 * For nested groups it is often necessary to only show the container body
352 * (the contained components). This method allows the footer to not be
353 * displayed
354 * </p>
355 *
356 * @param renderFooter
357 */
358 public void setRenderFooter(boolean renderFooter) {
359 if (footer != null) {
360 footer.setRender(renderFooter);
361 }
362 }
363
364 /**
365 * Text explaining how complete the group inputs, including things like what values should be selected
366 * in certain cases, what fields should be completed and so on (instructions)
367 *
368 * @return String instructional message
369 */
370 public String getInstructionalText() {
371 return this.instructionalText;
372 }
373
374 /**
375 * Setter for the instructional message
376 *
377 * @param instructionalText
378 */
379 public void setInstructionalText(String instructionalText) {
380 this.instructionalText = instructionalText;
381 }
382
383 /**
384 * Message field that displays instructional text
385 *
386 * <p>
387 * This message field can be configured to for adjusting how the instructional text will display. Generally
388 * the styleClasses property will be of most interest
389 * </p>
390 *
391 * @return MessageField instructional message field
392 */
393 public MessageField getInstructionalMessageField() {
394 return this.instructionalMessageField;
395 }
396
397 /**
398 * Setter for the instructional text message field
399 *
400 * <p>
401 * Note this is the setter for the field that will render the instructional text. The actual text can be
402 * set on the field but can also be set using {@link #setInstructionalText(String)}
403 * </p>
404 *
405 * @param instructionalMessageField
406 */
407 public void setInstructionalMessageField(MessageField instructionalMessageField) {
408 this.instructionalMessageField = instructionalMessageField;
409 }
410
411 /**
412 * Gets only the data fields that are nested in this container. This is a subset of
413 * what getComponentsForLifecycle() returns
414 *
415 * @return
416 */
417 public List<InputField> getInputFields(){
418 List<InputField> inputFields = new ArrayList<InputField>();
419 for(Component c: this.getComponentsForLifecycle()){
420 if(c instanceof InputField){
421 inputFields.add((InputField)c);
422 }
423 }
424 return inputFields;
425
426 }
427
428 /**
429 * getAllInputFields gets all the input fields contained in this container, but also in
430 * every sub-container that is a child of this container. When called from the top level
431 * View this will be every InputField across all pages.
432 * @return every InputField that is a child at any level of this container
433 */
434 public List<InputField> getAllInputFields(){
435 List<InputField> inputFields = new ArrayList<InputField>();
436 for(Component c: this.getComponentsForLifecycle()){
437 if(c instanceof InputField){
438 inputFields.add((InputField)c);
439 }
440 else if(c instanceof ContainerBase){
441 inputFields.addAll( ((ContainerBase) c).getAllInputFields());
442 }
443 else if(c instanceof FieldGroup){
444 ContainerBase cb = ((FieldGroup) c).getGroup();
445 inputFields.addAll(cb.getAllInputFields());
446 }
447 }
448 return inputFields;
449 }
450
451 /**
452 * This property is true if the container is used to display a group of fields that is visually a single
453 * field.
454 * @return the fieldContainer
455 */
456 public boolean isFieldContainer() {
457 return this.fieldContainer;
458 }
459
460 /**
461 * @param fieldContainer the fieldContainer to set
462 */
463 public void setFieldContainer(boolean fieldContainer) {
464 this.fieldContainer = fieldContainer;
465 }
466
467 }