001    // Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.internal.services;
016    
017    import org.apache.tapestry5.Asset;
018    import org.apache.tapestry5.internal.AssetConstants;
019    import org.apache.tapestry5.internal.TapestryInternalUtils;
020    import org.apache.tapestry5.ioc.Resource;
021    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
022    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
023    import org.apache.tapestry5.ioc.services.SymbolSource;
024    import org.apache.tapestry5.ioc.services.ThreadLocale;
025    import org.apache.tapestry5.ioc.util.StrategyRegistry;
026    import org.apache.tapestry5.services.AssetFactory;
027    import org.apache.tapestry5.services.AssetSource;
028    
029    import java.lang.ref.SoftReference;
030    import java.util.Locale;
031    import java.util.Map;
032    
033    @SuppressWarnings("all")
034    public class AssetSourceImpl implements AssetSource
035    {
036        private final StrategyRegistry<AssetFactory> registry;
037    
038        private final ThreadLocale threadLocale;
039    
040        private final Map<String, Resource> prefixToRootResource = CollectionFactory.newMap();
041    
042        private final Map<Resource, SoftReference<Asset>> cache = CollectionFactory.newWeakHashMap();
043    
044        private final SymbolSource symbolSource;
045    
046        public AssetSourceImpl(ThreadLocale threadLocale,
047    
048                               Map<String, AssetFactory> configuration, SymbolSource symbolSource)
049        {
050            this.threadLocale = threadLocale;
051            this.symbolSource = symbolSource;
052    
053            Map<Class, AssetFactory> byResourceClass = CollectionFactory.newMap();
054    
055            for (Map.Entry<String, AssetFactory> e : configuration.entrySet())
056            {
057                String prefix = e.getKey();
058                AssetFactory factory = e.getValue();
059    
060                Resource rootResource = factory.getRootResource();
061    
062                byResourceClass.put(rootResource.getClass(), factory);
063    
064                prefixToRootResource.put(prefix, rootResource);
065            }
066    
067            registry = StrategyRegistry.newInstance(AssetFactory.class, byResourceClass);
068        }
069    
070        public Asset getClasspathAsset(String path)
071        {
072            return getClasspathAsset(path, null);
073        }
074    
075        public Asset getClasspathAsset(String path, Locale locale)
076        {
077            return getAsset(null, path, locale);
078        }
079    
080        public Asset getContextAsset(String path, Locale locale)
081        {
082            return getAsset(prefixToRootResource.get(AssetConstants.CONTEXT), path, locale);
083        }
084    
085        public Asset getAsset(Resource baseResource, String path, Locale locale)
086        {
087            return getAssetInLocale(baseResource, path, defaulted(locale));
088        }
089    
090        public Resource resourceForPath(String path)
091        {
092            return getUnlocalizedResource(null, path);
093        }
094    
095        public Asset getExpandedAsset(String path)
096        {
097            return getUnlocalizedAsset(symbolSource.expandSymbols(path));
098        }
099    
100        public Asset getUnlocalizedAsset(String path)
101        {
102            return getAssetInLocale(null, path, null);
103        }
104    
105        private Asset getAssetInLocale(Resource baseResource, String path, Locale locale)
106        {
107            return getLocalizedAssetFromResource(getUnlocalizedResource(baseResource, path), locale);
108        }
109    
110        private Resource getUnlocalizedResource(Resource baseResource, String path)
111        {
112            assert InternalUtils.isNonBlank(path);
113            int colonx = path.indexOf(':');
114    
115            if (colonx < 0)
116            {
117                Resource root = baseResource != null ? baseResource : prefixToRootResource.get(AssetConstants.CLASSPATH);
118    
119                return root.forFile(path);
120            }
121    
122            String prefix = path.substring(0, colonx);
123    
124            Resource root = prefixToRootResource.get(prefix);
125    
126            if (root == null)
127                throw new IllegalArgumentException(ServicesMessages.unknownAssetPrefix(path));
128    
129            return root.forFile(path.substring(colonx + 1));
130        }
131    
132        private Asset getLocalizedAssetFromResource(Resource unlocalized, Locale locale)
133        {
134            Resource localized = locale == null ? unlocalized : unlocalized.forLocale(locale);
135    
136            if (localized == null || !localized.exists())
137                throw new RuntimeException(ServicesMessages.assetDoesNotExist(unlocalized));
138    
139            return getAssetForResource(localized);
140        }
141    
142        private synchronized Asset getAssetForResource(Resource resource)
143        {
144            Asset result = TapestryInternalUtils.getAndDeref(cache, resource);
145    
146            if (result == null)
147            {
148                result = createAssetFromResource(resource);
149                cache.put(resource, new SoftReference(result));
150            }
151    
152            return result;
153        }
154    
155        private Locale defaulted(Locale locale)
156        {
157            return locale != null ? locale : threadLocale.getLocale();
158        }
159    
160        private Asset createAssetFromResource(Resource resource)
161        {
162            // The class of the resource is derived from the class of the base resource.
163            // So we can then use the class of the resource as a key to locate the correct asset
164            // factory.
165    
166            Class resourceClass = resource.getClass();
167    
168            AssetFactory factory = registry.get(resourceClass);
169    
170            return factory.createAsset(resource);
171        }
172    }