001 /** 002 * Copyright 2005-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.uif.element; 017 018 import com.google.common.collect.Lists; 019 import org.apache.commons.lang.StringUtils; 020 import org.kuali.rice.krad.datadictionary.parse.BeanTag; 021 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 022 import org.kuali.rice.krad.datadictionary.parse.BeanTags; 023 import org.kuali.rice.krad.datadictionary.validator.ValidationTrace; 024 import org.kuali.rice.krad.datadictionary.validator.Validator; 025 import org.kuali.rice.krad.uif.UifConstants; 026 import org.kuali.rice.krad.uif.component.Component; 027 import org.kuali.rice.krad.uif.util.MessageStructureUtils; 028 import org.kuali.rice.krad.uif.view.View; 029 import org.kuali.rice.krad.util.KRADConstants; 030 031 import java.util.List; 032 033 /** 034 * Encapsulates a text message to be displayed 035 * 036 * <p> 037 * The <code>Message</code> is used to display static text in the user 038 * interface 039 * </p> 040 * 041 * @author Kuali Rice Team (rice.collab@kuali.org) 042 */ 043 @BeanTags({@BeanTag(name = "message-bean", parent = "Uif-Message"), 044 @BeanTag(name = "instructionalMessage-bean", parent = "Uif-InstructionalMessage"), 045 @BeanTag(name = "constraintMessage-bean", parent = "Uif-ConstraintMessage"), 046 @BeanTag(name = "requiredMessage-bean", parent = "Uif-RequiredMessage"), 047 @BeanTag(name = "requiredInstructionsMessage-bean", parent = "Uif-RequiredInstructionsMessage"), 048 @BeanTag(name = "stateBased-requiredInstructionsMessage-bean", 049 parent = "Uif-StateBased-RequiredInstructionsMessage"), 050 @BeanTag(name = "dialogPrompt-bean", parent = "Uif-DialogPrompt"), 051 @BeanTag(name = "imageCutineMessage-bean", parent = "Uif-ImageCutineMessage")}) 052 public class Message extends ContentElementBase { 053 private static final long serialVersionUID = 4090058533452450395L; 054 055 private String messageText; 056 private boolean generateSpan; 057 058 private List<Component> inlineComponents; 059 private List<Component> messageComponentStructure; 060 061 private boolean parseComponents; 062 063 public Message() { 064 super(); 065 066 generateSpan = true; 067 parseComponents = true; 068 } 069 070 /** 071 * Message perfom apply model parses message text for rich text functionality if the messageText contains 072 * [ or ] special characters 073 * 074 * @see Component#performApplyModel(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component) 075 */ 076 @Override 077 public void performApplyModel(View view, Object model, Component parent) { 078 super.performApplyModel(view, model, parent); 079 080 //if messageText contains the special characters [] then parse and fill in the messageComponentStructure 081 //but if messageComponentStructure has already been set it overrides messageText by default 082 if (messageText != null && messageText.contains(KRADConstants.MessageParsing.LEFT_TOKEN) && 083 messageText.contains(KRADConstants.MessageParsing.RIGHT_TOKEN) && 084 (messageComponentStructure == null || messageComponentStructure.isEmpty())) { 085 086 messageComponentStructure = MessageStructureUtils.parseMessage(this.getId(), this.getMessageText(), 087 this.getInlineComponents(), view, parseComponents); 088 089 if (messageComponentStructure != null) { 090 for (Component component : messageComponentStructure) { 091 view.getViewHelperService().performComponentInitialization(view, model, component); 092 } 093 } 094 } 095 } 096 097 /** 098 * @see Component#performFinalize(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component) 099 */ 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 }