org.kuali.rice.krad.datadictionary.MessageBeanProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.krad.datadictionary.MessageBeanProcessor.java

Source

/**
 * Copyright 2005-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.krad.datadictionary;

import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.exception.RiceRuntimeException;
import org.kuali.rice.core.api.util.KeyValue;
import org.kuali.rice.krad.messages.Message;
import org.kuali.rice.krad.messages.MessageService;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.uif.UifConstants;
import org.kuali.rice.krad.uif.component.DataBinding;
import org.kuali.rice.krad.uif.element.Action;
import org.kuali.rice.krad.uif.field.ActionField;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.KRADPropertyConstants;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ListFactoryBean;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

/**
 * Dictionary bean processor that retrieves external messages for bean definitions and alters the bean
 * definitions to use the external text
 *
 * @author Kuali Rice Team (rice.collab@kuali.org)
 */
public class MessageBeanProcessor extends DictionaryBeanProcessorBase {

    private ConfigurableListableBeanFactory beanFactory;
    private DataDictionary dataDictionary;

    public MessageBeanProcessor(DataDictionary dataDictionary, ConfigurableListableBeanFactory beanFactory) {
        this.dataDictionary = dataDictionary;
        this.beanFactory = beanFactory;
    }

    /**
     * @see DictionaryBeanProcessor#processRootBeanDefinition(java.lang.String,
     *      org.springframework.beans.factory.config.BeanDefinition)
     */
    public void processRootBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        processBeanMessages(beanName, beanDefinition, null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void processNestedBeanDefinition(String beanName, BeanDefinition beanDefinition, String propertyName,
            Stack<BeanDefinitionHolder> nestedBeanStack) {
        processBeanMessages(beanName, beanDefinition, nestedBeanStack);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String processStringPropertyValue(String propertyName, String propertyValue,
            Stack<BeanDefinitionHolder> nestedBeanStack) {
        return processMessagePlaceholders(propertyValue, nestedBeanStack);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void processCollectionBeanDefinition(String beanName, BeanDefinition beanDefinition, String propertyName,
            Stack<BeanDefinitionHolder> nestedBeanStack) {
        processBeanMessages(beanName, beanDefinition, nestedBeanStack);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String processArrayStringPropertyValue(String propertyName, Object[] propertyValue, String elementValue,
            int elementIndex, Stack<BeanDefinitionHolder> nestedBeanStack) {
        return processMessagePlaceholders(elementValue, nestedBeanStack);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String processListStringPropertyValue(String propertyName, List<?> propertyValue, String elementValue,
            int elementIndex, Stack<BeanDefinitionHolder> nestedBeanStack) {
        return processMessagePlaceholders(elementValue, nestedBeanStack);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String processSetStringPropertyValue(String propertyName, Set<?> propertyValue, String elementValue,
            Stack<BeanDefinitionHolder> nestedBeanStack) {
        return processMessagePlaceholders(elementValue, nestedBeanStack);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String processMapStringPropertyValue(String propertyName, Map<?, ?> propertyValue, String elementValue,
            Object elementKey, Stack<BeanDefinitionHolder> nestedBeanStack) {
        return processMessagePlaceholders(elementValue, nestedBeanStack);
    }

    /**
     * Retrieves external messages whose namespace and component matches the bean definition and applies
     * the message text to the bean property values
     *
     * @param beanName name of the bean to process
     * @param beanDefinition bean definition to process
     * @param nestedBeanStack stack of beans that contain the given bean, used for finding a namespace
     */
    protected void processBeanMessages(String beanName, BeanDefinition beanDefinition,
            Stack<BeanDefinitionHolder> nestedBeanStack) {
        Class<?> beanClass = getBeanClass(beanDefinition, beanFactory);
        if ((beanClass == null) || !(DictionaryBean.class.isAssignableFrom(beanClass)
                || ListFactoryBean.class.isAssignableFrom(beanClass))) {
            return;
        }

        String namespace = getNamespaceForBean(beanName, beanDefinition);
        if (StringUtils.isBlank(namespace)) {
            namespace = getNamespaceForBeanInStack(nestedBeanStack);
        }

        String componentCode = getComponentForBean(beanName, beanDefinition);
        if (StringUtils.equals(componentCode, beanName)) {
            // check if there is a parent bean in the factory using the standard suffix, if so we will skip this
            // bean as messages will be picked up by that parent bean definition. Note this is not for all parents,
            // just where the convention has been setup for extension (ex. 'bean' and 'bean-parentName')
            String extensionParentBeanName = beanName + KRADConstants.DICTIONARY_BEAN_PARENT_SUFFIX;
            if (beanFactory.containsBean(extensionParentBeanName)) {
                return;
            }
        }

        // if a namespace and component was found retrieve all messages associated with them
        if (StringUtils.isNotBlank(namespace) && StringUtils.isNotBlank(componentCode)) {
            Collection<Message> beanMessages = getMessageService().getAllMessagesForComponent(namespace,
                    componentCode);
            for (Message beanMessage : beanMessages) {
                applyMessageToBean(beanMessage, beanDefinition, beanClass);
            }
        }
    }

    /**
     * Applies the text for a given message to the associated bean definition property
     *
     * @param message message instance to apply
     * @param beanDefinition bean definition the message should be applied to
     * @param beanClass class for the bean definition
     */
    protected void applyMessageToBean(Message message, BeanDefinition beanDefinition, Class<?> beanClass) {
        String key = message.getKey().trim();

        // if message doesn't start with path indicator, it will be an explicit key that is matched when
        // iterating over the property values, so we will just return in that case
        if (!key.startsWith(KRADConstants.MESSAGE_KEY_PATH_INDICATOR)) {
            return;
        }

        // if here dealing with a path key, strip off indicator and then process as a property path
        key = StringUtils.stripStart(key, KRADConstants.MESSAGE_KEY_PATH_INDICATOR);

        // list factory beans just have the one list property (with no name)
        if (ListFactoryBean.class.isAssignableFrom(beanClass)) {
            MutablePropertyValues pvs = beanDefinition.getPropertyValues();

            PropertyValue propertyValue = pvs.getPropertyValueList().get(0);
            List<?> listValue = (List<?>) propertyValue.getValue();

            applyMessageToNestedListBean(message, listValue, key);
        } else if (StringUtils.contains(key, ".")) {
            applyMessageToNestedBean(message, beanDefinition, key);
        } else {
            applyMessageTextToPropertyValue(key, message.getText(), beanDefinition);
        }
    }

    /**
     * Applies the given message text to the property within the given bean definition
     *
     * <p>
     * The message text is applied to the bean definiton based on the property path. The path could be nested
     * in which case the property values of the bean definition are traversed to find the nested bean definition
     * that should hold the property value. The path could also represent a nested list bean. In this case a
     * helper method is called to find the correct list bean to apply the message to
     * </p>
     *
     * @param message message containing the text to apply
     * @param beanDefinition bean definition containing the property
     * @param propertyPath path to property within the bean definition the message should be applied to
     */
    protected void applyMessageToNestedBean(Message message, BeanDefinition beanDefinition, String propertyPath) {
        MutablePropertyValues pvs = beanDefinition.getPropertyValues();

        String beanPath = StringUtils.substringBefore(propertyPath, ".");
        String nestedPropertyPath = StringUtils.substringAfter(propertyPath, ".");

        boolean foundNestedBean = false;
        while (StringUtils.isNotBlank(nestedPropertyPath)) {
            if (pvs.contains(beanPath)) {
                PropertyValue propertyValue = pvs.getPropertyValue(beanPath);

                BeanDefinition propertyBeanDefinition = getPropertyValueBeanDefinition(propertyValue);
                if (propertyBeanDefinition != null) {
                    applyMessageToNestedBean(message, propertyBeanDefinition, nestedPropertyPath);

                    foundNestedBean = true;
                    break;
                } else if (propertyValue.getValue() instanceof List) {
                    applyMessageToNestedListBean(message, (List<?>) propertyValue.getValue(), nestedPropertyPath);

                    foundNestedBean = true;
                    break;
                }
            }

            beanPath += "." + StringUtils.substringBefore(nestedPropertyPath, ".");
            nestedPropertyPath = StringUtils.substringAfter(nestedPropertyPath, ".");
        }

        if (!foundNestedBean) {
            applyMessageTextToPropertyValue(propertyPath, message.getText(), beanDefinition);
        }
    }

    /**
     * Applies a message to a nested list bean definition
     *
     * <p>
     * Here the property path first gives an identifier value (such as property name) to use for finding
     * the bean definition with the list the message should apply to. Any part of the path after the identifier
     * value is treated like a nested property path
     * </p>
     *
     * @param message message instance that contains the text to apply
     * @param listPropertyValue property value list that should contain the bean definition the message
     * will be applied to
     * @param propertyPath path to the bean definition the message should apply to
     */
    protected void applyMessageToNestedListBean(Message message, List<?> listPropertyValue, String propertyPath) {
        // property path must be nested, with first part giving the list bean identifier value
        if (!StringUtils.contains(propertyPath, ".")) {
            throw new RiceRuntimeException(
                    "Key for nested list bean must contain the identifer value followed by the path.");
        }

        String listIdentifierPropertyValue = StringUtils.substringBefore(propertyPath, ".");
        String listBeanPropertyPath = StringUtils.substringAfter(propertyPath, ".");

        // iterate through list and find beans that match the given identifier
        for (int i = 0; i < listPropertyValue.size(); i++) {
            Object elem = listPropertyValue.get(i);

            if ((elem instanceof BeanDefinition) || (elem instanceof BeanDefinitionHolder)) {
                BeanDefinition beanDefinition;
                if (elem instanceof BeanDefinition) {
                    beanDefinition = (BeanDefinition) elem;
                } else {
                    beanDefinition = ((BeanDefinitionHolder) elem).getBeanDefinition();
                }

                boolean isMatch = isBeanMessageMatch(listIdentifierPropertyValue, beanDefinition);
                if (isMatch) {
                    if (StringUtils.contains(listBeanPropertyPath, ".")) {
                        applyMessageToNestedBean(message, beanDefinition, listBeanPropertyPath);
                    } else {
                        applyMessageTextToPropertyValue(listBeanPropertyPath, message.getText(), beanDefinition);
                    }
                }
            }
        }
    }

    /**
     * Determines whether the given bean definition is a matched based on the given identifier value
     *
     * <p>
     * Based on the class for the bean definition an identifier property name is used, the corresponding value
     * from the bean definition is then retrieved and compared to the given value to determine whether the
     * bean definition is a match
     * </p>
     *
     * @param matchListIdentifierPropertyValue property value to match bean definitions on
     * @param beanDefinition bean definition to determine the match for
     * @return boolean true if the bean definition is a match, false if not
     */
    protected boolean isBeanMessageMatch(String matchListIdentifierPropertyValue, BeanDefinition beanDefinition) {
        boolean isMatch = false;

        String listIdentifierPropertyName = null;

        Class<?> beanClass = getBeanClass(beanDefinition, beanFactory);
        if (DataBinding.class.isAssignableFrom(beanClass)) {
            listIdentifierPropertyName = KRADPropertyConstants.PROPERTY_NAME;
        } else if (Action.class.isAssignableFrom(beanClass) || ActionField.class.isAssignableFrom(beanClass)) {
            listIdentifierPropertyName = KRADPropertyConstants.METHOD_TO_CALL;
        } else if (KeyValue.class.isAssignableFrom(beanClass)) {
            listIdentifierPropertyName = KRADPropertyConstants.KEY;
        }

        if (StringUtils.isNotBlank(listIdentifierPropertyName)) {
            String listIdentifierPropertyValue = findPropertyValueInBeanDefinition(beanDefinition,
                    listIdentifierPropertyName);
            if (listIdentifierPropertyValue != null) {
                isMatch = StringUtils.equals(listIdentifierPropertyValue, matchListIdentifierPropertyValue);
            }
        }

        return isMatch;
    }

    /**
     * Attempts to find a property value for the given property name within the bean definition, if the property
     * does not exist in the bean and there is a parent, the parent is checked for containing the property. This
     * continues until a property value is found or all the parents have been traversed
     *
     * @param beanDefinition bean definition to find property value in
     * @param propertyName name of the property to find the value for
     * @return String value for property in the bean definition or null if the property was not found
     */
    protected String findPropertyValueInBeanDefinition(BeanDefinition beanDefinition, String propertyName) {
        String beanPropertyValue = null;

        MutablePropertyValues pvs = beanDefinition.getPropertyValues();
        if (pvs.contains(propertyName)) {
            PropertyValue propertyValue = pvs.getPropertyValue(propertyName);
            if (propertyValue.getValue() != null) {
                beanPropertyValue = propertyValue.getValue().toString();
            }
        } else {
            if (StringUtils.isNotBlank(beanDefinition.getParentName())) {
                BeanDefinition parentBeanDefinition = beanFactory.getBeanDefinition(beanDefinition.getParentName());

                beanPropertyValue = findPropertyValueInBeanDefinition(parentBeanDefinition, propertyName);
            }
        }

        return beanPropertyValue;
    }

    /**
     * Checks a string property value for a message placeholder and if found the message is retrieved and updated
     * in the property value
     *
     * @param propertyValue string value to process for message placeholders
     * @param nestedBeanStack stack of bean definitions that contain the property, used to determine the namespace
     * and component for the message retrieval
     * @return String new value for the property (possibly modified from an external message)
     */
    protected String processMessagePlaceholders(String propertyValue, Stack<BeanDefinitionHolder> nestedBeanStack) {
        String trimmedPropertyValue = StringUtils.stripStart(propertyValue, " ");
        if (StringUtils.isBlank(trimmedPropertyValue)) {
            return propertyValue;
        }

        String newPropertyValue = propertyValue;

        // first check for a replacement message key
        if (trimmedPropertyValue.startsWith(KRADConstants.MESSAGE_KEY_PLACEHOLDER_PREFIX)
                && StringUtils.contains(trimmedPropertyValue, KRADConstants.MESSAGE_KEY_PLACEHOLDER_SUFFIX)) {
            String messageKeyStr = StringUtils.substringBetween(trimmedPropertyValue,
                    KRADConstants.MESSAGE_KEY_PLACEHOLDER_PREFIX, KRADConstants.MESSAGE_KEY_PLACEHOLDER_SUFFIX);

            // get any default specified value (given after the message key)
            String messageKeyWithPlaceholder = KRADConstants.MESSAGE_KEY_PLACEHOLDER_PREFIX + messageKeyStr
                    + KRADConstants.MESSAGE_KEY_PLACEHOLDER_SUFFIX;

            String defaultPropertyValue = StringUtils.substringAfter(trimmedPropertyValue,
                    messageKeyWithPlaceholder);

            // set the new property value to the message text (if found), or the default value if a message was not found
            // note the message text could be an empty string, in which case it will override the default
            String messageText = getMessageTextForKey(messageKeyStr, nestedBeanStack);
            if (messageText != null) {
                // if default value set then we need to merge any expressions
                if (StringUtils.isNotBlank(defaultPropertyValue)) {
                    newPropertyValue = getMergedMessageText(messageText, defaultPropertyValue);
                } else {
                    newPropertyValue = messageText;
                }
            } else {
                newPropertyValue = defaultPropertyValue;
            }
        }
        // now check for message keys within an expression
        else if (StringUtils.contains(trimmedPropertyValue, KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_PREFIX)) {
            String[] expressionMessageKeys = StringUtils.substringsBetween(newPropertyValue,
                    KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_PREFIX,
                    KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_SUFFIX);

            for (String expressionMessageKey : expressionMessageKeys) {
                String expressionMessageText = getMessageTextForKey(expressionMessageKey, nestedBeanStack);
                newPropertyValue = StringUtils
                        .replace(newPropertyValue,
                                KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_PREFIX + expressionMessageKey
                                        + KRADConstants.EXPRESSION_MESSAGE_PLACEHOLDER_SUFFIX,
                                expressionMessageText);
            }
        }

        return newPropertyValue;
    }

    /**
     * Retrieves the test associated with the message give by the message key string
     *
     * @param messageKeyStr key string for the message, can contain just the key, or also the component and/or
     * namespace. If component or namespace not given it is determined from the bean stack
     * @param nestedBeanStack bean stack that contains the property for which the message applies
     * @return String test associated with the message
     */
    protected String getMessageTextForKey(String messageKeyStr, Stack<BeanDefinitionHolder> nestedBeanStack) {
        String namespace = null;
        String componentCode = null;
        String key = null;

        // check for specification of namespace and component
        if (StringUtils.contains(messageKeyStr, ":")) {
            String[] messageParams = StringUtils.split(messageKeyStr, ":");

            if (messageParams.length == 3) {
                namespace = messageParams[0];
                componentCode = messageParams[1];
                key = messageParams[2];
            } else if (messageParams.length == 2) {
                componentCode = messageParams[0];
                key = messageParams[1];
            } else {
                throw new RiceRuntimeException("Message key '" + messageKeyStr + "' has an invalid format");
            }
        } else {
            key = messageKeyStr;
        }

        if (StringUtils.isBlank(namespace)) {
            namespace = getNamespaceForBeanInStack(nestedBeanStack);
        }

        if (StringUtils.isBlank(componentCode)) {
            for (int i = nestedBeanStack.size() - 1; i >= 0; i--) {
                BeanDefinitionHolder definitionHolder = nestedBeanStack.get(i);
                componentCode = getComponentForBean(definitionHolder.getBeanName(),
                        definitionHolder.getBeanDefinition());
                if (StringUtils.isNotBlank(componentCode)) {
                    break;
                }
            }
        }

        String messageText = null;
        if (StringUtils.isNotBlank(namespace) && StringUtils.isNotBlank(componentCode)
                && StringUtils.isNotBlank(key)) {
            messageText = getMessageService().getMessageText(namespace, componentCode, key);
        }

        return messageText;
    }

    /**
     * Applies the given message text to the bean definition with the given property name, if a current
     * value exists for the property name the value is checked for expressions which are then merged with
     * the message
     *
     * @param propertyName - name of the property to set on the bean definition
     * @param messageText - message text that will be the property value
     * @param beanDefinition - bean definition to set property on
     */
    protected void applyMessageTextToPropertyValue(String propertyName, String messageText,
            BeanDefinition beanDefinition) {
        String newPropertyValue = messageText;

        MutablePropertyValues pvs = beanDefinition.getPropertyValues();
        if (pvs.contains(propertyName)) {
            PropertyValue propertyValue = pvs.getPropertyValue(propertyName);

            String stringPropertyValue = getStringValue(propertyValue.getValue());
            if (StringUtils.isNotBlank(stringPropertyValue)) {
                newPropertyValue = getMergedMessageText(messageText, stringPropertyValue);
            }
        }

        applyPropertyValueToBean(propertyName, newPropertyValue, pvs);
    }

    /**
     * Prepares the message text that will replace the property value checking for any expression placeholders
     *
     * <p>
     * The message text may contain placeholders (using brace delimiters) for expression placement. It is
     * expected when these placeholders are given the property value contains the expressions (using the
     * expression placeholders) that will be inserted into the message text
     * </p>
     *
     * @param messageText - raw text of the message
     * @param propertyValue - current value for the property
     * @return String the message text with expressions inserted (if any expressions were found)
     */
    protected String getMergedMessageText(String messageText, String propertyValue) {
        String mergedText = messageText;

        String[] expressions = StringUtils.substringsBetween(propertyValue, UifConstants.EL_PLACEHOLDER_PREFIX,
                UifConstants.EL_PLACEHOLDER_SUFFIX);
        if ((expressions != null) && expressions.length > 0) {
            // add expression placeholders back on
            String[] messageParameters = new String[expressions.length];
            for (int i = 0; i < expressions.length; i++) {
                String expression = expressions[i];

                expression = UifConstants.EL_PLACEHOLDER_PREFIX + expression + UifConstants.EL_PLACEHOLDER_SUFFIX;
                messageParameters[i] = expression;
            }

            // escape single quotes for message format process
            messageText = messageText.replace("'", "''");
            try {
                mergedText = MessageFormat.format(messageText, messageParameters);
            } catch (IllegalArgumentException e) {
                throw new RiceRuntimeException(
                        "Unable to merge expressions with message text. Expression count is: " + expressions.length,
                        e);
            }
        }

        return mergedText;
    }

    /**
     * Walks up the stack of bean definitions until a namespace is found and returns that namespace
     *
     * @param nestedBeanStack stack of bean definitions to find namespace for
     * @return String namespace found in stack or null if one was not found
     */
    protected String getNamespaceForBeanInStack(Stack<BeanDefinitionHolder> nestedBeanStack) {
        String namespace = null;

        if (nestedBeanStack != null) {
            for (int i = nestedBeanStack.size() - 1; i >= 0; i--) {
                BeanDefinitionHolder definitionHolder = nestedBeanStack.get(i);
                namespace = getNamespaceForBean(definitionHolder.getBeanName(),
                        definitionHolder.getBeanDefinition());
                if (StringUtils.isNotBlank(namespace)) {
                    break;
                }
            }
        }

        return namespace;
    }

    /**
     * Retrieves the namespace associated with the bean definition
     *
     * @param beanName name of the bean to find namespace for
     * @param beanDefinition bean definition to find namespace for
     * @return String namespace for bean or null if a namespace was not found
     */
    protected String getNamespaceForBean(String beanName, BeanDefinition beanDefinition) {
        String namespace = null;

        MutablePropertyValues pvs = beanDefinition.getPropertyValues();
        if (pvs.contains(KRADPropertyConstants.NAMESPACE_CODE)) {
            PropertyValue propertyValue = pvs.getPropertyValue(KRADPropertyConstants.NAMESPACE_CODE);
            namespace = getStringValue(propertyValue.getValue());
        } else if (StringUtils.isNotBlank(beanName) && !isGeneratedBeanName(beanName)) {
            // if not on bean definition, get from associated module in the dictionary
            namespace = dataDictionary.getNamespaceForBeanDefinition(beanName);
        }

        return namespace;
    }

    /**
     * Retrieves the component code associated with the bean definition
     *
     * @param beanName name of the bean to find component code for
     * @param beanDefinition bean definition to find component code for
     * @return String component code for bean or null if a component code was not found
     */
    protected String getComponentForBean(String beanName, BeanDefinition beanDefinition) {
        String componentCode = null;

        MutablePropertyValues pvs = beanDefinition.getPropertyValues();
        if (pvs.contains(KRADPropertyConstants.COMPONENT_CODE)) {
            PropertyValue propertyValue = pvs.getPropertyValue(KRADPropertyConstants.COMPONENT_CODE);

            componentCode = getStringValue(propertyValue.getValue());
        }

        if ((componentCode == null) && StringUtils.isNotBlank(beanName) && !isGeneratedBeanName(beanName)) {
            componentCode = beanName;
        }

        if (StringUtils.isNotBlank(componentCode)) {
            componentCode = StringUtils.removeEnd(componentCode, KRADConstants.DICTIONARY_BEAN_PARENT_SUFFIX);
        }

        return componentCode;
    }

    /**
     * Returns instance of the Message Service
     *
     * @return MessageService isntanc
     */
    protected MessageService getMessageService() {
        return KRADServiceLocatorWeb.getMessageService();
    }
}