001/**
002 * Copyright 2005-2018 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 */
016package org.kuali.rice.krad.messages.providers;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.exception.RiceRuntimeException;
020import org.kuali.rice.core.api.search.SearchOperator;
021import org.kuali.rice.krad.messages.Message;
022import org.kuali.rice.krad.messages.MessageProvider;
023import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
024import org.kuali.rice.krad.service.LookupService;
025
026import java.util.Collection;
027import java.util.HashMap;
028import java.util.Map;
029
030/**
031 * Implementation of {@link MessageProvider} that stores messages in a database
032 *
033 * @author Kuali Rice Team (rice.collab@kuali.org)
034 */
035public class DatabaseMessageProvider implements MessageProvider {
036
037    private LookupService lookupService;
038
039    /**
040     * @see org.kuali.rice.krad.messages.MessageProvider#getMessage(java.lang.String, java.lang.String,
041     * java.lang.String, java.lang.String)
042     */
043    public Message getMessage(String namespace, String component, String key, String locale) {
044        Collection<Message> results = getMessageByCriteria(namespace, component, key, locale);
045
046        if ((results != null) && !results.isEmpty()) {
047            return results.iterator().next();
048        }
049
050        return null;
051    }
052
053    /**
054     * @see org.kuali.rice.krad.messages.MessageProvider#getAllMessagesForComponent(java.lang.String,
055     * java.lang.String, java.lang.String)
056     */
057    public Collection<Message> getAllMessagesForComponent(String namespace, String component, String locale) {
058        return getMessageByCriteria(namespace, component, null, locale);
059    }
060
061    /**
062     * Performs a query using the {@link LookupService} to retrieve messages that match the given
063     * namespace, component, name, and locale. Not parameters maybe empty in which case they will not be added
064     * to the criteria
065     *
066     * @param namespace namespace code to search for
067     * @param component component code to search for
068     * @param key key of the parameter to find
069     * @param locale locale code to search for
070     * @return Collection<Message> matching messages or empty collection if not are found
071     */
072    protected Collection<Message> getMessageByCriteria(String namespace, String component, String key, String locale) {
073        Collection<Message> results = null;
074
075        Map<String, String> criteria = new HashMap<String, String>();
076
077        if (StringUtils.isNotBlank(namespace)) {
078            criteria.put("namespaceCode", namespace);
079        }
080
081        if (StringUtils.isNotBlank(component)) {
082            criteria.put("componentCode", component);
083        }
084
085        if (StringUtils.isNotBlank(key)) {
086            criteria.put("key", key);
087        }
088
089        if (StringUtils.isNotBlank(locale)) {
090            // build or condition that will match just the language as well
091            String[] localeIdentifiers = StringUtils.split(locale, "-");
092            if ((localeIdentifiers == null) || (localeIdentifiers.length != 2)) {
093                throw new RiceRuntimeException("Invalid locale code: " + (locale == null ? "Null" : locale));
094            }
095
096            String localeLanguage = localeIdentifiers[0];
097            if ( StringUtils.isNotBlank(localeLanguage)) {
098                criteria.put("locale", locale + SearchOperator.OR.op() + localeLanguage);
099            } else {
100                criteria.put("locale", locale );
101            }
102        }
103
104        results = getLookupService().findCollectionBySearch(Message.class, criteria);
105
106        // filter out duplicate message results due to locale wildcard search (for example could have a match 
107        // for en and en-US, in which case we want to take the record for the more specific locale
108        Map<String, Message> uniqueMessages = new HashMap<String, Message>();
109        for (Message message : results) {
110            String messageKey = message.getNamespaceCode() + "|" + message.getComponentCode() + "|" + message.getKey();
111            if (uniqueMessages.containsKey(messageKey)) {
112                Message duplicateMessage = uniqueMessages.get(messageKey);
113                // attempt to find the one that matches the locale exactly
114                if (message.getLocale().equals(locale)) {
115                    // use current message, otherwise leave the previous message
116                    uniqueMessages.put(messageKey, message);
117                }
118            } else {
119                uniqueMessages.put(messageKey, message);
120            }
121        }
122
123        return uniqueMessages.values();
124    }
125
126    public LookupService getLookupService() {
127        if (lookupService == null) {
128            lookupService = KRADServiceLocatorWeb.getLookupService();
129        }
130
131        return lookupService;
132    }
133
134    public void setLookupService(LookupService lookupService) {
135        this.lookupService = lookupService;
136    }
137
138}