View Javadoc
1   /**
2    * Copyright 2005-2014 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.datadictionary.parse.BeanTagAttribute;
20  import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
21  import org.kuali.rice.krad.uif.UifConstants;
22  import org.kuali.rice.krad.uif.component.Component;
23  import org.kuali.rice.krad.uif.component.ComponentBase;
24  import org.kuali.rice.krad.uif.component.DelayedCopy;
25  import org.kuali.rice.krad.uif.element.Header;
26  import org.kuali.rice.krad.uif.element.Message;
27  import org.kuali.rice.krad.uif.element.ValidationMessages;
28  import org.kuali.rice.krad.uif.layout.LayoutManager;
29  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
30  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction;
31  import org.kuali.rice.krad.uif.util.ComponentFactory;
32  import org.kuali.rice.krad.uif.util.ComponentUtils;
33  import org.kuali.rice.krad.uif.util.LifecycleElement;
34  import org.kuali.rice.krad.uif.widget.Help;
35  import org.kuali.rice.krad.uif.widget.Tooltip;
36  
37  import java.util.ArrayList;
38  import java.util.List;
39  
40  /**
41   * Base <code>Container</code> implementation which container implementations
42   * can extend
43   *
44   * <p>
45   * Provides properties for the basic <code>Container</code> functionality in
46   * addition to default implementation of the lifecycle methods including some
47   * setup of the header, items list, and layout manager
48   * </p>
49   *
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   */
52  public abstract class ContainerBase extends ComponentBase implements Container {
53      private static final long serialVersionUID = -4182226230601746657L;
54  
55      private int defaultItemPosition;
56  
57      private Help help;
58  
59      private LayoutManager layoutManager;
60  
61      private Header header;
62      private Group footer;
63  
64      private String instructionalText;
65      private Message instructionalMessage;
66  
67      @DelayedCopy
68      private ValidationMessages validationMessages;
69  
70      private String enterKeyAction;
71  
72      /**
73       * Default Constructor
74       */
75      public ContainerBase() {
76          defaultItemPosition = 1;
77      }
78  
79      /**
80       * {@inheritDoc}
81       */
82      @Override
83      public boolean isProcessRemoteFieldHolders() {
84          return true;
85      }
86  
87      /**
88       * The following initialization is performed:
89       *
90       * <ul>
91       * <li>Sorts the containers list of components</li>
92       * <li>Initializes the instructional field if necessary</li>
93       * <li>Initializes LayoutManager</li>
94       * </ul>
95       *
96       * {@inheritDoc}
97       */
98      @SuppressWarnings("deprecation")
99      @Override
100     public void performInitialization(Object model) {
101         super.performInitialization(model);
102 
103         if ((StringUtils.isNotBlank(instructionalText) || (getPropertyExpression("instructionalText") != null)) && (
104                 instructionalMessage
105                         == null)) {
106             instructionalMessage = ComponentFactory.getInstructionalMessage();
107         }
108 
109         if (layoutManager != null && !this.getItems().isEmpty()) {
110             layoutManager.performInitialization(model);
111         }
112     }
113 
114     /**
115      * {@inheritDoc}
116      */
117     @SuppressWarnings("deprecation")
118     @Override
119     public void performApplyModel(Object model, LifecycleElement parent) {
120         super.performApplyModel(model, parent);
121 
122         // setup summary message field if necessary
123         if (instructionalMessage != null && StringUtils.isBlank(instructionalMessage.getMessageText())) {
124             instructionalMessage.setMessageText(instructionalText);
125         }
126 
127         if (layoutManager != null && !this.getItems().isEmpty()) {
128             layoutManager.performApplyModel(model, this);
129         }
130     }
131 
132     /**
133      * The following finalization is performed:
134      *
135      * <ul>
136      * <li>Sets the headerText of the header Group if it is blank</li>
137      * <li>Set the messageText of the summary Message if it is blank</li>
138      * <li>Finalizes LayoutManager</li>
139      * </ul>
140      *
141      * {@inheritDoc}
142      */
143     @SuppressWarnings("deprecation")
144     @Override
145     public void performFinalize(Object model, LifecycleElement parent) {
146         super.performFinalize(model, parent);
147 
148         if (header != null) {
149             header.addDataAttribute(UifConstants.DataAttributes.HEADER_FOR, this.getId());
150         }
151 
152         if (layoutManager != null && !this.getItems().isEmpty()) {
153             layoutManager.performFinalize(model, this);
154         }
155 
156         // Generate validation messages
157         if (validationMessages != null) {
158             validationMessages.generateMessages(ViewLifecycle.getView(), model, this);
159         }
160 
161         // add data attributes to help identify enter key actions
162         if (this.getEnterKeyAction() != null && StringUtils.isNotBlank(this.getEnterKeyAction())) {
163             this.addDataAttribute(UifConstants.DataAttributes.ENTER_KEY, this.getEnterKeyAction());
164         }
165     }
166 
167     /**
168      * {@inheritDoc}
169      */
170     @Override
171     public List<String> getAdditionalTemplates() {
172         List<String> additionalTemplates = super.getAdditionalTemplates();
173 
174         if (layoutManager != null) {
175             if (additionalTemplates.isEmpty()) {
176                 additionalTemplates = new ArrayList<String>();
177             }
178             additionalTemplates.add(layoutManager.getTemplate());
179         }
180 
181         return additionalTemplates;
182     }
183 
184     /**
185      * Performs sorting of the container items based on the order property
186      */
187     @Override
188     public void sortItems() {
189         // sort items list by the order property
190         List<? extends Component> sortedItems = ComponentUtils.sort(getItems(), defaultItemPosition);
191         setItems(sortedItems);
192     }
193 
194     /**
195      * @see org.kuali.rice.krad.uif.container.Container#getValidationMessages()
196      */
197     @Override
198     @ViewLifecycleRestriction
199     @BeanTagAttribute(name = "validationMessages", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
200     public ValidationMessages getValidationMessages() {
201         return this.validationMessages;
202     }
203 
204     /**
205      * {@inheritDoc}
206      */
207     @Override
208     public void setValidationMessages(ValidationMessages validationMessages) {
209         this.validationMessages = validationMessages;
210     }
211 
212     /**
213      * @see org.kuali.rice.krad.uif.widget.Helpable#getHelp()
214      */
215     @Override
216     @BeanTagAttribute(name = "help", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
217     public Help getHelp() {
218         return this.help;
219     }
220 
221     /**
222      * @see org.kuali.rice.krad.uif.widget.Helpable#setHelp(org.kuali.rice.krad.uif.widget.Help)
223      */
224     @Override
225     public void setHelp(Help help) {
226         this.help = help;
227     }
228 
229     /**
230      * For containers the help tooltip is placed on the header.
231      *
232      * @see org.kuali.rice.krad.uif.widget.Helpable#setTooltipOfComponent(org.kuali.rice.krad.uif.widget.Tooltip)
233      */
234     @Override
235     public void setTooltipOfComponent(Tooltip tooltip) {
236         getHeader().setToolTip(tooltip);
237     }
238 
239     /**
240      * Return the container header text for the help title
241      *
242      * @return container title
243      * @see org.kuali.rice.krad.uif.widget.Helpable#setTooltipOfComponent(org.kuali.rice.krad.uif.widget.Tooltip)
244      */
245     @Override
246     public String getHelpTitle() {
247         return this.getHeader().getHeaderText();
248     }
249 
250     /**
251      * {@inheritDoc}
252      */
253     @Override
254     @BeanTagAttribute(name = "items", type = BeanTagAttribute.AttributeType.LISTBEAN)
255     public abstract List<? extends Component> getItems();
256 
257     /**
258      * Setter for the containers list of components
259      *
260      * @param items
261      */
262     public abstract void setItems(List<? extends Component> items);
263 
264     /**
265      * For <code>Component</code> instances in the container's items list that
266      * do not have an order set, a default order number will be assigned using
267      * this property. The first component found in the list without an order
268      * will be assigned the configured initial value, and incremented by one for
269      * each component (without an order) found afterwards
270      *
271      * @return int order sequence
272      */
273     @BeanTagAttribute(name = "defaultItemPosition")
274     public int getDefaultItemPosition() {
275         return this.defaultItemPosition;
276     }
277 
278     /**
279      * Setter for the container's item ordering sequence number (initial value)
280      *
281      * @param defaultItemPosition
282      */
283     public void setDefaultItemPosition(int defaultItemPosition) {
284         this.defaultItemPosition = defaultItemPosition;
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     @Override
291     @BeanTagAttribute(name = "layoutManager", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
292     public LayoutManager getLayoutManager() {
293         return this.layoutManager;
294     }
295 
296     /**
297      * {@inheritDoc}
298      */
299     @Override
300     public void setLayoutManager(LayoutManager layoutManager) {
301         this.layoutManager = layoutManager;
302     }
303 
304     /**
305      * {@inheritDoc}
306      */
307     @Override
308     @BeanTagAttribute(name = "header", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
309     public Header getHeader() {
310         return this.header;
311     }
312 
313     /**
314      * {@inheritDoc}
315      */
316     @Override
317     public void setHeader(Header header) {
318         this.header = header;
319     }
320 
321     /**
322      * {@inheritDoc}
323      */
324     @Override
325     @BeanTagAttribute(name = "footer", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
326     public Group getFooter() {
327         return this.footer;
328     }
329 
330     /**
331      * {@inheritDoc}
332      */
333     @Override
334     public void setFooter(Group footer) {
335         this.footer = footer;
336     }
337 
338     /**
339      * Convenience setter for configuration to turn rendering of the header
340      * on/off
341      *
342      * <p>
343      * For nested groups (like Field Groups) it is often necessary to only show
344      * the container body (the contained components). This method allows the
345      * header to not be displayed
346      * </p>
347      *
348      * @param renderHeader
349      */
350     public void setRenderHeader(boolean renderHeader) {
351         if (header != null) {
352             header.setRender(renderHeader);
353         }
354     }
355 
356     /**
357      * Convenience getter for the header text
358      *
359      * @return The text that should be displayed on the header
360      */
361     @BeanTagAttribute(name = "headertext")
362     public String getHeaderText() {
363         if (header != null && header.getHeaderText() != null) {
364             return header.getHeaderText();
365         } else {
366             return "";
367         }
368     }
369 
370     /**
371      * Convenience setter for configuration to set the header text
372      *
373      * @param headerText the text that should be displayed on the header.
374      */
375     public void setHeaderText(String headerText) {
376         if (header != null) {
377             header.setHeaderText(headerText);
378         }
379     }
380 
381     /**
382      * Convenience setter for configuration to turn rendering of the footer
383      * on/off
384      *
385      * <p>
386      * For nested groups it is often necessary to only show the container body
387      * (the contained components). This method allows the footer to not be
388      * displayed
389      * </p>
390      *
391      * @param renderFooter
392      */
393     public void setRenderFooter(boolean renderFooter) {
394         if (footer != null) {
395             footer.setRender(renderFooter);
396         }
397     }
398 
399     /**
400      * Text explaining how complete the group inputs, including things like what values should be selected
401      * in certain cases, what fields should be completed and so on (instructions)
402      *
403      * @return instructional message
404      */
405     @BeanTagAttribute(name = "instructionalText")
406     public String getInstructionalText() {
407         return this.instructionalText;
408     }
409 
410     /**
411      * Setter for the instructional message
412      *
413      * @param instructionalText
414      */
415     public void setInstructionalText(String instructionalText) {
416         this.instructionalText = instructionalText;
417     }
418 
419     /**
420      * Message field that displays instructional text
421      *
422      * <p>
423      * This message field can be configured to for adjusting how the instructional text will display. Generally
424      * the styleClasses property will be of most interest
425      * </p>
426      *
427      * @return instructional message field
428      */
429     @BeanTagAttribute(name = "instructionalMessage", type = BeanTagAttribute.AttributeType.SINGLEBEAN)
430     public Message getInstructionalMessage() {
431         return this.instructionalMessage;
432     }
433 
434     /**
435      * Setter for the instructional text message field
436      *
437      * <p>
438      * Note this is the setter for the field that will render the instructional text. The actual text can be
439      * set on the field but can also be set using {@link #setInstructionalText(String)}
440      * </p>
441      *
442      * @param instructionalMessage
443      */
444     public void setInstructionalMessage(Message instructionalMessage) {
445         this.instructionalMessage = instructionalMessage;
446     }
447 
448     /**
449      * {@inheritDoc}
450      */
451     @Override
452     public void completeValidation(ValidationTrace tracer) {
453         tracer.addBean(this);
454 
455         // Checks for over writing of the instructional text or message
456         if (getInstructionalText() != null && getInstructionalMessage() != null) {
457             String currentValues[] = {"instructionalMessage.text = " + getInstructionalMessage().getMessageText(),
458                     "instructionalText = " + getInstructionalText()};
459             tracer.createWarning("InstructionalMessage will override instructioanlText", currentValues);
460         }
461 
462         super.completeValidation(tracer.getCopy());
463     }
464 
465     /**
466      * {@inheritDoc}
467      */
468     @Override
469     @BeanTagAttribute(name = "enterKeyAction")
470     public String getEnterKeyAction() {
471         return this.enterKeyAction;
472     }
473 
474     /**
475      * {@inheritDoc}
476      */
477     public void setEnterKeyAction(String enterKeyAction) {
478         this.enterKeyAction = enterKeyAction;
479     }
480 
481 }