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