Coverage Report - net.wotonomy.web.WOResourceManager
 
Classes in this File Line Coverage Branch Coverage Complexity
WOResourceManager
0% 
0% 
4.615
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Blacksmith, Inc.
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.web;
 20  
 
 21  
 import java.io.BufferedReader;
 22  
 import java.io.ByteArrayOutputStream;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.io.InputStreamReader;
 26  
 import java.io.Reader;
 27  
 import java.util.Enumeration;
 28  
 import java.util.HashMap;
 29  
 import java.util.HashSet;
 30  
 import java.util.Locale;
 31  
 import java.util.Map;
 32  
 
 33  
 import net.wotonomy.foundation.NSArray;
 34  
 import net.wotonomy.foundation.NSData;
 35  
 import net.wotonomy.foundation.NSKeyValueCoding;
 36  
 import net.wotonomy.foundation.NSMutableDictionary;
 37  
 import net.wotonomy.foundation.internal.PropertyListParser;
 38  
 
 39  
 /**
 40  
 * Manages all resources vended by the application.
 41  
 *
 42  
 * @author michael@mpowers.net
 43  
 * @author $Author: cgruber $
 44  
 * @version $Revision: 905 $
 45  
 */
 46  
 public class WOResourceManager
 47  
 {
 48  
     private NSMutableDictionary resourceCache;
 49  
     private NSMutableDictionary dynamicDataCache;
 50  
     private NSMutableDictionary stringTableCache;
 51  
     private Map localeCache; // used for wo-style i18n
 52  
     private Locale californiaLocale; // used for wo-style i18n
 53  
 
 54  
     /**
 55  
     * Constructor is only accessible to subclasses.
 56  
     */    
 57  0
     protected WOResourceManager()
 58  0
     {
 59  0
         resourceCache = new NSMutableDictionary();
 60  0
         dynamicDataCache = new NSMutableDictionary();
 61  0
         stringTableCache = new NSMutableDictionary();
 62  0
         localeCache = new HashMap();
 63  0
         californiaLocale = new Locale( "en", "US" );
 64  0
         localeCache.put( "en", californiaLocale );
 65  0
     }
 66  
     
 67  
     /**
 68  
     * Returns the raw data corresponding to the specified resource.
 69  
     * Any data retrieved by this method will be placed in the 
 70  
     * resource manager's global cache.
 71  
     */
 72  
     public byte[] bytesForResourceNamed(String aFileName,
 73  
                                         String aFrameworkName,
 74  
                                         NSArray aLanguagesList)
 75  
     {
 76  0
         String mash = aFileName + aFrameworkName;
 77  0
         if ( aLanguagesList != null )
 78  
         {
 79  0
             mash = mash + aLanguagesList.componentsJoinedByString(":");
 80  
         }
 81  
         
 82  0
         byte[] result = (byte[]) resourceCache.objectForKey( mash );
 83  0
         if ( result == null )
 84  
         {
 85  0
             InputStream input = inputStreamForResourceNamed(
 86  0
                 aFileName, aFrameworkName, aLanguagesList );
 87  0
             if ( input != null )
 88  
             {
 89  
                 try
 90  
                 {
 91  
                     int c;
 92  0
                     ByteArrayOutputStream output = new ByteArrayOutputStream();
 93  0
                     while ( ( c = input.read() ) != -1 )
 94  
                     {
 95  0
                         output.write( c );
 96  0
                     }
 97  0
                     output.flush();
 98  0
                     input.close();
 99  0
                     output.close();
 100  0
                     result = output.toByteArray();
 101  0
                     synchronized ( resourceCache )
 102  
                     {
 103  0
                         resourceCache.setObjectForKey( result, mash );
 104  0
                     }
 105  
                  }
 106  0
                  catch ( Throwable t )
 107  
                  {
 108  0
                      System.err.println( "WOResourceManager: Error reading bytes: " + aFileName );
 109  0
                      t.printStackTrace();
 110  0
                  }
 111  
              }
 112  
         }
 113  0
         return result;
 114  
     }
 115  
                                         
 116  
     /**
 117  
     * Returns the content type corresponding to the specified resource.
 118  
     * This implementation recognizes gif, jpg, png, html, and xml extensions.
 119  
     * Otherwise, "text/plain" is returned.
 120  
     */
 121  
     public String contentTypeForResourceNamed(String aResourcePath)
 122  
     {
 123  0
         if ( aResourcePath.endsWith( ".gif" ) ) return "image/gif";
 124  0
         if ( aResourcePath.endsWith( ".jpg" ) ) return "image/jpeg";
 125  0
         if ( aResourcePath.endsWith( ".png" ) ) return "image/png";
 126  0
         if ( aResourcePath.endsWith( ".html" ) ) return "text/html";
 127  0
         if ( aResourcePath.endsWith( ".xml" ) ) return "text/xml";
 128  0
         return "text/plain";
 129  
     }
 130  
     
 131  
     /**
 132  
     * Returns a url to be used when errors occur while retrieving a resource.
 133  
     */
 134  
     public String errorMessageUrlForResourceNamed(String aResourceName,
 135  
                                                   String aFrameworkName)
 136  
     {
 137  0
         if ( aResourceName == null ) aResourceName = "null";
 138  0
         if ( aFrameworkName == null ) 
 139  
         {
 140  0
             return "/ERROR/NOT_FOUND/app=" +
 141  0
                 WOApplication.application().name() +
 142  0
                 "/filename=" + aResourceName;
 143  
         }
 144  
         else
 145  
         {
 146  0
             return "/ERROR/NOT_FOUND/framework=" +
 147  0
                 aFrameworkName + "/filename=" + aResourceName;
 148  
         }
 149  
         
 150  
     }
 151  
          
 152  
     /**
 153  
     * Clears all cached system-wide resource data.
 154  
     */                                         
 155  
     public void flushDataCache()
 156  
     {
 157  0
         synchronized ( resourceCache )
 158  
         {
 159  0
             resourceCache.removeAllObjects();
 160  0
         }
 161  0
         synchronized ( dynamicDataCache )
 162  
         {
 163  0
             dynamicDataCache.removeAllObjects();
 164  0
         }
 165  0
         synchronized ( stringTableCache )
 166  
         {
 167  0
             stringTableCache.removeAllObjects();
 168  0
         }
 169  0
     }
 170  
     
 171  
     /**
 172  
     * Returns the file-system path for the specified resource.
 173  
     * Deprecated and not implemented.
 174  
     * @deprecated Use inputStreamForResourceNamed instead.
 175  
     */
 176  
     public String pathForResourceNamed(String aResourceName,
 177  
                                        String aFrameworkName,
 178  
                                        NSArray aLanguagesList)
 179  
     {
 180  0
         throw new RuntimeException( "ResourceManager.pathForResourceNamed: deprecated" );
 181  
     }
 182  
                      
 183  
     /**
 184  
     * Removes the data from the dynamic data cache for the specified session.
 185  
     * If aSession is null, the data is removed from the application-wide
 186  
     * data cache.
 187  
     */                     
 188  
     public void removeDataForKey(String aKey,
 189  
                                  WOSession aSession)
 190  
     {
 191  0
         if ( aSession != null )
 192  
         {
 193  0
             if ( aSession.dynamicDataCache != null )
 194  
             {
 195  0
                 aSession.dynamicDataCache.removeObjectForKey( aKey );
 196  0
             }
 197  
         }
 198  
         else
 199  
         {
 200  0
             synchronized ( dynamicDataCache )
 201  
             {
 202  0
                 dynamicDataCache.removeObjectForKey( aKey );
 203  0
             }
 204  
         }
 205  0
     }
 206  
     
 207  
     /**
 208  
     * Sets the data in the dynamic data cache for the specified session.
 209  
     * If aSession is null, the data is placed in the application-wide
 210  
     * data cache.  If the key is a system-generated key, the data will
 211  
     * be removed by calling removeData() the next time it is requested.
 212  
     */                     
 213  
     public void setData(NSData someData,
 214  
                         String key,
 215  
                         String type,
 216  
                         WOSession aSession)
 217  
     {
 218  0
         if ( aSession != null )
 219  
         {
 220  0
             if ( aSession.dynamicDataCache != null )
 221  
             {
 222  0
                 aSession.dynamicDataCache.setObjectForKey(
 223  0
                     new TypedData( type, someData ), key );
 224  0
             }
 225  
         }
 226  
         else
 227  
         {
 228  0
             synchronized ( dynamicDataCache )
 229  
             {
 230  0
                 dynamicDataCache.setObjectForKey(
 231  0
                     new TypedData( type, someData ), key );
 232  0
             }
 233  
         }
 234  0
     }
 235  
     
 236  
     /**
 237  
     * Returns a localized string from a property list for 
 238  
     * a given key. If the key doesn't exist, aDefaultValue 
 239  
     * is returned.
 240  
     */
 241  
     public String stringForKey(String aKey,
 242  
                                String aFileName,
 243  
                                String aDefaultValue,
 244  
                                String aFrameworkName,
 245  
                                NSArray aLanguagesList)
 246  
     {
 247  
         
 248  0
         String mash = aFileName + aFrameworkName;
 249  0
         if ( aLanguagesList != null )
 250  
         {
 251  0
             mash = mash + aLanguagesList.componentsJoinedByString(":");
 252  
         }
 253  
         
 254  0
         Object table = stringTableCache.objectForKey( mash );
 255  0
         if ( table == null )
 256  
         {
 257  
             try
 258  
             {
 259  0
                 InputStream input = (InputStream) inputStreamForResourceNamed(
 260  0
                     aFileName, aFrameworkName, aLanguagesList);
 261  0
                 if ( input != null )
 262  
                 {
 263  0
                     Reader reader = 
 264  0
                         new BufferedReader(new InputStreamReader(input));
 265  0
                     table = PropertyListParser.propertyListFromReader( reader );
 266  0
                     synchronized ( stringTableCache )
 267  
                     {
 268  0
                         stringTableCache.setObjectForKey( table, mash );
 269  0
                     }
 270  
                 }
 271  
             }
 272  0
             catch ( IOException ioe )
 273  
             {
 274  0
                 System.err.println( "WOResourceManager: error reading: " + aFileName );
 275  0
                 ioe.printStackTrace();
 276  
             }
 277  0
             catch ( Throwable t )
 278  
             {
 279  
                 // could not parse
 280  0
                 System.err.println( "WOResourceManager: could not parse: " + aFileName );
 281  0
                 System.err.println( t );
 282  0
             }
 283  
         }
 284  
         
 285  0
         Object result = null;
 286  0
         if ( table != null )
 287  
         {
 288  0
             result = NSKeyValueCoding.DefaultImplementation.valueForKey( table, aKey );
 289  
         }
 290  0
         if ( result == null )
 291  
         {
 292  0
             result = aDefaultValue;
 293  0
         }
 294  
         else
 295  
         {
 296  0
             result = result.toString();
 297  
         }
 298  
         
 299  0
         return (String) result;
 300  
     }
 301  
         
 302  
     /**
 303  
     * Returns a url that invokes the resource manager for the
 304  
     * specified resource.
 305  
     */                        
 306  
     public String urlForResourceNamed(String aResourceName,
 307  
                                       String aFrameworkName,
 308  
                                       NSArray aLanguagesList,
 309  
                                       WORequest aRequest)
 310  
     {
 311  0
         StringBuffer buffer = new StringBuffer();
 312  0
         if ( aFrameworkName == null )
 313  
         {
 314  0
             aFrameworkName = "application";
 315  
         }
 316  0
         buffer.append( aRequest.applicationName() );
 317  0
         buffer.append( '/' );
 318  0
         buffer.append( WOApplication.resourceRequestHandlerKey() );
 319  0
         if ( !aFrameworkName.startsWith("/") )
 320  
         {
 321  0
             buffer.append( '/' );
 322  
         }
 323  0
         buffer.append( aFrameworkName );
 324  0
         buffer.append( '/' );
 325  0
         buffer.append( aResourceName );
 326  0
         return buffer.toString();
 327  
     }
 328  
                                         
 329  
     /**
 330  
     * Returns an input for the raw resource.  Data returned by
 331  
     * this method will not be put in the resource manager's global cache.
 332  
     */
 333  
     public InputStream inputStreamForResourceNamed(String aResourceName,
 334  
                                                    String aFrameworkName,
 335  
                                                    NSArray aLanguagesList)
 336  
     {
 337  0
         if ( aResourceName == null ) return null;
 338  0
         InputStream result = null;
 339  
         
 340  0
         StringBuffer path = new StringBuffer();
 341  0
         path.append( '/' );
 342  0
         if ( aFrameworkName != null ) 
 343  
         {
 344  0
             path.append( aFrameworkName ).append( '/' );
 345  
         }
 346  
         
 347  0
         int i = aResourceName.lastIndexOf( "." );
 348  0
         if ( i != -1 ) 
 349  0
             path.append( aResourceName.substring( 0, i ) );
 350  
         else
 351  0
             path.append( aResourceName );
 352  
 
 353  0
         String location = path.toString();
 354  0
         if ( aLanguagesList != null )
 355  
         {
 356  
             String language;
 357  
             Locale locale;
 358  0
             HashSet tried = new HashSet(5);
 359  0
             Enumeration e = aLanguagesList.objectEnumerator();
 360  0
             while ( e.hasMoreElements() && result == null )
 361  
             {
 362  0
                 language = e.nextElement().toString();
 363  
                 
 364  
                 // look for java-style localization
 365  0
                 if ( i != -1 )
 366  
                 {
 367  0
                     result = getStream( location + '_' 
 368  0
                         + language + aResourceName.substring( i ) );
 369  0
                 }
 370  
                 else // no dot extension
 371  
                 {
 372  0
                     result = getStream( location + '_' + language );
 373  
                 }
 374  
                 
 375  
                 // look for wo-style localization
 376  0
                 if ( result == null )
 377  
                 {
 378  0
                     locale = (Locale) localeCache.get( language );
 379  0
                     if ( locale == null )
 380  
                     {
 381  0
                         if ( language.length() == 5 )
 382  
                         {
 383  0
                             locale = new Locale( 
 384  0
                                 language.substring( 0, 2 ), 
 385  0
                                 language.substring( 3, 5 ) );
 386  0
                         }
 387  
                         else
 388  
                         {
 389  0
                             locale = new Locale( language, "" );
 390  
                         }
 391  0
                         synchronized ( localeCache )
 392  
                         {
 393  0
                             localeCache.put( language, locale );
 394  0
                         }
 395  
                     }
 396  
                     
 397  0
                     language = '/'+locale.getDisplayLanguage( californiaLocale )+".lproj";
 398  0
                     if ( !tried.contains( language ) )
 399  
                     {
 400  0
                         if ( aFrameworkName != null )
 401  
                         {
 402  0
                             int j = aFrameworkName.length()+1;
 403  0
                             path.insert( j, language ); 
 404  0
                             result = getStream( path.toString() + aResourceName.substring( i ) );
 405  0
                             path.delete( j, j+language.length() );
 406  0
                         }
 407  
                         else
 408  
                         {
 409  0
                             result = getStream( language + path.toString() + aResourceName.substring( i ) );
 410  
                         }
 411  0
                         tried.add( language );
 412  0
                     }
 413  
                 }
 414  
             }
 415  
         }            
 416  
         
 417  
         // look for file in package        
 418  0
         if ( result == null )
 419  
         {
 420  0
             if ( i != -1 )
 421  
             {
 422  0
                 result = getStream( path.append( 
 423  0
                     aResourceName.substring( i ) ).toString() );
 424  0
             }
 425  
             else // no dot extension
 426  
             {
 427  0
                 result = getStream( location );
 428  
             }
 429  
         }
 430  
 
 431  0
         return result;
 432  
     }
 433  
     
 434  
     private static final InputStream getStream( String path )
 435  
     { //System.out.println( "getStream: " + path );        
 436  0
         InputStream input = 
 437  0
             WOApplication.application().getClass().getResourceAsStream( path );
 438  0
         if ( input == null )
 439  
         {
 440  
             // in case the local class loader doesn't delegate to its parent
 441  0
             input = ClassLoader.getSystemResourceAsStream( path );
 442  
         }                    
 443  0
         return input;
 444  
     }
 445  
     
 446  
     private static final class TypedData
 447  
     {
 448  
         String type;
 449  
         NSData data;
 450  
         
 451  0
         public TypedData( String aType, NSData aData )
 452  0
         {
 453  0
             type = aType;
 454  0
             data = aData;
 455  0
         }
 456  
     }
 457  
 }
 458  
 
 459  
 /*
 460  
  * $Log$
 461  
  * Revision 1.2  2006/02/19 01:44:02  cgruber
 462  
  * Add xmlrpc files
 463  
  * Remove jclark and replace with dom4j and javax.xml.sax stuff
 464  
  * Re-work dependencies and imports so it all compiles.
 465  
  *
 466  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 467  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 468  
  *
 469  
  * Revision 1.5  2003/08/07 00:15:15  chochos
 470  
  * general cleanup (mostly removing unused imports)
 471  
  *
 472  
  * Revision 1.4  2003/02/28 22:58:57  mpowers
 473  
  * Added support for wo-style localization (*.lproj).
 474  
  *
 475  
  * Revision 1.3  2003/02/21 16:40:24  mpowers
 476  
  * Now reading port and smtp host from system properties.
 477  
  * Implemented WOApplication.main.
 478  
  *
 479  
  * Revision 1.2  2003/01/28 19:33:52  mpowers
 480  
  * Implemented the rest of WOResourceManager.
 481  
  * Implemented support for java-style i18n.
 482  
  * Components now use the resource manager to load templates.
 483  
  *
 484  
  * Revision 1.1  2003/01/27 15:08:00  mpowers
 485  
  * Implemented WOResourceManager, using java resources for now.
 486  
  *
 487  
  *
 488  
  */
 489