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.datadictionary;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.exception.RiceRuntimeException;
20  import org.kuali.rice.core.api.util.KeyValue;
21  import org.kuali.rice.krad.messages.Message;
22  import org.kuali.rice.krad.messages.MessageService;
23  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
24  import org.kuali.rice.krad.uif.UifConstants;
25  import org.kuali.rice.krad.uif.component.DataBinding;
26  import org.kuali.rice.krad.uif.element.Action;
27  import org.kuali.rice.krad.uif.field.ActionField;
28  import org.kuali.rice.krad.util.KRADConstants;
29  import org.kuali.rice.krad.util.KRADPropertyConstants;
30  import org.springframework.beans.MutablePropertyValues;
31  import org.springframework.beans.PropertyValue;
32  import org.springframework.beans.factory.config.BeanDefinition;
33  import org.springframework.beans.factory.config.BeanDefinitionHolder;
34  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
35  import org.springframework.beans.factory.config.ListFactoryBean;
36  
37  import java.text.MessageFormat;
38  import java.util.Collection;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Set;
42  import java.util.Stack;
43  
44  /**
45   * Dictionary bean processor that retrieves external messages for bean definitions and alters the bean
46   * definitions to use the external text
47   *
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   */
50  public class MessageBeanProcessor extends DictionaryBeanProcessorBase {
51  
52      private ConfigurableListableBeanFactory beanFactory;
53      private DataDictionary dataDictionary;
54  
55      public MessageBeanProcessor(DataDictionary dataDictionary, ConfigurableListableBeanFactory beanFactory) {
56          this.dataDictionary = dataDictionary;
57          this.beanFactory = beanFactory;
58      }
59  
60      /**
61       * @see DictionaryBeanProcessor#processRootBeanDefinition(java.lang.String,
62       *      org.springframework.beans.factory.config.BeanDefinition)
63       */
64      public void processRootBeanDefinition(String beanName, BeanDefinition beanDefinition) {
65          processBeanMessages(beanName, beanDefinition, null);
66      }
67  
68      /**
69       * {@inheritDoc}
70       */
71      @Override
72      public void processNestedBeanDefinition(String beanName, BeanDefinition beanDefinition, String propertyName,
73              Stack<BeanDefinitionHolder> nestedBeanStack) {
74          processBeanMessages(beanName, beanDefinition, nestedBeanStack);
75      }
76  
77      /**
78       * {@inheritDoc}
79       */
80      @Override
81      public String processStringPropertyValue(String propertyName, String propertyValue,
82              Stack<BeanDefinitionHolder> nestedBeanStack) {
83          return processMessagePlaceholders(propertyValue, nestedBeanStack);
84      }
85  
86      /**
87       * {@inheritDoc}
88       */
89      @Override
90      public void processCollectionBeanDefinition(String beanName, BeanDefinition beanDefinition, String propertyName,
91              Stack<BeanDefinitionHolder> nestedBeanStack) {
92          processBeanMessages(beanName, beanDefinition, nestedBeanStack);
93      }
94  
95      /**
96       * {@inheritDoc}
97       */
98      @Override
99      public String processArrayStringPropertyValue(String propertyName, Object[] propertyValue, String elementValue,
100             int elementIndex, Stack<BeanDefinitionHolder> nestedBeanStack) {
101         return processMessagePlaceholders(elementValue, nestedBeanStack);
102     }
103 
104     /**
105      * {@inheritDoc}
106      */
107     @Override
108     public String processListStringPropertyValue(String propertyName, List<?> propertyValue, String elementValue,
109             int elementIndex, Stack<BeanDefinitionHolder> nestedBeanStack) {
110         return processMessagePlaceholders(elementValue, nestedBeanStack);
111     }
112 
113     /**
114      * {@inheritDoc}
115      */
116     @Override
117     public String processSetStringPropertyValue(String propertyName, Set<?> propertyValue, String elementValue,
118             Stack<BeanDefinitionHolder> nestedBeanStack) {
119         return processMessagePlaceholders(elementValue, nestedBeanStack);
120     }
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public String processMapStringPropertyValue(String propertyName, Map<?, ?> propertyValue, String elementValue,
127             Object elementKey, Stack<BeanDefinitionHolder> nestedBeanStack) {
128         return processMessagePlaceholders(elementValue, nestedBeanStack);
129     }
130 
131     /**
132      * Retrieves external messages whose namespace and component matches the bean definition and applies
133      * the message text to the bean property values
134      *
135      * @param beanName name of the bean to process
136      * @param beanDefinition bean definition to process
137      * @param nestedBeanStack stack of beans that contain the given bean, used for finding a namespace
138      */
139     protected void processBeanMessages(String beanName, BeanDefinition beanDefinition,
140             Stack<BeanDefinitionHolder> nestedBeanStack) {
141         Class<?> beanClass = getBeanClass(beanDefinition, beanFactory);
142         if ((beanClass == null) || !(DictionaryBean.class.isAssignableFrom(beanClass) || ListFactoryBean.class
143                 .isAssignableFrom(beanClass))) {
144             return;
145         }
146 
147         String namespace = getNamespaceForBean(beanName, beanDefinition);
148         if (StringUtils.isBlank(namespace)) {
149             namespace = getNamespaceForBeanInStack(nestedBeanStack);
150         }
151 
152         String componentCode = getComponentForBean(beanName, beanDefinition);
153         if (StringUtils.equals(componentCode, beanName)) {
154             // check if there is a parent bean in the factory using the standard suffix, if so we will skip this
155             // bean as messages will be picked up by that parent bean definition. Note this is not for all parents,
156             // just where the convention has been setup for extension (ex. 'bean' and 'bean-parentName')
157             String extensionParentBeanName = beanName + KRADConstants.DICTIONARY_BEAN_PARENT_SUFFIX;
158             if (beanFactory.containsBean(extensionParentBeanName)) {
159                 return;
160             }
161         }
162 
163         // if a namespace and component was found retrieve all messages associated with them
164         if (StringUtils.isNotBlank(namespace) && StringUtils.isNotBlank(componentCode)) {
165             Collection<Message> beanMessages = getMessageService().getAllMessagesForComponent(namespace, componentCode);
166             for (Message beanMessage : beanMessages) {
167                 applyMessageToBean(beanMessage, beanDefinition, beanClass);
168             }
169         }
170     }
171 
172     /**
173      * Applies the text for a given message to the associated bean definition property
174      *
175      * @param message message instance to apply
176      * @param beanDefinition bean definition the message should be applied to
177      * @param beanClass class for the bean definition
178      */
179     protected void applyMessageToBean(Message message, BeanDefinition beanDefinition, Class<?> beanClass) {
180         String key = message.getKey().trim();
181 
182         // if message doesn't start with path indicator, it will be an explicit key that is matched when
183         // iterating over the property values, so we will just return in that case
184         if (!key.startsWith(KRADConstants.MESSAGE_KEY_PATH_INDICATOR)) {
185             return;
186         }
187 
188         // if here dealing with a path key, strip off indicator and then process as a property path
189         key = StringUtils.stripStart(key, KRADConstants.MESSAGE_KEY_PATH_INDICATOR);
190 
191         // list factory beans just have the one list property (with no name)
192         if (ListFactoryBean.class.isAssignableFrom(beanClass)) {
193             MutablePropertyValues pvs = beanDefinition.getPropertyValues();
194 
195             PropertyValue propertyValue = pvs.getPropertyValueList().get(0);
196             List<?> listValue = (List<?>) propertyValue.getValue();
197 
198             applyMessageToNestedListBean(message, listValue, key);
199         } else if (StringUtils.contains(key, ".")) {
200             applyMessageToNestedBean(message, beanDefinition, key);
201         } else {
202             applyMessageTextToPropertyValue(key, message.getText(), beanDefinition);
203         }
204     }
205 
206     /**
207      * Applies the given message text to the property within the given bean definition
208      *
209      * <p>
210      * The message text is applied to the bean definiton based on the property path. The path could be nested
211      * in which case the property values of the bean definition are traversed to find the nested bean definition
212      * that should hold the property value. The path could also represent a nested list bean. In this case a
213      * helper method is called to find the correct list bean to apply the message to
214      * </p>
215      *
216      * @param message message containing the text to apply
217      * @param beanDefinition bean definition containing the property
218      * @param propertyPath path to property within the bean definition the message should be applied to
219      */
220     protected void applyMessageToNestedBean(Message message, BeanDefinition beanDefinition, String propertyPath) {
221         MutablePropertyValues pvs = beanDefinition.getPropertyValues();
222 
223         String beanPath = StringUtils.substringBefore(propertyPath, ".");
224         String nestedPropertyPath = StringUtils.substringAfter(propertyPath, ".");
225 
226         boolean foundNestedBean = false;
227         while (StringUtils.isNotBlank(nestedPropertyPath)) {
228             if (pvs.contains(beanPath)) {
229                 PropertyValue propertyValue = pvs.getPropertyValue(beanPath);
230 
231                 BeanDefinition propertyBeanDefinition = getPropertyValueBeanDefinition(propertyValue);
232                 if (propertyBeanDefinition != null) {
233                     applyMessageToNestedBean(message, propertyBeanDefinition, nestedPropertyPath);
234 
235                     foundNestedBean = true;
236                     break;
237                 } else if (propertyValue.getValue() instanceof List) {
238                     applyMessageToNestedListBean(message, (List<?>) propertyValue.getValue(), nestedPropertyPath);
239 
240                     foundNestedBean = true;
241                     break;
242                 }
243             }
244 
245             beanPath += "." + StringUtils.substringBefore(nestedPropertyPath, ".");
246             nestedPropertyPath = StringUtils.substringAfter(nestedPropertyPath, ".");
247         }
248 
249         if (!foundNestedBean) {
250             applyMessageTextToPropertyValue(propertyPath, message.getText(), beanDefinition);
251         }
252     }
253 
254     /**
255      * Applies a message to a nested list bean definition
256      *
257      * <p>
258      * Here the property path first gives an identifier value (such as property name) to use for finding
259      * the bean definition with the list the message should apply to. Any part of the path after the identifier
260      * value is treated like a nested property path
261      * </p>
262      *
263      * @param message message instance that contains the text to apply
264      * @param listPropertyValue property value list that should contain the bean definition the message
265      * will be applied to
266      * @param propertyPath path to the bean definition the message should apply to
267      */
268     protected void applyMessageToNestedListBean(Message message, List<?> listPropertyValue, String propertyPath) {
269         // property path must be nested, with first part giving the list bean identifier value
270         if (!StringUtils.contains(propertyPath, ".")) {
271             throw new RiceRuntimeException(
272                     "Key for nested list bean must contain the identifer value followed by the path.");
273         }
274 
275         String listIdentifierPropertyValue = StringUtils.substringBefore(propertyPath, ".");
276         String listBeanPropertyPath = StringUtils.substringAfter(propertyPath, ".");
277 
278         // iterate through list and find beans that match the given identifier
279         for (int i = 0; i < listPropertyValue.size(); i++) {
280             Object elem = listPropertyValue.get(i);
281 
282             if ((elem instanceof BeanDefinition) || (elem instanceof BeanDefinitionHolder)) {
283                 BeanDefinition beanDefinition;
284                 if (elem instanceof BeanDefinition) {
285                     beanDefinition = (BeanDefinition) elem;
286                 } else {
287                     beanDefinition = ((BeanDefinitionHolder) elem).getBeanDefinition();
288                 }
289 
290                 boolean isMatch = isBeanMessageMatch(listIdentifierPropertyValue, beanDefinition);
291                 if (isMatch) {
292                     if (StringUtils.contains(listBeanPropertyPath, ".")) {
293                         applyMessageToNestedBean(message, beanDefinition, listBeanPropertyPath);
294                     } else {
295                         applyMessageTextToPropertyValue(listBeanPropertyPath, message.getText(), beanDefinition);
296                     }
297                 }
298             }
299         }
300     }
301 
302     /**
303      * Determines whether the given bean definition is a matched based on the given identifier value
304      *
305      * <p>
306      * Based on the class for the bean definition an identifier property name is used, the corresponding value
307      * from the bean definition is then retrieved and compared to the given value to determine whether the
308      * bean definition is a match
309      * </p>
310      *
311      * @param matchListIdentifierPropertyValue property value to match bean definitions on
312      * @param beanDefinition bean definition to determine the match for
313      * @return boolean true if the bean definition is a match, false if not
314      */
315     protected boolean isBeanMessageMatch(String matchListIdentifierPropertyValue, BeanDefinition beanDefinition) {
316         boolean isMatch = false;
317 
318         String listIdentifierPropertyName = null;
319 
320         Class<?> beanClass = getBeanClass(beanDefinition, beanFactory);
321         if (DataBinding.class.isAssignableFrom(beanClass)) {
322             listIdentifierPropertyName = KRADPropertyConstants.PROPERTY_NAME;
323         } else if (Action.class.isAssignableFrom(beanClass) || ActionField.class.isAssignableFrom(beanClass)) {
324             listIdentifierPropertyName = KRADPropertyConstants.METHOD_TO_CALL;
325         } else if (KeyValue.class.isAssignableFrom(beanClass)) {
326             listIdentifierPropertyName = KRADPropertyConstants.KEY;
327         }
328 
329         if (StringUtils.isNotBlank(listIdentifierPropertyName)) {
330             String listIdentifierPropertyValue = findPropertyValueInBeanDefinition(beanDefinition,
331                     listIdentifierPropertyName);
332             if (listIdentifierPropertyValue != null) {
333                 isMatch = StringUtils.equals(listIdentifierPropertyValue, matchListIdentifierPropertyValue);
334             }
335         }
336 
337         return isMatch;
338     }
339 
340     /**
341      * Attempts to find a property value for the given property name within the bean definition, if the property
342      * does not exist in the bean and there is a parent, the parent is checked for containing the property. This
343      * continues until a property value is found or all the parents have been traversed
344      *
345      * @param beanDefinition bean definition to find property value in
346      * @param propertyName name of the property to find the value for
347      * @return String value for property in the bean definition or null if the property was not found
348      */
349     protected String findPropertyValueInBeanDefinition(BeanDefinition beanDefinition, String propertyName) {
350         String beanPropertyValue = null;
351 
352         MutablePropertyValues pvs = beanDefinition.getPropertyValues();
353         if (pvs.contains(propertyName)) {
354             PropertyValue propertyValue = pvs.getPropertyValue(propertyName);
355             if (propertyValue.getValue() != null) {
356                 beanPropertyValue = propertyValue.getValue().toString();
357             }
358         } else {
359             if (StringUtils.isNotBlank(beanDefinition.getParentName())) {
360                 BeanDefinition parentBeanDefinition = beanFactory.getBeanDefinition(beanDefinition.getParentName());
361 
362                 beanPropertyValue = findPropertyValueInBeanDefinition(parentBeanDefinition, propertyName);
363             }
364         }
365 
366         return beanPropertyValue;
367     }
368 
369     /**
370      * Checks a string property value for a message placeholder and if found the message is retrieved and updated
371      * in the property value
372      *
373      * @param propertyValue string value to process for message placeholders
374      * @param nestedBeanStack stack of bean definitions that contain the property, used to determine the namespace
375      * and component for the message retrieval
376      * @return String new value for the property (possibly modified from an external message)
377      */
378     protected String processMessagePlaceholders(String propertyValue, Stack<BeanDefinitionHolder> nestedBeanStack) {
379         String trimmedPropertyValue = StringUtils.stripStart(propertyValue, " ");
380         if (StringUtils.isBlank(trimmedPropertyValue)) {
381             return propertyValue;
382         }
383 
384         String newPropertyValue = propertyValue;
385 
386         // first check for a replacement message key
387         if (trimmedPropertyValue.startsWith(KRADConstants.MESSAGE_KEY_PLACEHOLDER_PREFIX) && StringUtils.contains(
388                 trimmedPropertyValue, KRADConstants.MESSAGE_KEY_PLACEHOLDER_SUFFIX)) {
389             String messageKeyStr = StringUtils.substringBetween(trimmedPropertyValue,
390                     KRADConstants.MESSAGE_KEY_PLACEHOLDER_PREFIX, KRADConstants.MESSAGE_KEY_PLACEHOLDER_SUFFIX);
391 
392             // get any default specified value (given after the message key)
393             String messageKeyWithPlaceholder = KRADConstants.MESSAGE_KEY_PLACEHOLDER_PREFIX + messageKeyStr +
394                     KRADConstants.MESSAGE_KEY_PLACEHOLDER_SUFFIX;
395 
396             String defaultPropertyValue = StringUtils.substringAfter(trimmedPropertyValue, messageKeyWithPlaceholder);
397 
398             // set the new property value to the message text (if found), or the default value if a message was not found
399             // note the message text could be an empty string, in which case it will override the default
400             String messageText = getMessageTextForKey(messageKeyStr, nestedBeanStack);
401             if (messageText != null) {
402                 // if default value set then we need to merge any expressions
403                 if (StringUtils.isNotBlank(defaultPropertyValue)) {
404                     newPropertyValue = getMergedMessageText(messageText, defaultPropertyValue);
405                 } else {
406                     newPropertyValue = messageText;
407                 }
408             } else {
409                 newPropertyValue = defaultPropertyValue;
410             }
411         }
412         // now check for message keys within an expression
413         else if (StringUtils.contains(trimmedPropertyValue, KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_PREFIX)) {
414             String[] expressionMessageKeys = StringUtils.substringsBetween(newPropertyValue,
415                     KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_PREFIX,
416                     KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_SUFFIX);
417 
418             for (String expressionMessageKey : expressionMessageKeys) {
419                 String expressionMessageText = getMessageTextForKey(expressionMessageKey, nestedBeanStack);
420                 newPropertyValue = StringUtils.replace(newPropertyValue,
421                         KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_PREFIX + expressionMessageKey +
422                                 KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_SUFFIX, expressionMessageText);
423             }
424         }
425 
426         return newPropertyValue;
427     }
428 
429     /**
430      * Retrieves the test associated with the message give by the message key string
431      *
432      * @param messageKeyStr key string for the message, can contain just the key, or also the component and/or
433      * namespace. If component or namespace not given it is determined from the bean stack
434      * @param nestedBeanStack bean stack that contains the property for which the message applies
435      * @return String test associated with the message
436      */
437     protected String getMessageTextForKey(String messageKeyStr, Stack<BeanDefinitionHolder> nestedBeanStack) {
438         String namespace = null;
439         String componentCode = null;
440         String key = null;
441 
442         // check for specification of namespace and component
443         if (StringUtils.contains(messageKeyStr, ":")) {
444             String[] messageParams = StringUtils.split(messageKeyStr, ":");
445 
446             if (messageParams.length == 3) {
447                 namespace = messageParams[0];
448                 componentCode = messageParams[1];
449                 key = messageParams[2];
450             } else if (messageParams.length == 2) {
451                 componentCode = messageParams[0];
452                 key = messageParams[1];
453             } else {
454                 throw new RiceRuntimeException("Message key '" + messageKeyStr + "' has an invalid format");
455             }
456         } else {
457             key = messageKeyStr;
458         }
459 
460         if (StringUtils.isBlank(namespace)) {
461             namespace = getNamespaceForBeanInStack(nestedBeanStack);
462         }
463 
464         if (StringUtils.isBlank(componentCode)) {
465             for (int i = nestedBeanStack.size() - 1; i >= 0; i--) {
466                 BeanDefinitionHolder definitionHolder = nestedBeanStack.get(i);
467                 componentCode = getComponentForBean(definitionHolder.getBeanName(),
468                         definitionHolder.getBeanDefinition());
469                 if (StringUtils.isNotBlank(componentCode)) {
470                     break;
471                 }
472             }
473         }
474 
475         String messageText = null;
476         if (StringUtils.isNotBlank(namespace) && StringUtils.isNotBlank(componentCode) && StringUtils.isNotBlank(key)) {
477             messageText = getMessageService().getMessageText(namespace, componentCode, key);
478         }
479 
480         return messageText;
481     }
482 
483     /**
484      * Applies the given message text to the bean definition with the given property name, if a current
485      * value exists for the property name the value is checked for expressions which are then merged with
486      * the message
487      *
488      * @param propertyName - name of the property to set on the bean definition
489      * @param messageText - message text that will be the property value
490      * @param beanDefinition - bean definition to set property on
491      */
492     protected void applyMessageTextToPropertyValue(String propertyName, String messageText,
493             BeanDefinition beanDefinition) {
494         String newPropertyValue = messageText;
495 
496         MutablePropertyValues pvs = beanDefinition.getPropertyValues();
497         if (pvs.contains(propertyName)) {
498             PropertyValue propertyValue = pvs.getPropertyValue(propertyName);
499 
500             String stringPropertyValue = getStringValue(propertyValue.getValue());
501             if (StringUtils.isNotBlank(stringPropertyValue)) {
502                 newPropertyValue = getMergedMessageText(messageText, stringPropertyValue);
503             }
504         }
505 
506         applyPropertyValueToBean(propertyName, newPropertyValue, pvs);
507     }
508 
509     /**
510      * Prepares the message text that will replace the property value checking for any expression placeholders
511      *
512      * <p>
513      * The message text may contain placeholders (using brace delimiters) for expression placement. It is
514      * expected when these placeholders are given the property value contains the expressions (using the
515      * expression placeholders) that will be inserted into the message text
516      * </p>
517      *
518      * @param messageText - raw text of the message
519      * @param propertyValue - current value for the property
520      * @return String the message text with expressions inserted (if any expressions were found)
521      */
522     protected String getMergedMessageText(String messageText, String propertyValue) {
523         String mergedText = messageText;
524 
525         String[] expressions = StringUtils.substringsBetween(propertyValue, UifConstants.EL_PLACEHOLDER_PREFIX,
526                 UifConstants.EL_PLACEHOLDER_SUFFIX);
527         if ((expressions != null) && expressions.length > 0) {
528             // add expression placeholders back on
529             String[] messageParameters = new String[expressions.length];
530             for (int i = 0; i < expressions.length; i++) {
531                 String expression = expressions[i];
532 
533                 expression = UifConstants.EL_PLACEHOLDER_PREFIX + expression + UifConstants.EL_PLACEHOLDER_SUFFIX;
534                 messageParameters[i] = expression;
535             }
536 
537             // escape single quotes for message format process
538             messageText = messageText.replace("'", "''");
539             try {
540                 mergedText = MessageFormat.format(messageText, messageParameters);
541             } catch (IllegalArgumentException e) {
542                 throw new RiceRuntimeException(
543                         "Unable to merge expressions with message text. Expression count is: " + expressions.length, e);
544             }
545         }
546 
547         return mergedText;
548     }
549 
550     /**
551      * Walks up the stack of bean definitions until a namespace is found and returns that namespace
552      *
553      * @param nestedBeanStack stack of bean definitions to find namespace for
554      * @return String namespace found in stack or null if one was not found
555      */
556     protected String getNamespaceForBeanInStack(Stack<BeanDefinitionHolder> nestedBeanStack) {
557         String namespace = null;
558 
559         if (nestedBeanStack != null) {
560             for (int i = nestedBeanStack.size() - 1; i >= 0; i--) {
561                 BeanDefinitionHolder definitionHolder = nestedBeanStack.get(i);
562                 namespace = getNamespaceForBean(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
563                 if (StringUtils.isNotBlank(namespace)) {
564                     break;
565                 }
566             }
567         }
568 
569         return namespace;
570     }
571 
572     /**
573      * Retrieves the namespace associated with the bean definition
574      *
575      * @param beanName name of the bean to find namespace for
576      * @param beanDefinition bean definition to find namespace for
577      * @return String namespace for bean or null if a namespace was not found
578      */
579     protected String getNamespaceForBean(String beanName, BeanDefinition beanDefinition) {
580         String namespace = null;
581 
582         MutablePropertyValues pvs = beanDefinition.getPropertyValues();
583         if (pvs.contains(KRADPropertyConstants.NAMESPACE_CODE)) {
584             PropertyValue propertyValue = pvs.getPropertyValue(KRADPropertyConstants.NAMESPACE_CODE);
585             namespace = getStringValue(propertyValue.getValue());
586         } else if (StringUtils.isNotBlank(beanName) && !isGeneratedBeanName(beanName)) {
587             // if not on bean definition, get from associated module in the dictionary
588             namespace = dataDictionary.getNamespaceForBeanDefinition(beanName);
589         }
590 
591         return namespace;
592     }
593 
594     /**
595      * Retrieves the component code associated with the bean definition
596      *
597      * @param beanName name of the bean to find component code for
598      * @param beanDefinition bean definition to find component code for
599      * @return String component code for bean or null if a component code was not found
600      */
601     protected String getComponentForBean(String beanName, BeanDefinition beanDefinition) {
602         String componentCode = null;
603 
604         MutablePropertyValues pvs = beanDefinition.getPropertyValues();
605         if (pvs.contains(KRADPropertyConstants.COMPONENT_CODE)) {
606             PropertyValue propertyValue = pvs.getPropertyValue(KRADPropertyConstants.COMPONENT_CODE);
607 
608             componentCode = getStringValue(propertyValue.getValue());
609         }
610 
611         if ((componentCode == null) && StringUtils.isNotBlank(beanName) && !isGeneratedBeanName(beanName)) {
612             componentCode = beanName;
613         }
614 
615         if (StringUtils.isNotBlank(componentCode)) {
616             componentCode = StringUtils.removeEnd(componentCode, KRADConstants.DICTIONARY_BEAN_PARENT_SUFFIX);
617         }
618 
619         return componentCode;
620     }
621 
622     /**
623      * Returns instance of the Message Service
624      *
625      * @return MessageService isntanc
626      */
627     protected MessageService getMessageService() {
628         return KRADServiceLocatorWeb.getMessageService();
629     }
630 }