/* * 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.felix.webconsole.internal.i18n; import java.net.URL; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; /** * The <code>ResourceBundleCache</code> caches resource bundles per OSGi bundle. */ public class ResourceBundleCache { /** * The default locale corresponding to the default language in the * bundle.properties file, which is English. * (FELIX-1957 The Locale(String) constructor used before is not available * in the OSGi/Minimum-1.1 profile and should be prevented) */ private static final Locale DEFAULT_LOCALE = Locale.ENGLISH; private final Bundle bundle; private final Map resourceBundles; private Map resourceBundleEntries; /** * Creates a new object * * @param bundle the bundle which resources should be loaded. */ public ResourceBundleCache( final Bundle bundle ) { this.bundle = bundle; this.resourceBundles = new HashMap(); } /** * Gets the resource bundle for the specified locale. * * @param locale the requested locale * @return the resource bundle for the requested locale */ public ResourceBundle getResourceBundle( final Locale locale ) { if ( locale == null ) { return getResourceBundleInternal( DEFAULT_LOCALE ); } return getResourceBundleInternal( locale ); } ResourceBundle getResourceBundleInternal( final Locale locale ) { if ( locale == null ) { return null; } synchronized ( resourceBundles ) { ResourceBundle bundle = ( ResourceBundle ) resourceBundles.get( locale ); if ( bundle != null ) { return bundle; } } ResourceBundle parent = getResourceBundleInternal( getParentLocale( locale ) ); ResourceBundle bundle = loadResourceBundle( parent, locale ); synchronized ( resourceBundles ) { resourceBundles.put( locale, bundle ); } return bundle; } private ResourceBundle loadResourceBundle( final ResourceBundle parent, final Locale locale ) { final String path = "_" + locale.toString(); //$NON-NLS-1$ final URL source = ( URL ) getResourceBundleEntries().get( path ); return new ConsolePropertyResourceBundle( parent, source ); } private synchronized Map getResourceBundleEntries() { if ( this.resourceBundleEntries == null ) { String file = ( String ) bundle.getHeaders().get( Constants.BUNDLE_LOCALIZATION ); if ( file == null ) { file = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME; } // remove leading slash if ( file.startsWith( "/" ) ) //$NON-NLS-1$ { file = file.substring( 1 ); } // split path and base name int slash = file.lastIndexOf( '/' ); String fileName = file.substring( slash + 1 ); String path = ( slash <= 0 ) ? "/" : file.substring( 0, slash ); //$NON-NLS-1$ HashMap resourceBundleEntries = new HashMap(); Enumeration locales = bundle.findEntries( path, fileName + "*.properties", false ); //$NON-NLS-1$ if ( locales != null ) { while ( locales.hasMoreElements() ) { URL entry = ( URL ) locales.nextElement(); // calculate the key String entryPath = entry.getPath(); final int start = entryPath.lastIndexOf( '/' ) + 1 + fileName.length(); // path, slash and base name final int end = entryPath.length() - 11; // .properties suffix entryPath = entryPath.substring( start, end ); // the default language is "name.properties" thus the entry // path is empty and must default to "_"+DEFAULT_LOCALE if (entryPath.length() == 0) { entryPath = "_" + DEFAULT_LOCALE; //$NON-NLS-1$ } // only add this entry, if the "language" is not provided // by the main bundle or an earlier bound fragment if (!resourceBundleEntries.containsKey( entryPath )) { resourceBundleEntries.put( entryPath, entry ); } } } this.resourceBundleEntries = resourceBundleEntries; } return this.resourceBundleEntries; } private static final Locale getParentLocale( Locale locale ) { if ( locale.getVariant().length() != 0 ) { return new Locale( locale.getLanguage(), locale.getCountry() ); } else if ( locale.getCountry().length() != 0 ) { return new Locale( locale.getLanguage(), "" ); //$NON-NLS-1$ } else if ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) { return DEFAULT_LOCALE; } // no more parents return null; } }