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.element;
17
18 import com.google.common.collect.Lists;
19 import org.apache.commons.lang.StringUtils;
20 import org.kuali.rice.krad.datadictionary.parse.BeanTag;
21 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
22 import org.kuali.rice.krad.datadictionary.parse.BeanTags;
23 import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
24 import org.kuali.rice.krad.datadictionary.validator.Validator;
25 import org.kuali.rice.krad.uif.UifConstants;
26 import org.kuali.rice.krad.uif.component.Component;
27 import org.kuali.rice.krad.uif.util.MessageStructureUtils;
28 import org.kuali.rice.krad.uif.view.View;
29 import org.kuali.rice.krad.util.KRADConstants;
30
31 import java.util.List;
32
33 /**
34 * Encapsulates a text message to be displayed
35 *
36 * <p>
37 * The <code>Message</code> is used to display static text in the user
38 * interface
39 * </p>
40 *
41 * @author Kuali Rice Team (rice.collab@kuali.org)
42 */
43 @BeanTags({@BeanTag(name = "message-bean", parent = "Uif-Message"),
44 @BeanTag(name = "instructionalMessage-bean", parent = "Uif-InstructionalMessage"),
45 @BeanTag(name = "constraintMessage-bean", parent = "Uif-ConstraintMessage"),
46 @BeanTag(name = "requiredMessage-bean", parent = "Uif-RequiredMessage"),
47 @BeanTag(name = "requiredInstructionsMessage-bean", parent = "Uif-RequiredInstructionsMessage"),
48 @BeanTag(name = "stateBased-requiredInstructionsMessage-bean",
49 parent = "Uif-StateBased-RequiredInstructionsMessage"),
50 @BeanTag(name = "dialogPrompt-bean", parent = "Uif-DialogPrompt"),
51 @BeanTag(name = "imageCutineMessage-bean", parent = "Uif-ImageCutineMessage")})
52 public class Message extends ContentElementBase {
53 private static final long serialVersionUID = 4090058533452450395L;
54
55 private String messageText;
56 private boolean generateSpan;
57
58 private List<Component> inlineComponents;
59 private List<Component> messageComponentStructure;
60
61 private boolean parseComponents;
62
63 public Message() {
64 super();
65
66 generateSpan = true;
67 parseComponents = true;
68 }
69
70 /**
71 * Message perfom apply model parses message text for rich text functionality if the messageText contains
72 * [ or ] special characters
73 *
74 * @see Component#performApplyModel(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component)
75 */
76 @Override
77 public void performApplyModel(View view, Object model, Component parent) {
78 super.performApplyModel(view, model, parent);
79
80 //if messageText contains the special characters [] then parse and fill in the messageComponentStructure
81 //but if messageComponentStructure has already been set it overrides messageText by default
82 if (messageText != null && messageText.contains(KRADConstants.MessageParsing.LEFT_TOKEN) &&
83 messageText.contains(KRADConstants.MessageParsing.RIGHT_TOKEN) &&
84 (messageComponentStructure == null || messageComponentStructure.isEmpty())) {
85
86 messageComponentStructure = MessageStructureUtils.parseMessage(this.getId(), this.getMessageText(),
87 this.getInlineComponents(), view, parseComponents);
88
89 if (messageComponentStructure != null) {
90 for (Component component : messageComponentStructure) {
91 view.getViewHelperService().performComponentInitialization(view, model, component);
92 }
93 }
94 }
95 }
96
97 /**
98 * @see Component#performFinalize(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component)
99 */
100 @Override
101 public void performFinalize(View view, Object model, Component parent) {
102 super.performFinalize(view, model, parent);
103
104 // message needs to be aware of its own parent because it now contains content that can have validation
105 this.addDataAttribute(UifConstants.DataAttributes.PARENT, parent.getId());
106 }
107
108 /**
109 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
110 */
111 @Override
112 public List<Component> getComponentsForLifecycle() {
113 List<Component> components = super.getComponentsForLifecycle();
114
115 if (messageComponentStructure != null) {
116 for (Component component : messageComponentStructure) {
117 components.add(component);
118 }
119 }
120
121 return components;
122 }
123
124 /**
125 * Override to render only if the message text has been given or there is a conditional expression on the
126 * message text
127 *
128 * @see org.kuali.rice.krad.uif.component.ComponentBase#isRender()
129 */
130 @Override
131 public boolean isRender() {
132 boolean render = super.isRender();
133
134 if (render) {
135 render = getPropertyExpressions().containsKey("messageText") || (StringUtils.isNotBlank(messageText)
136 && !StringUtils.equals(messageText, " "));
137 }
138
139 return render;
140 }
141
142 /**
143 * Text that makes up the message that will be displayed.
144 *
145 * <p>If special characters [] are detected the message inserts special content at that location.
146 * The types of features supported are (note that <> are not part of the content below,
147 * they specify placeholders):
148 * <ul>
149 * <li>[id=<component id>] - insert component with id specified at that location in the message</li>
150 * <li>[n] - insert component at index n from the inlineComponent list</li>
151 * <li>[<html tag>][/<html tag>] - insert html content directly into the message content at that
152 * location,
153 * without the need to escape the <> characters in xml</li>
154 * <li>[color=<html color code/name>][/color] - wrap content in color tags to make text that color
155 * in the message</li>
156 * <li>[css=<css classes>][/css] - apply css classes specified to the wrapped content - same as wrapping
157 * the content in span with class property set</li>
158 * </ul>
159 * If the [] characters are needed in message text, they need to be declared with an escape character: \\[ \\]
160 * </p>
161 *
162 * @return message text
163 */
164 @BeanTagAttribute(name = "messageText")
165 public String getMessageText() {
166 return this.messageText;
167 }
168
169 /**
170 * Setter for the message text
171 *
172 * @param messageText
173 */
174 public void setMessageText(String messageText) {
175 this.messageText = messageText;
176 }
177
178 /**
179 * If true, generate the span around this message (default). When false, skip span generation for this
180 * message - this has the additional effect the css classes/style classes will be lost for this message.
181 *
182 * @return true if generating a wrapping span, false otherwise
183 */
184 @BeanTagAttribute(name = "generateSpan")
185 public boolean isGenerateSpan() {
186 return generateSpan;
187 }
188
189 /**
190 * Sets the generate span flag
191 *
192 * @param generateSpan
193 */
194 public void setGenerateSpan(boolean generateSpan) {
195 this.generateSpan = generateSpan;
196 }
197
198 /**
199 * The message component structure is a list of components which represent the components that make up a message
200 * when using rich message functionality.
201 *
202 * <p>The structure represents the parsed messageText when not set. Normally this structure is setup by the Message
203 * class and <b>SHOULD NOT BE SET</b> in xml, unless full control over the structure is needed. </p>
204 *
205 * @return list of components which represent the message structure
206 */
207 public List<Component> getMessageComponentStructure() {
208 return messageComponentStructure;
209 }
210
211 /**
212 * Set the message component structure. This will override/ignore messageText when set. Normally
213 * this <b>SHOULD NOT BE SET</b> by the xml configuration.
214 *
215 * @param messageComponentStructure list of components which represent the message structure
216 */
217 public void setMessageComponentStructure(List<Component> messageComponentStructure) {
218 this.messageComponentStructure = messageComponentStructure;
219 }
220
221 /**
222 * The inlineComponents are a list of components in order by index.
223 *
224 * <p>inlineComponents is only used when the message is using rich message functionality. A message
225 * with [0] will reference component at index 0 of this list and insert it at that place in the message,
226 * and likewise [1] will reference item 1, etc. If the index referenced is out of bounds (or list doesnt exist),
227 * an error will be thrown during message parse.</p>
228 *
229 * @return the inlineComponents to be filled in at indexes referenced by [n] in the message
230 */
231 @BeanTagAttribute(name = "inlineComponents", type = BeanTagAttribute.AttributeType.LISTBEAN)
232 public List<Component> getInlineComponents() {
233 return inlineComponents;
234 }
235
236 /**
237 * Set the inlineComponents to be filled in at indexes referenced by [n] in the message
238 *
239 * @param inlineComponents the inlineComponents to be filled in at indexes referenced by [n] in the message
240 */
241 public void setInlineComponents(List<Component> inlineComponents) {
242 this.inlineComponents = inlineComponents;
243 }
244
245 /**
246 * @see org.kuali.rice.krad.uif.component.Component#completeValidation
247 */
248 @Override
249 public void completeValidation(ValidationTrace tracer) {
250 tracer.addBean(this);
251
252 // Checks that text is set
253 if (getMessageText() == null) {
254 if (Validator.checkExpressions(this, "messageText")) {
255 String currentValues[] = {"messageText =" + getMessageText()};
256 tracer.createWarning("MessageText should be set", currentValues);
257 }
258 }
259
260 super.completeValidation(tracer.getCopy());
261 }
262
263 /**
264 * Indicates if the inline components must be parsed for rich messages
265 *
266 * @return boolean
267 */
268 @BeanTagAttribute(name = "parseComponents")
269 public boolean isParseComponents() {
270 return parseComponents;
271 }
272
273 /**
274 * Sets the parse components flag to indicate if inline components must be parsed for rich messages
275 *
276 * @param parseComponents
277 */
278 public void setParseComponents(boolean parseComponents) {
279 this.parseComponents = parseComponents;
280 }
281
282 /**
283 * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
284 */
285 @Override
286 protected <T> void copyProperties(T component) {
287 super.copyProperties(component);
288 Message messageCopy = (Message) component;
289 messageCopy.setGenerateSpan(this.generateSpan);
290
291 if (this.inlineComponents != null) {
292 List<Component> inlineComponents = Lists.newArrayListWithExpectedSize(this.inlineComponents.size());
293
294 for (Component inlineComponent : this.inlineComponents) {
295 inlineComponents.add((Component)inlineComponent.copy());
296 }
297
298 messageCopy.setInlineComponents(inlineComponents);
299 }
300
301 if (this.messageComponentStructure != null) {
302 List<Component> messageComponentStructure = Lists.newArrayListWithExpectedSize(
303 this.messageComponentStructure.size());
304
305 for (Component messageComponentStructureItem : this.messageComponentStructure) {
306 messageComponentStructure.add((Component)messageComponentStructureItem.copy());
307 }
308
309 messageCopy.setMessageComponentStructure(messageComponentStructure);
310 }
311
312 messageCopy.setMessageText(this.messageText);
313 messageCopy.setParseComponents(this.parseComponents);
314 }
315 }