diff options
Diffstat (limited to 'projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOResourceManager.java')
| -rw-r--r-- | projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOResourceManager.java | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOResourceManager.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOResourceManager.java new file mode 100644 index 0000000..c69c9db --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/WOResourceManager.java @@ -0,0 +1,489 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Blacksmith, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSData; +import net.wotonomy.foundation.NSKeyValueCoding; +import net.wotonomy.foundation.NSMutableDictionary; +import net.wotonomy.foundation.internal.PropertyListParser; + +/** +* Manages all resources vended by the application. +* +* @author michael@mpowers.net +* @author $Author: cgruber $ +* @version $Revision: 905 $ +*/ +public class WOResourceManager +{ + private NSMutableDictionary resourceCache; + private NSMutableDictionary dynamicDataCache; + private NSMutableDictionary stringTableCache; + private Map localeCache; // used for wo-style i18n + private Locale californiaLocale; // used for wo-style i18n + + /** + * Constructor is only accessible to subclasses. + */ + protected WOResourceManager() + { + resourceCache = new NSMutableDictionary(); + dynamicDataCache = new NSMutableDictionary(); + stringTableCache = new NSMutableDictionary(); + localeCache = new HashMap(); + californiaLocale = new Locale( "en", "US" ); + localeCache.put( "en", californiaLocale ); + } + + /** + * Returns the raw data corresponding to the specified resource. + * Any data retrieved by this method will be placed in the + * resource manager's global cache. + */ + public byte[] bytesForResourceNamed(String aFileName, + String aFrameworkName, + NSArray aLanguagesList) + { + String mash = aFileName + aFrameworkName; + if ( aLanguagesList != null ) + { + mash = mash + aLanguagesList.componentsJoinedByString(":"); + } + + byte[] result = (byte[]) resourceCache.objectForKey( mash ); + if ( result == null ) + { + InputStream input = inputStreamForResourceNamed( + aFileName, aFrameworkName, aLanguagesList ); + if ( input != null ) + { + try + { + int c; + ByteArrayOutputStream output = new ByteArrayOutputStream(); + while ( ( c = input.read() ) != -1 ) + { + output.write( c ); + } + output.flush(); + input.close(); + output.close(); + result = output.toByteArray(); + synchronized ( resourceCache ) + { + resourceCache.setObjectForKey( result, mash ); + } + } + catch ( Throwable t ) + { + System.err.println( "WOResourceManager: Error reading bytes: " + aFileName ); + t.printStackTrace(); + } + } + } + return result; + } + + /** + * Returns the content type corresponding to the specified resource. + * This implementation recognizes gif, jpg, png, html, and xml extensions. + * Otherwise, "text/plain" is returned. + */ + public String contentTypeForResourceNamed(String aResourcePath) + { + if ( aResourcePath.endsWith( ".gif" ) ) return "image/gif"; + if ( aResourcePath.endsWith( ".jpg" ) ) return "image/jpeg"; + if ( aResourcePath.endsWith( ".png" ) ) return "image/png"; + if ( aResourcePath.endsWith( ".html" ) ) return "text/html"; + if ( aResourcePath.endsWith( ".xml" ) ) return "text/xml"; + return "text/plain"; + } + + /** + * Returns a url to be used when errors occur while retrieving a resource. + */ + public String errorMessageUrlForResourceNamed(String aResourceName, + String aFrameworkName) + { + if ( aResourceName == null ) aResourceName = "null"; + if ( aFrameworkName == null ) + { + return "/ERROR/NOT_FOUND/app=" + + WOApplication.application().name() + + "/filename=" + aResourceName; + } + else + { + return "/ERROR/NOT_FOUND/framework=" + + aFrameworkName + "/filename=" + aResourceName; + } + + } + + /** + * Clears all cached system-wide resource data. + */ + public void flushDataCache() + { + synchronized ( resourceCache ) + { + resourceCache.removeAllObjects(); + } + synchronized ( dynamicDataCache ) + { + dynamicDataCache.removeAllObjects(); + } + synchronized ( stringTableCache ) + { + stringTableCache.removeAllObjects(); + } + } + + /** + * Returns the file-system path for the specified resource. + * Deprecated and not implemented. + * @deprecated Use inputStreamForResourceNamed instead. + */ + public String pathForResourceNamed(String aResourceName, + String aFrameworkName, + NSArray aLanguagesList) + { + throw new RuntimeException( "ResourceManager.pathForResourceNamed: deprecated" ); + } + + /** + * Removes the data from the dynamic data cache for the specified session. + * If aSession is null, the data is removed from the application-wide + * data cache. + */ + public void removeDataForKey(String aKey, + WOSession aSession) + { + if ( aSession != null ) + { + if ( aSession.dynamicDataCache != null ) + { + aSession.dynamicDataCache.removeObjectForKey( aKey ); + } + } + else + { + synchronized ( dynamicDataCache ) + { + dynamicDataCache.removeObjectForKey( aKey ); + } + } + } + + /** + * Sets the data in the dynamic data cache for the specified session. + * If aSession is null, the data is placed in the application-wide + * data cache. If the key is a system-generated key, the data will + * be removed by calling removeData() the next time it is requested. + */ + public void setData(NSData someData, + String key, + String type, + WOSession aSession) + { + if ( aSession != null ) + { + if ( aSession.dynamicDataCache != null ) + { + aSession.dynamicDataCache.setObjectForKey( + new TypedData( type, someData ), key ); + } + } + else + { + synchronized ( dynamicDataCache ) + { + dynamicDataCache.setObjectForKey( + new TypedData( type, someData ), key ); + } + } + } + + /** + * Returns a localized string from a property list for + * a given key. If the key doesn't exist, aDefaultValue + * is returned. + */ + public String stringForKey(String aKey, + String aFileName, + String aDefaultValue, + String aFrameworkName, + NSArray aLanguagesList) + { + + String mash = aFileName + aFrameworkName; + if ( aLanguagesList != null ) + { + mash = mash + aLanguagesList.componentsJoinedByString(":"); + } + + Object table = stringTableCache.objectForKey( mash ); + if ( table == null ) + { + try + { + InputStream input = (InputStream) inputStreamForResourceNamed( + aFileName, aFrameworkName, aLanguagesList); + if ( input != null ) + { + Reader reader = + new BufferedReader(new InputStreamReader(input)); + table = PropertyListParser.propertyListFromReader( reader ); + synchronized ( stringTableCache ) + { + stringTableCache.setObjectForKey( table, mash ); + } + } + } + catch ( IOException ioe ) + { + System.err.println( "WOResourceManager: error reading: " + aFileName ); + ioe.printStackTrace(); + } + catch ( Throwable t ) + { + // could not parse + System.err.println( "WOResourceManager: could not parse: " + aFileName ); + System.err.println( t ); + } + } + + Object result = null; + if ( table != null ) + { + result = NSKeyValueCoding.DefaultImplementation.valueForKey( table, aKey ); + } + if ( result == null ) + { + result = aDefaultValue; + } + else + { + result = result.toString(); + } + + return (String) result; + } + + /** + * Returns a url that invokes the resource manager for the + * specified resource. + */ + public String urlForResourceNamed(String aResourceName, + String aFrameworkName, + NSArray aLanguagesList, + WORequest aRequest) + { + StringBuffer buffer = new StringBuffer(); + if ( aFrameworkName == null ) + { + aFrameworkName = "application"; + } + buffer.append( aRequest.applicationName() ); + buffer.append( '/' ); + buffer.append( WOApplication.resourceRequestHandlerKey() ); + if ( !aFrameworkName.startsWith("/") ) + { + buffer.append( '/' ); + } + buffer.append( aFrameworkName ); + buffer.append( '/' ); + buffer.append( aResourceName ); + return buffer.toString(); + } + + /** + * Returns an input for the raw resource. Data returned by + * this method will not be put in the resource manager's global cache. + */ + public InputStream inputStreamForResourceNamed(String aResourceName, + String aFrameworkName, + NSArray aLanguagesList) + { + if ( aResourceName == null ) return null; + InputStream result = null; + + StringBuffer path = new StringBuffer(); + path.append( '/' ); + if ( aFrameworkName != null ) + { + path.append( aFrameworkName ).append( '/' ); + } + + int i = aResourceName.lastIndexOf( "." ); + if ( i != -1 ) + path.append( aResourceName.substring( 0, i ) ); + else + path.append( aResourceName ); + + String location = path.toString(); + if ( aLanguagesList != null ) + { + String language; + Locale locale; + HashSet tried = new HashSet(5); + Enumeration e = aLanguagesList.objectEnumerator(); + while ( e.hasMoreElements() && result == null ) + { + language = e.nextElement().toString(); + + // look for java-style localization + if ( i != -1 ) + { + result = getStream( location + '_' + + language + aResourceName.substring( i ) ); + } + else // no dot extension + { + result = getStream( location + '_' + language ); + } + + // look for wo-style localization + if ( result == null ) + { + locale = (Locale) localeCache.get( language ); + if ( locale == null ) + { + if ( language.length() == 5 ) + { + locale = new Locale( + language.substring( 0, 2 ), + language.substring( 3, 5 ) ); + } + else + { + locale = new Locale( language, "" ); + } + synchronized ( localeCache ) + { + localeCache.put( language, locale ); + } + } + + language = '/'+locale.getDisplayLanguage( californiaLocale )+".lproj"; + if ( !tried.contains( language ) ) + { + if ( aFrameworkName != null ) + { + int j = aFrameworkName.length()+1; + path.insert( j, language ); + result = getStream( path.toString() + aResourceName.substring( i ) ); + path.delete( j, j+language.length() ); + } + else + { + result = getStream( language + path.toString() + aResourceName.substring( i ) ); + } + tried.add( language ); + } + } + } + } + + // look for file in package + if ( result == null ) + { + if ( i != -1 ) + { + result = getStream( path.append( + aResourceName.substring( i ) ).toString() ); + } + else // no dot extension + { + result = getStream( location ); + } + } + + return result; + } + + private static final InputStream getStream( String path ) + { //System.out.println( "getStream: " + path ); + InputStream input = + WOApplication.application().getClass().getResourceAsStream( path ); + if ( input == null ) + { + // in case the local class loader doesn't delegate to its parent + input = ClassLoader.getSystemResourceAsStream( path ); + } + return input; + } + + private static final class TypedData + { + String type; + NSData data; + + public TypedData( String aType, NSData aData ) + { + type = aType; + data = aData; + } + } +} + +/* + * $Log$ + * Revision 1.2 2006/02/19 01:44:02 cgruber + * Add xmlrpc files + * Remove jclark and replace with dom4j and javax.xml.sax stuff + * Re-work dependencies and imports so it all compiles. + * + * Revision 1.1 2006/02/16 13:22:22 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.5 2003/08/07 00:15:15 chochos + * general cleanup (mostly removing unused imports) + * + * Revision 1.4 2003/02/28 22:58:57 mpowers + * Added support for wo-style localization (*.lproj). + * + * Revision 1.3 2003/02/21 16:40:24 mpowers + * Now reading port and smtp host from system properties. + * Implemented WOApplication.main. + * + * Revision 1.2 2003/01/28 19:33:52 mpowers + * Implemented the rest of WOResourceManager. + * Implemented support for java-style i18n. + * Components now use the resource manager to load templates. + * + * Revision 1.1 2003/01/27 15:08:00 mpowers + * Implemented WOResourceManager, using java resources for now. + * + * + */ + |
