
org.apache.sling.i18n.impl.JcrResourceBundle Maven / Gradle / Ivy
Show all versions of org.apache.sling.i18n Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.sling.i18n.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.apache.jackrabbit.commons.json.JsonHandler;
import org.apache.jackrabbit.commons.json.JsonParser;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JcrResourceBundle extends ResourceBundle {
private static final Logger log = LoggerFactory.getLogger(JcrResourceBundle.class);
/** default primary type (=resource type) for message entry dictionaries */
static final String RT_MESSAGE_ENTRY = "sling:MessageEntry";
static final String MIXIN_MESSAGE = "sling:Message";
static final String MIXIN_LANGUAGE = "mix:language";
static final String PROP_KEY = "sling:key";
static final String PROP_VALUE = "sling:message";
static final String PROP_BASENAME = "sling:basename";
static final String PROP_LANGUAGE = "jcr:language";
static final String PROP_MIXINS = "jcr:mixinTypes";
static final String QUERY_LANGUAGE_ROOTS = "//element(*,mix:language)[@jcr:language]";
private final Map resources;
private final Locale locale;
private final String baseName;
private final Set languageRoots = new HashSet();
JcrResourceBundle(Locale locale, String baseName,
ResourceResolver resourceResolver) {
this.locale = locale;
this.baseName = baseName;
log.info("Finding all dictionaries for '{}' (basename: {}) ...", locale, baseName == null ? "" : baseName);
long start = System.currentTimeMillis();
resourceResolver.refresh();
Set roots = loadPotentialLanguageRoots(resourceResolver, locale, baseName);
this.resources = loadFully(resourceResolver, roots, this.languageRoots);
long end = System.currentTimeMillis();
if (log.isInfoEnabled()) {
log.info(
"Finished loading {} entries for '{}' (basename: {}) in {}ms",
new Object[] { resources.size(), locale, baseName == null ? "" : baseName, (end - start)}
);
}
}
protected Set getLanguageRootPaths() {
return languageRoots;
}
@Override
protected void setParent(ResourceBundle parent) {
super.setParent(parent);
}
public ResourceBundle getParent() {
return parent;
}
@Override
public Locale getLocale() {
return locale;
}
public String getBaseName() {
return baseName;
}
/**
* Returns a Set of all resource keys provided by this resource bundle only.
*
* This method is a new Java 1.6 method to implement the
* ResourceBundle.keySet() method.
*
* @return The keys of the resources provided by this resource bundle
*/
@Override
protected Set handleKeySet() {
return resources.keySet();
}
@Override
public Enumeration getKeys() {
Enumeration parentKeys = (parent != null)
? parent.getKeys()
: null;
return new ResourceBundleEnumeration(resources.keySet(), parentKeys);
}
@Override
protected Object handleGetObject(String key) {
if (log.isDebugEnabled()) {
log.debug("Requesting key '{}' from resource bundle (baseName '{}', locale '{}')", new Object[] {key, baseName, locale});
}
return resources.get(key);
}
/**
* Fully loads the resource bundle from the storage.
*
* This method adds entries to the {@code languageRoots} set of strings.
* Therefore this method must not be called concurrently or the set
* must either be thread safe.
*
* @param resolver The storage access (must not be {@code null})
* @param roots The set of (potential) dictionary subtrees. This must
* not be {@code null}. If empty, no resources will actually be
* loaded.
* @param languageRoots The set of actually dictionary subtrees. While
* processing the resources, all subtrees listed in the {@code roots}
* set is added to this set if it actually contains resources. This
* must not be {@code null}.
* @return
*
* @throws NullPointerException if either of the parameters is {@code null}.
*/
@SuppressWarnings("deprecation")
private Map loadFully(final ResourceResolver resolver, Set roots, Set languageRoots) {
final String[] searchPath = resolver.getSearchPath();
// for each search path entry, have a list of maps (dictionaries)
// plus other = "outside the search path" at the end
// [0] /apps2 -> [dict1, dict2, dict3 ...]
// [1] /apps -> [dict4, dict5, ...]
// [2] /libs -> [dict6, ...]
// [3] (other) -> [dict7, dict8 ...]
List>> dictionariesBySearchPath = new ArrayList>>(searchPath.length + 1);
for (int i = 0; i < searchPath.length + 1; i++) {
dictionariesBySearchPath.add(new ArrayList