/*
* Copyright 2012 Jason Miller
*
* 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 jj.i18n;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import jj.application.AppLocation;
import jj.resource.AbstractResource;
import jj.resource.NoSuchResourceException;
import jj.resource.ResourceFinder;
import jj.util.SHA1Helper;
/**
* <p>
* Represents a collection of {@link PropertiesResource}s that are accessed in
* a manner similar to the {@link java.util.PropertyResourceBundle}, although
* simpler.
*
* <p>
* Identify by a name and a {@link Locale}. Candidate resource names are generated
* based on this combination. For example, given the name "index" and {@link Locale#US}
* then an attempt will be made to load the following resources:
* <ul>
* <li>index_en_US.properties
* <li>index_en.properties
* <li>index.properties
* </ul>
*
* <p>
* Retrieve messages by key. PropertiesResources will be checked from most
* to least specific and the first result found will be returned. If no match is
* found, null is returned.
*
* @author jason
*
*/
public class MessagesResource extends AbstractResource<Locale> {
// doubled because java8 is a pain
private static final String __ = "_";
private static final String EXT = ".properties";
private final Locale locale;
private final String sha;
private final Map<String, String> messages;
@Inject
MessagesResource(
final Dependencies dependencies,
final Locale locale,
final ResourceFinder resourceFinder
) {
super(dependencies);
this.locale = locale;
ArrayList<PropertiesResource> propertiesResources = findResources(resourceFinder);
if (propertiesResources.isEmpty()) {
throw new NoSuchResourceException(MessagesResource.class, name());
}
String[] shas = new String[propertiesResources.size()];
int count = 0;
HashMap<String, String> messagesMaker = new HashMap<>();
for (PropertiesResource r : propertiesResources) {
shas[count++] = r.sha1();
messagesMaker.putAll(r.properties());
}
messages = Collections.unmodifiableMap(messagesMaker);
sha = SHA1Helper.keyFor(shas);
}
private ArrayList<PropertiesResource> findResources(final ResourceFinder resourceFinder) {
ArrayList<PropertiesResource> result = new ArrayList<>(4);
for (String candidateName : candidateNames()) {
PropertiesResource resource =
resourceFinder.loadResource(PropertiesResource.class, AppLocation.Private, candidateName);
if (resource != null) {
result.add(resource);
resource.addDependent(this);
} // else somehow flag that we want to know if it gets created
}
return result;
}
private String[] candidateNames() {
String name = name();
return new String[] {
name + EXT,
name + __ + locale.getLanguage() + EXT,
name + __ + locale.getLanguage() + __ + locale.getCountry() + EXT,
name + __ + locale.getLanguage() + __ + locale.getCountry() + __ + locale.getVariant() + EXT
};
}
public Locale locale() {
return locale;
}
@Override
public String sha1() {
return sha;
}
@Override
public Locale creationArg() {
return locale;
}
public boolean containsKey(String key) {
return messages.containsKey(key);
}
public String message(String key) {
return messages.get(key);
}
public Set<String> keys() {
return messages.keySet();
}
@Override
protected boolean removeOnReload() {
// this is a root resource
return false;
}
@Override
public boolean needsReplacing() throws IOException {
// always replaced by dependencies
return false;
}
}