/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.j2me.global;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.global.ResourceException;
import javax.microedition.global.UnsupportedLocaleException;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
/**
* This class represents a resource manager factory for creating application
* resource managers.
*
*/
public class AppResourceManagerFactory extends ResourceManagerFactory {
/**
* Class name.
*/
private static final String classname =
AppResourceManagerFactory.class.getName();
/**
* Implementation of resource cache.
*/
protected ResourceCache resourceCache;
/**
* Creates a new resource manager factory for creating application resource
* managers.
*
* @param cache implementation of {@link ResourceCache} to cache
* resources.
*/
public AppResourceManagerFactory(ResourceCache cache) {
resourceCache = cache;
}
/**
* Get manager for system locale. It's never used. {@link
* AppResourceManagerFactory#getManager(String)} replaces this call.
*
* @param baseName the base name
* @return <code>null</code>
* @throws javax.microedition.global.ResourceException if
* manager cannot be created
*/
public ResourceManager getManager(String baseName) {
return null;
}
/**
* Creates initialized instance of ResourceBundleReader for given name.
* It opens resource file and reads header.
*
* @param name name of resource file
* @return reader for this resource file
*/
protected ResourceBundleReader getResourceBundleReaderForName(String name) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
classname + ": getResourceBundleReaderForName (" +
name + ")");
}
return AppResourceBundleReader.getInstance(name);
}
/**
* Creates a new instance of <code>AppResourceManager</code>. The new
* instance can be used to get application resources of the given base name
* and locale. The resource files will be accessed through the given
* resource bundle reader.
*
* @param baseName the base name
* @param locales array of locales to try
* @param readers array of readers corresponding to locales
* @return new <code>AppResourceManager</code> object
*/
protected ResourceManager newResourceManagerInstance(
String baseName,
String[] locales,
ResourceBundleReader[] readers) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
classname + ": " +
"Creating new AppResourceManager for \"" +
baseName + "\" with cache: " + this.resourceCache +
"\nwith readers:");
for (int i = 0; i < readers.length; i++) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
readers[i].toString());
}
}
return new AppResourceManager(baseName,
locales,
readers,
this.resourceCache);
}
/**
* Instantiates <code>ResourceManager</code> for a given locale, going
* through all parent locales and creating respective
* {@link ResourceBundleReader}s for them.
*
* @param baseName the base name
* @param locale the locale
* @param mf the metafile
* @param rbr the bundle reader
* @return created {@link AppResourceManager} object
*/
private ResourceManager getManager(String baseName, String locale,
MetaFile mf, ResourceBundleReader rbr) {
// locales
Vector l = new Vector(5);
// readers
Vector r = new Vector(5);
l.addElement(locale);
r.addElement(rbr);
locale = LocaleHelpers.getParentLocale(locale);
/*
* Go through all parent locales,
* e.g. "en-US-var", "en-US", "en", "", null
*/
while (locale != null) {
if (mf.containsLocale(locale)) {
try {
ResourceBundleReader reader =
getResourceBundleReaderForName(
getResourceUrl(baseName, locale));
l.addElement(locale);
r.addElement(reader);
} catch (ResourceException re_ignore) {
/* intentionally ignored */
/* For parent locales it does not throw exception in case any */
/* problems. The exception is thrown for the current locale */
/* only in the public getManager methods. */
}
}
locale = LocaleHelpers.getParentLocale(locale);
}
String[] locales = new String[l.size()];
ResourceBundleReader[] readers = new ResourceBundleReader[r.size()];
l.copyInto(locales);
r.copyInto(readers);
// instantiate ResourceManager with supported locales
return newResourceManagerInstance(baseName, locales, readers);
}
/**
* Creates <code>ResourceManager</code> for given base name and locale.
* Locale inheritance is resolved here. All parent locales are found and
* <code>ResourceManager</code> is instantiated for the first locale that
* is supported and contains any resources. Metafile is read for base name
* and parent locales are matched with locales from the metafile.
*
* @param baseName the base name
* @param locale the locale code
* @return created {@link AppResourceManager} object
* @throws ResourceException if resources are not found for any of
* the matched locales or resource file has invalid format.
* @throws UnsupportedLocaleException if given base name
* supports neither requested locale nor any of its parent locales.
*/
public ResourceManager getManager(String baseName, String locale)
throws ResourceException, UnsupportedLocaleException {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
classname + ": " + "getManager (" +
baseName + "," + locale + ")");
}
Vector l = new Vector(4);
while (locale != null) {
l.addElement(locale);
locale = LocaleHelpers.getParentLocale(locale);
}
String[] locales = new String[l.size()];
l.copyInto(locales);
return getManager(baseName, locales);
}
/**
* Create ResourceManager for given base name and list of locales. No
* locale inheritance used here. Metafile is read for base name and locales
* in array are matched with locales from metafile. Only those that match
* are used.
*
* @param baseName the base name
* @param locales array of locales
* @return created {@link AppResourceManager} object
* @throws ResourceException if no resources for the base name and any of
* the locales were found.
* @throws UnsupportedLocaleException if no locales from the array are
* listed in metafile.
*/
public ResourceManager getManager(String baseName, String[] locales)
throws ResourceException, UnsupportedLocaleException {
// read metafile
MetaFile mf = getMetafileForBaseName(baseName);
// match locales with those read from metafile
for (int i = 0; i < locales.length; i++) {
if (mf.containsLocale(locales[i])) {
ResourceBundleReader reader = getResourceBundleReaderForName(
getResourceUrl(baseName, locales[i]));
return getManager(baseName, locales[i], mf, reader);
}
}
throw new UnsupportedLocaleException();
}
/**
* Get array of supported locales for given base name. It reads metafile
* for the base name.
*
* @param baseName the base name
* @return array of locales
*/
public String[] getSupportedLocales(String baseName) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
classname + ": getSupportedLocales");
}
if (baseName == null)
throw new NullPointerException("Basename is null.");
MetaFile mf = getMetafileForBaseName(baseName);
String[] locales = mf.toArray();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
for (int i = 0; i < locales.length; i++) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
locales[i]);
}
}
return locales;
}
/**
* Gives resource URL that can be used for method {@link
* Class#getResourceAsStream}.
*
* @param baseName the base name
* @param locale the locale
* @return resource URL
*/
protected String getResourceUrl(String baseName, String locale) {
String resourceUrl;
resourceUrl = (locale != null && locale.length() > 0)
? GLOBAL_PREFIX + locale + "/" + baseName
: GLOBAL_PREFIX + baseName;
String url = resourceUrl + RESOURCE_FILE_EXTENSION;
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR238,
classname + ": url=" + url);
}
return url;
}
/**
* Read metafile {@link MetaFile} for givane base name. Metafiles are
* stored under global directory in form _<base name>.<br>
*
*
* @param baseName the base name
* @return metafile for the base name
*/
protected MetaFile getMetafileForBaseName(String baseName) {
MetaFile mf;
int hcode = resourceCache.getHashCodeForResource("_" + baseName, "", 0);
Resource r = resourceCache.lookup(hcode);
if (r == null) {
try {
mf = new MetaFile(GLOBAL_PREFIX + "_" + baseName);
} catch (IOException ioe) {
throw new ResourceException(
ResourceException.METAFILE_NOT_FOUND,
"Not found metafile for base name \""
+ baseName + "\"");
}
resourceCache.addResource(new Resource(hcode, mf.getSize(), mf));
} else {
mf = (MetaFile) r.getValue();
}
return (MetaFile)mf.clone();
}
/**
* Class MetaFile represents metafile for base name. Meta file is text file
* which contains list of locales for base name. Locales are separated by
* space (U+0020). Locales can be quoted. <br>
* "" - empty string in locale list means that common resource bundle is
* available for this base name.
*/
public class MetaFile {
/**
* Approximate size used for storage of the object in cache.
*/
int size = 0;
/**
* List of locales.
*/
Vector locales = new Vector(5);
/**
* Create new metafile.
*
*/
private MetaFile() {
}
/**
* Create new metafile.
*
* @param filename path to metafile
* @exception IOException Description of the Exception
* @throws IOException if filename can't be read or error occurs
* during read.
*/
public MetaFile(String filename) throws IOException {
InputStream is = getClass().getResourceAsStream(filename);
if (is == null) {
throw new IOException();
}
read(is);
}
/**
* Create new metafile.
*
* @param in stream to read metafile from
* @exception IOException Description of the Exception
* @throws IOException if filename can't be read or error occurs
* during read.
*/
public MetaFile(InputStream in) throws IOException {
read(in);
}
/**
* Creates a copy of the instance.
* @return the instance copy.
*/
public Object clone() {
MetaFile mf = new MetaFile();
for (Enumeration e = locales.elements(); e.hasMoreElements(); ) {
String locale = new String((String)e.nextElement());
mf.locales.addElement(locale);
}
return mf;
}
/**
* Check if given locale appears in metafile.
*
* @param locale the locale
* @return <code>true</code> if locale appeared in metafile
*/
public boolean containsLocale(String locale) {
String l;
for (Enumeration e = locales.elements(); e.hasMoreElements(); ) {
l = (String) e.nextElement();
if (l.equals(locale)) {
return true;
}
}
return false;
}
/**
* Convert locales to string array.
*
* @return array of locales
*/
public String[] toArray() {
String[] larr = new String[locales.size()];
locales.copyInto(larr);
return larr;
}
/**
* Initialize metafile. Parse input stream.
*
* @param in Description of the Parameter
* @exception IOException Description of the Exception
*/
private void read(InputStream in) throws IOException {
int BUFFER_LENGTH = 16;
int readChars = 0;
char[] buffer = new char[BUFFER_LENGTH];
InputStreamReader iReader = new InputStreamReader(in);
StringBuffer sb = new StringBuffer();
do {
readChars = iReader.read(buffer);
if (readChars > 0) {
sb.append(buffer, 0, readChars);
}
} while (readChars >= 0);
if (sb.length() == 0) {
return;
}
if (sb.charAt(sb.length() - 1) != ' ') {
sb.append(' ');
}
boolean inside = false;
StringBuffer sbuf = new StringBuffer();
char c = ' ';
char last = ' ';
char prev = ' ';
int sb_length = sb.length();
for (int i = 0; i < sb_length; i++) {
c = sb.charAt(i);
if (c == ' ' || c == '"') {
if (inside) {
if (last == c) {
String locale;
if (sbuf.length() == 0) {
locale = "";
} else {
locale = sbuf.toString().trim();
if (!LocaleHelpers.isValidLocale(locale)) {
throw new ResourceException(
ResourceException.DATA_ERROR,
"Invalid locale in metafile: \"" +
locale + "\"");
}
}
locale = LocaleHelpers.normalizeLocale(locale);
if (!locales.contains(locale)) {
if (locale.length() == 0) {
locales.insertElementAt(locale, 0);
} else {
locales.addElement(locale);
}
size += sbuf.length();
}
sbuf.setLength(0);
inside = false;
} else {
sbuf.append(c);
}
} else {
if (c == prev) {
throw new ResourceException(
ResourceException.DATA_ERROR,
"Invalid metafile format, double '" +
c + "' detected");
}
last = c;
if (c == '"') {
inside = true;
}
}
} else {
inside = true;
sbuf.append(c);
}
prev = c;
}
if (inside) {
throw new ResourceException(ResourceException.DATA_ERROR,
"Unclosed quotation");
}
}
/**
* Get approximate size of metafile instance It counts only string
* lengths. Necessary for use with {@link ResourceCache}
*
* @return approximate size of MetaFile instance
*/
public int getSize() {
return size;
}
}
// MetaFile
}
|