View Javadoc
1   /**
2    * Copyright 2005-2016 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      * {@inheritDoc}
196      */
197     @Override
198     @ViewLifecycleRestriction
199     @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
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 	 * {@inheritDoc}
214 	 */
215 	@Override
216     @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
217 	public Help getHelp() {
218 		return this.help;
219 	}
220 
221     /**
222      * {@inheritDoc}
223      */
224     @Override
225     public void setHelp(Help help) {
226         this.help = help;
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
233     public void setTooltipOfComponent(Tooltip tooltip) {
234         getHeader().setToolTip(tooltip);
235     }
236 
237     /**
238      * {@inheritDoc}
239      */
240     @Override
241     public String getHelpTitle() {
242         return this.getHeader().getHeaderText();
243     }
244 
245     /**
246 	 * {@inheritDoc}
247 	 */
248 	@Override
249     @BeanTagAttribute
250 	public abstract List<? extends Component> getItems();
251 
252     /**
253      * Setter for the containers list of components
254      *
255      * @param items
256      */
257     public abstract void setItems(List<? extends Component> items);
258 
259 	/**
260 	 * For <code>Component</code> instances in the container's items list that
261 	 * do not have an order set, a default order number will be assigned using
262 	 * this property. The first component found in the list without an order
263 	 * will be assigned the configured initial value, and incremented by one for
264 	 * each component (without an order) found afterwards
265 	 *
266 	 * @return int order sequence
267 	 */
268     @BeanTagAttribute
269 	public int getDefaultItemPosition() {
270 		return this.defaultItemPosition;
271 	}
272 
273     /**
274      * Setter for the container's item ordering sequence number (initial value)
275      *
276      * @param defaultItemPosition
277      */
278     public void setDefaultItemPosition(int defaultItemPosition) {
279         this.defaultItemPosition = defaultItemPosition;
280     }
281 
282 	/**
283 	 * {@inheritDoc}
284 	 */
285 	@Override
286     @BeanTagAttribute(type= BeanTagAttribute.AttributeType.BYTYPE)
287 	public LayoutManager getLayoutManager() {
288 		return this.layoutManager;
289 	}
290 
291     /**
292      * {@inheritDoc}
293      */
294     @Override
295     public void setLayoutManager(LayoutManager layoutManager) {
296         this.layoutManager = layoutManager;
297     }
298 
299 	/**
300 	 * {@inheritDoc}
301 	 */
302 	@Override
303     @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
304 	public Header getHeader() {
305 		return this.header;
306 	}
307 
308     /**
309      * {@inheritDoc}
310      */
311     @Override
312     public void setHeader(Header header) {
313         this.header = header;
314     }
315 
316 	/**
317 	 * {@inheritDoc}
318 	 */
319 	@Override
320     @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECT)
321 	public Group getFooter() {
322 		return this.footer;
323 	}
324 
325     /**
326      * {@inheritDoc}
327      */
328     @Override
329     public void setFooter(Group footer) {
330         this.footer = footer;
331     }
332 
333     /**
334      * Convenience setter for configuration to turn rendering of the header
335      * on/off
336      *
337      * <p>
338      * For nested groups (like Field Groups) it is often necessary to only show
339      * the container body (the contained components). This method allows the
340      * header to not be displayed
341      * </p>
342      *
343      * @param renderHeader
344      */
345     public void setRenderHeader(boolean renderHeader) {
346         if (header != null) {
347             header.setRender(renderHeader);
348         }
349     }
350 
351     /**
352      * Convenience getter for the header text
353      *
354      * @return The text that should be displayed on the header
355      */
356     @BeanTagAttribute
357     public String getHeaderText () {
358         if (header != null && header.getHeaderText() != null) {
359             return header.getHeaderText();
360         } else {
361             return "";
362         }
363     }
364 
365     /**
366      * Convenience setter for configuration to set the header text
367      *
368      * @param headerText the text that should be displayed on the header.
369      */
370     public void setHeaderText(String headerText) {
371         if (header != null) {
372             header.setHeaderText(headerText);
373         }
374     }
375 
376     /**
377      * Convenience setter for configuration to turn rendering of the footer
378      * on/off
379      *
380      * <p>
381      * For nested groups it is often necessary to only show the container body
382      * (the contained components). This method allows the footer to not be
383      * displayed
384      * </p>
385      *
386      * @param renderFooter
387      */
388     public void setRenderFooter(boolean renderFooter) {
389         if (footer != null) {
390             footer.setRender(renderFooter);
391         }
392     }
393 
394     /**
395      * Text explaining how complete the group inputs, including things like what values should be selected
396      * in certain cases, what fields should be completed and so on (instructions)
397      *
398      * @return instructional message
399      */
400     @BeanTagAttribute
401 	public String getInstructionalText() {
402 		return this.instructionalText;
403 	}
404 
405     /**
406      * Setter for the instructional message
407      *
408      * @param instructionalText
409      */
410     public void setInstructionalText(String instructionalText) {
411         this.instructionalText = instructionalText;
412     }
413 
414     /**
415      * Message field that displays instructional text
416      *
417      * <p>
418      * This message field can be configured to for adjusting how the instructional text will display. Generally
419      * the styleClasses property will be of most interest
420      * </p>
421      *
422      * @return instructional message field
423      */
424     @BeanTagAttribute
425 	public Message getInstructionalMessage() {
426 		return this.instructionalMessage;
427 	}
428 
429     /**
430      * Setter for the instructional text message field
431      *
432      * <p>
433      * Note this is the setter for the field that will render the instructional text. The actual text can be
434      * set on the field but can also be set using {@link #setInstructionalText(String)}
435      * </p>
436      *
437      * @param instructionalMessage
438      */
439     public void setInstructionalMessage(Message instructionalMessage) {
440         this.instructionalMessage = instructionalMessage;
441     }
442     
443     /**
444      * {@inheritDoc}
445      */
446     @Override
447     @BeanTagAttribute
448     public String getEnterKeyAction() {
449         return this.enterKeyAction;
450     }
451 
452     /**
453      * {@inheritDoc}
454      */
455     public void setEnterKeyAction(String enterKeyAction) {
456         this.enterKeyAction = enterKeyAction;
457     }
458 
459     /**
460      * {@inheritDoc}
461      */
462     @Override
463     public void completeValidation(ValidationTrace tracer) {
464         tracer.addBean(this);
465 
466         // Checks for over writing of the instructional text or message
467         if (getInstructionalText() != null && getInstructionalMessage() != null) {
468             String currentValues[] = {"instructionalMessage.text = " + getInstructionalMessage().getMessageText(),
469                     "instructionalText = " + getInstructionalText()};
470             tracer.createWarning("InstructionalMessage will override instructioanlText", currentValues);
471         }
472 
473         super.completeValidation(tracer.getCopy());
474     }
475 }