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