/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.service.resources;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
/**
* The abstract class for ResourceManagementService. It listens for
* {@link ResourcePack} that are registered and exposes them later for use by
* subclasses. It implements default behaviour for most methods.
*/
public abstract class AbstractResourcesService
implements ResourceManagementService,
ServiceListener
{
/**
* The logger
*/
private static final Logger logger =
Logger.getLogger(AbstractResourcesService.class);
/**
* The OSGI BundleContext
*/
private BundleContext bundleContext;
/**
* Resources for currently loaded <tt>SettingsPack</tt>.
*/
private Map<String, String> settingsResources;
/**
* Currently loaded settings pack.
*/
private ResourcePack settingsPack = null;
/**
* Resources for currently loaded <tt>LanguagePack</tt>.
*/
private Map<String, String> languageResources;
/**
* Currently loaded language pack.
*/
private LanguagePack languagePack = null;
/**
* The {@link Locale} of <code>languageResources</code> so that the caching
* of the latter can be used when a string with the same <code>Locale</code>
* is requested.
*/
private Locale languageLocale;
/**
* Resources for currently loaded <tt>ImagePack</tt>.
*/
private Map<String, String> imageResources;
/**
* Currently loaded image pack.
*/
private ImagePack imagePack = null;
/**
* Resources for currently loaded <tt>ColorPack</tt>.
*/
private Map<String, String> colorResources;
/**
* Currently loaded color pack.
*/
private ResourcePack colorPack = null;
/**
* Resources for currently loaded <tt>SoundPack</tt>.
*/
private Map<String, String> soundResources;
/**
* Currently loaded sound pack.
*/
private ResourcePack soundPack = null;
/**
* Currently loaded <tt>SkinPack</tt>.
*/
private SkinPack skinPack = null;
/**
* Creates an instance of <tt>AbstractResourcesService</tt>.
*
* @param bundleContext the OSGi bundle context
*/
public AbstractResourcesService(BundleContext bundleContext)
{
this.bundleContext = bundleContext;
bundleContext.addServiceListener(this);
colorPack
= getDefaultResourcePack(
ColorPack.class,
ColorPack.RESOURCE_NAME_DEFAULT_VALUE);
if (colorPack != null)
colorResources = getResources(colorPack);
imagePack
= getDefaultResourcePack(
ImagePack.class,
ImagePack.RESOURCE_NAME_DEFAULT_VALUE);
if (imagePack != null)
imageResources = getResources(imagePack);
// changes the default locale if set in the config
ConfigurationService confService =
ServiceUtils.getService( bundleContext, ConfigurationService.class);
String defaultLocale =
(String) confService.getProperty(DEFAULT_LOCALE_CONFIG);
if(defaultLocale != null)
Locale.setDefault(
ResourceManagementServiceUtils.getLocale(defaultLocale));
languagePack
= getDefaultResourcePack(
LanguagePack.class,
LanguagePack.RESOURCE_NAME_DEFAULT_VALUE);
if (languagePack != null)
{
languageLocale = Locale.getDefault();
languageResources = languagePack.getResources(languageLocale);
}
settingsPack
= getDefaultResourcePack(
SettingsPack.class,
SettingsPack.RESOURCE_NAME_DEFAULT_VALUE);
if (settingsPack != null)
settingsResources = getResources(settingsPack);
soundPack
= getDefaultResourcePack(
SoundPack.class,
SoundPack.RESOURCE_NAME_DEFAULT_VALUE);
if (soundPack != null)
soundResources = getResources(soundPack);
skinPack
= getDefaultResourcePack(
SkinPack.class,
SkinPack.RESOURCE_NAME_DEFAULT_VALUE);
if (skinPack != null)
{
if (imageResources != null)
imageResources.putAll(skinPack.getImageResources());
colorResources.putAll(skinPack.getColorResources());
settingsResources.putAll(skinPack.getSettingsResources());
}
}
/**
* Handles all <tt>ServiceEvent</tt>s corresponding to <tt>ResourcePack</tt>
* being registered or unregistered.
*
* @param event the <tt>ServiceEvent</tt> that notified us
*/
public void serviceChanged(ServiceEvent event)
{
Object sService = bundleContext.getService(
event.getServiceReference());
if (!(sService instanceof ResourcePack))
{
return;
}
ResourcePack resourcePack = (ResourcePack) sService;
if (event.getType() == ServiceEvent.REGISTERED)
{
if (logger.isInfoEnabled())
logger.info("Resource registered " + resourcePack);
Map<String, String> resources = getResources(resourcePack);
if(resourcePack instanceof ColorPack && colorPack == null)
{
colorPack = resourcePack;
colorResources = resources;
}
else if(resourcePack instanceof ImagePack && imagePack == null)
{
imagePack = (ImagePack) resourcePack;
imageResources = resources;
}
else if(resourcePack instanceof LanguagePack
&& languagePack == null)
{
languagePack = (LanguagePack) resourcePack;
languageLocale = Locale.getDefault();
languageResources = resources;
}
else if(resourcePack instanceof SettingsPack
&& settingsPack == null)
{
settingsPack = resourcePack;
settingsResources = resources;
}
else if(resourcePack instanceof SoundPack && soundPack == null)
{
soundPack = resourcePack;
soundResources = resources;
}
else if(resourcePack instanceof SkinPack && skinPack == null)
{
skinPack = (SkinPack) resourcePack;
if (imagePack!=null)
imageResources = getResources(imagePack);
if (colorPack!=null)
colorResources = getResources(colorPack);
if (settingsPack != null)
settingsResources = getResources(settingsPack);
if (imageResources != null)
imageResources.putAll(skinPack.getImageResources());
colorResources.putAll(skinPack.getColorResources());
settingsResources.putAll(skinPack.getSettingsResources());
onSkinPackChanged();
}
}
else if (event.getType() == ServiceEvent.UNREGISTERING)
{
if(resourcePack instanceof ColorPack
&& colorPack.equals(resourcePack))
{
colorPack
= getDefaultResourcePack(
ColorPack.class,
ColorPack.RESOURCE_NAME_DEFAULT_VALUE);
if (colorPack != null)
colorResources = getResources(colorPack);
}
else if(resourcePack instanceof ImagePack
&& imagePack.equals(resourcePack))
{
imagePack
= getDefaultResourcePack(
ImagePack.class,
ImagePack.RESOURCE_NAME_DEFAULT_VALUE);
if (imagePack != null)
imageResources = getResources(imagePack);
}
else if(resourcePack instanceof LanguagePack
&& languagePack.equals(resourcePack))
{
languagePack
= getDefaultResourcePack(
LanguagePack.class,
LanguagePack.RESOURCE_NAME_DEFAULT_VALUE);
}
else if(resourcePack instanceof SettingsPack
&& settingsPack.equals(resourcePack))
{
settingsPack
= getDefaultResourcePack(
SettingsPack.class,
SettingsPack.RESOURCE_NAME_DEFAULT_VALUE);
if (settingsPack != null)
settingsResources = getResources(settingsPack);
}
else if(resourcePack instanceof SoundPack
&& soundPack.equals(resourcePack))
{
soundPack
= getDefaultResourcePack(
SoundPack.class,
SoundPack.RESOURCE_NAME_DEFAULT_VALUE);
if (soundPack != null)
soundResources = getResources(soundPack);
}
else if(resourcePack instanceof SkinPack
&& skinPack.equals(resourcePack))
{
if(imagePack!=null)
{
imageResources = getResources(imagePack);
}
if(colorPack!=null)
{
colorResources = getResources(colorPack);
}
if(settingsPack!=null)
{
settingsResources = getResources(settingsPack);
}
skinPack
= getDefaultResourcePack(
SkinPack.class,
SkinPack.RESOURCE_NAME_DEFAULT_VALUE);
if (skinPack != null)
{
imageResources.putAll(skinPack.getImageResources());
colorResources.putAll(skinPack.getColorResources());
settingsResources.putAll(skinPack.getSettingsResources());
}
onSkinPackChanged();
}
}
}
/**
* Method is invoked when the SkinPack is loaded or unloaded.
*/
protected abstract void onSkinPackChanged();
/**
* Searches for the <tt>ResourcePack</tt> corresponding to the given
* <tt>className</tt> and <tt></tt>.
*
* @param className The name of the resource class.
* @param typeName The name of the type we're looking for.
* For example: RESOURCE_NAME_DEFAULT_VALUE
* @return the <tt>ResourcePack</tt> corresponding to the given
* <tt>className</tt> and <tt></tt>.
*/
protected <T extends ResourcePack> T getDefaultResourcePack(
Class<T> clazz,
String typeName)
{
Collection<ServiceReference<T>> serRefs;
String osgiFilter
= "(" + ResourcePack.RESOURCE_NAME + "=" + typeName + ")";
try
{
serRefs = bundleContext.getServiceReferences(clazz, osgiFilter);
}
catch (InvalidSyntaxException ex)
{
serRefs = null;
logger.error("Could not obtain resource packs reference.", ex);
}
if ((serRefs != null) && !serRefs.isEmpty())
{
return bundleContext.getService(serRefs.iterator().next());
}
return null;
}
/**
* Returns the <tt>Map</tt> of (key, value) pairs contained in the given
* resource pack.
*
* @param resourcePack The <tt>ResourcePack</tt> from which we're obtaining
* the resources.
* @return the <tt>Map</tt> of (key, value) pairs contained in the given
* resource pack.
*/
protected Map<String, String> getResources(ResourcePack resourcePack)
{
return resourcePack.getResources();
}
/**
* All the locales in the language pack.
* @return all the locales this Language pack contains.
*/
public Iterator<Locale> getAvailableLocales()
{
return languagePack.getAvailableLocales();
}
/**
* Returns the string for given <tt>key</tt> for specified <tt>locale</tt>.
* It's the real process of retrieving string for specified locale.
* The result is used in other methods that operate on localized strings.
*
* @param key the key name for the string
* @param locale the Locale of the string
* @return the resources string corresponding to the given <tt>key</tt> and
* <tt>locale</tt>
*/
protected String doGetI18String(String key, Locale locale)
{
Map<String, String> stringResources;
if ((locale != null) && locale.equals(languageLocale))
{
stringResources = languageResources;
}
else
{
stringResources
= (languagePack == null)
? null
: languagePack.getResources(locale);
}
String resourceString =
(stringResources == null) ? null : stringResources.get(key);
return resourceString;
}
/**
* Returns an internationalized string corresponding to the given key.
*
* @param key The identifier of the string in the resources properties file.
* @return An internationalized string corresponding to the given key.
*/
public String getI18NString(String key)
{
return getI18NString(key, null, Locale.getDefault());
}
/**
* Returns an internationalized string corresponding to the given key.
*
* @param key The identifier of the string.
* @param params the parameters to pass to the localized string
* @return An internationalized string corresponding to the given key.
*/
public String getI18NString(String key, String[] params)
{
return getI18NString(key, params, Locale.getDefault());
}
/**
* Returns an internationalized string corresponding to the given key.
*
* @param key The identifier of the string in the resources properties file.
* @param locale The locale.
* @return An internationalized string corresponding to the given key and
* given locale.
*/
public String getI18NString(String key, Locale locale)
{
return getI18NString(key, null, locale);
}
/**
* Does the additional processing on the resource string. It removes "&"
* marks used for mnemonics and other characters.
*
* @param resourceString the resource string to be processed
* @return the processed string
*/
private String processI18NString(String resourceString)
{
if(resourceString == null)
return null;
int mnemonicIndex = resourceString.indexOf('&');
if (mnemonicIndex == 0
|| (mnemonicIndex > 0
&& resourceString.charAt(mnemonicIndex - 1) != '\\'))
{
String firstPart = resourceString.substring(0, mnemonicIndex);
String secondPart = resourceString.substring(mnemonicIndex + 1);
resourceString = firstPart.concat(secondPart);
}
if (resourceString.indexOf('\\') > -1)
{
resourceString = resourceString.replaceAll("\\\\", "");
}
if (resourceString.indexOf("''") > -1)
{
resourceString = resourceString.replaceAll("''", "'");
}
return resourceString;
}
/**
* Returns an internationalized string corresponding to the given key.
*
* @param key The identifier of the string in the resources properties
* file.
* @param params the parameters to pass to the localized string
* @param locale The locale.
* @return An internationalized string corresponding to the given key.
*/
public String getI18NString(String key, String[] params, Locale locale)
{
String resourceString = doGetI18String(key, locale);
if (resourceString == null)
{
logger.warn("Missing resource for key: " + key);
return '!' + key + '!';
}
if(params != null)
{
resourceString
= MessageFormat.format(resourceString, (Object[]) params);
}
return processI18NString(resourceString);
}
/**
* Returns the character after the first '&' in the internationalized
* string corresponding to <tt>key</tt>
*
* @param key The identifier of the string in the resources properties file.
* @return the character after the first '&' in the internationalized
* string corresponding to <tt>key</tt>.
*/
public char getI18nMnemonic(String key)
{
return getI18nMnemonic(key, Locale.getDefault());
}
/**
* Returns the character after the first '&' in the internationalized
* string corresponding to <tt>key</tt>
*
* @param key The identifier of the string in the resources properties file.
* @param locale The locale that we'd like to receive the result in.
* @return the character after the first '&' in the internationalized
* string corresponding to <tt>key</tt>.
*/
public char getI18nMnemonic(String key, Locale locale)
{
String resourceString = doGetI18String(key, locale);
if (resourceString == null)
{
logger.warn("Missing resource for key: " + key);
return 0;
}
int mnemonicIndex = resourceString.indexOf('&');
if (mnemonicIndex > -1 && mnemonicIndex < resourceString.length() - 1)
{
return resourceString.charAt(mnemonicIndex + 1);
}
return 0;
}
/**
* Returns the string value of the corresponding configuration key.
*
* @param key The identifier of the string in the resources properties file.
* @return the string of the corresponding configuration key.
*/
public String getSettingsString(String key)
{
return (settingsResources == null) ? null : settingsResources.get(key);
}
/**
* Returns the int value of the corresponding configuration key.
*
* @param key The identifier of the string in the resources properties file.
* @return the int value of the corresponding configuration key.
*/
public int getSettingsInt(String key)
{
String resourceString = getSettingsString(key);
if (resourceString == null)
{
logger.warn("Missing resource for key: " + key);
return 0;
}
return Integer.parseInt(resourceString);
}
/**
* Returns an <tt>URL</tt> from a given identifier.
*
* @param urlKey The identifier of the url.
* @return The url for the given identifier.
*/
public URL getSettingsURL(String urlKey)
{
String path = getSettingsString(urlKey);
if (path == null || path.length() == 0)
{
logger.warn("Missing resource for key: " + urlKey);
return null;
}
return settingsPack.getClass().getClassLoader().getResource(path);
}
/**
* Returns a stream from a given identifier.
*
* @param streamKey The identifier of the stream.
* @return The stream for the given identifier.
*/
public InputStream getSettingsInputStream(String streamKey)
{
return getSettingsInputStream(streamKey, settingsPack.getClass());
}
/**
* Returns a stream from a given identifier, obtained through the class
* loader of the given resourceClass.
*
* @param streamKey The identifier of the stream.
* @param resourceClass the resource class through which the resource would
* be obtained
* @return The stream for the given identifier.
*/
public InputStream getSettingsInputStream( String streamKey,
Class<?> resourceClass)
{
String path = getSettingsString(streamKey);
if (path == null || path.length() == 0)
{
logger.warn("Missing resource for key: " + streamKey);
return null;
}
return resourceClass.getClassLoader().getResourceAsStream(path);
}
/**
* Returns the image path corresponding to the given key.
*
* @param key The identifier of the image in the resource properties file.
* @return the image path corresponding to the given key.
*/
public String getImagePath(String key)
{
return (imageResources == null) ? null : imageResources.get(key);
}
/**
* Loads an image from a given image identifier.
*
* @param imageID The identifier of the image.
* @return The image for the given identifier.
*/
public byte[] getImageInBytes(String imageID)
{
InputStream in = getImageInputStream(imageID);
if(in == null)
return null;
byte[] image = null;
try
{
image = new byte[in.available()];
in.read(image);
}
catch (IOException e)
{
logger.error("Failed to load image:" + imageID, e);
}
return image;
}
/**
* Loads an image from a given image identifier.
*
* @param imageID The identifier of the image.
* @return The image for the given identifier.
*/
public ImageIcon getImage(String imageID)
{
URL imageURL = getImageURL(imageID);
return (imageURL == null) ? null : new ImageIcon(imageURL);
}
/**
* Returns the path of the sound corresponding to the given
* property key.
*
* @param soundKey the key, for the sound path
* @return the path of the sound corresponding to the given
* property key.
*/
public String getSoundPath(String soundKey)
{
return soundResources.get(soundKey);
}
/**
* Resources for currently loaded <tt>ColorPack</tt>.
*
* @return the currently color resources
*/
protected Map<String, String> getColorResources()
{
return colorResources;
}
/**
* Currently loaded <tt>SkinPack</tt>.
*
* @return the currently loaded skin pack
*/
protected SkinPack getSkinPack()
{
return skinPack;
}
/**
* Currently loaded image pack.
*
* @return the currently loaded image pack
*/
protected ImagePack getImagePack()
{
return imagePack;
}
/**
* Currently loaded sound pack.
*
* @return the currently loaded sound pack
*/
protected ResourcePack getSoundPack()
{
return soundPack;
}
}