JsonMessageSource.java
/*******************************************************************************
* Copyright 2017 The MIT Internet Trust Consortium
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* 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.mitre.openid.connect.config;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.Resource;
import com.google.common.base.Splitter;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
/**
* @author jricher
*
*/
public class JsonMessageSource extends AbstractMessageSource {
// Logger for this class
private static final Logger logger = LoggerFactory.getLogger(JsonMessageSource.class);
private Resource baseDirectory;
private Locale fallbackLocale = new Locale("en"); // US English is the fallback language
private Map<Locale, List<JsonObject>> languageMaps = new HashMap<>();
@Autowired
private ConfigurationPropertiesBean config;
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
List<JsonObject> langs = getLanguageMap(locale);
String value = getValue(code, langs);
if (value == null) {
// if we haven't found anything, try the default locale
langs = getLanguageMap(fallbackLocale);
value = getValue(code, langs);
}
if (value == null) {
// if it's still null, return null
return null;
} else {
// otherwise format the message
return new MessageFormat(value, locale);
}
}
/**
* Get a value from the set of maps, taking the first match in order
* @param code
* @param langs
* @return
*/
private String getValue(String code, List<JsonObject> langs) {
if (langs == null || langs.isEmpty()) {
// no language maps, nothing to look up
return null;
}
for (JsonObject lang : langs) {
String value = getValue(code, lang);
if (value != null) {
// short circuit out of here if we find a match, otherwise keep going through the list
return value;
}
}
// if we didn't find anything return null
return null;
}
/**
* Get a value from a single map
* @param code
* @param locale
* @param lang
* @return
*/
private String getValue(String code, JsonObject lang) {
// if there's no language map, nothing to look up
if (lang == null) {
return null;
}
JsonElement e = lang;
Iterable<String> parts = Splitter.on('.').split(code);
Iterator<String> it = parts.iterator();
String value = null;
while (it.hasNext()) {
String p = it.next();
if (e.isJsonObject()) {
JsonObject o = e.getAsJsonObject();
if (o.has(p)) {
e = o.get(p); // found the next level
if (!it.hasNext()) {
// we've reached a leaf, grab it
if (e.isJsonPrimitive()) {
value = e.getAsString();
}
}
} else {
// didn't find it, stop processing
break;
}
} else {
// didn't find it, stop processing
break;
}
}
return value;
}
/**
* @param locale
* @return
*/
private List<JsonObject> getLanguageMap(Locale locale) {
if (!languageMaps.containsKey(locale)) {
try {
List<JsonObject> set = new ArrayList<>();
for (String namespace : config.getLanguageNamespaces()) {
// full locale string, e.g. "en_US"
String filename = locale.getLanguage() + "_" + locale.getCountry() + File.separator + namespace + ".json";
Resource r = getBaseDirectory().createRelative(filename);
if (!r.exists()) {
// fallback to language only
logger.debug("Fallback locale to language only.");
filename = locale.getLanguage() + File.separator + namespace + ".json";
r = getBaseDirectory().createRelative(filename);
}
logger.info("No locale loaded, trying to load from " + r);
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(new InputStreamReader(r.getInputStream(), "UTF-8"));
set.add(obj);
}
languageMaps.put(locale, set);
} catch (JsonIOException | JsonSyntaxException | IOException e) {
logger.error("Unable to load locale", e);
}
}
return languageMaps.get(locale);
}
/**
* @return the baseDirectory
*/
public Resource getBaseDirectory() {
return baseDirectory;
}
/**
* @param baseDirectory the baseDirectory to set
*/
public void setBaseDirectory(Resource baseDirectory) {
this.baseDirectory = baseDirectory;
}
}