001    // Copyright 2006, 2007, 2008, 2009 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.ioc.Resource;
019    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
020    import org.apache.tapestry5.ioc.internal.util.Defense;
021    import org.apache.tapestry5.ioc.services.ThreadLocale;
022    import org.apache.tapestry5.ioc.util.StrategyRegistry;
023    import org.apache.tapestry5.services.AssetFactory;
024    import org.apache.tapestry5.services.AssetSource;
025    
026    import java.util.Locale;
027    import java.util.Map;
028    
029    public class AssetSourceImpl implements AssetSource
030    {
031        private static final String CLASSPATH = "classpath";
032    
033        private static final String CONTEXT = "context";
034    
035        private final StrategyRegistry<AssetFactory> registry;
036    
037        private final ThreadLocale threadLocale;
038    
039        private final Map<String, Resource> prefixToRootResource = CollectionFactory.newMap();
040    
041        private final Map<Resource, Asset> cache = CollectionFactory.newConcurrentMap();
042    
043        public AssetSourceImpl(ThreadLocale threadLocale,
044    
045                               Map<String, AssetFactory> configuration)
046        {
047            this.threadLocale = threadLocale;
048    
049            Map<Class, AssetFactory> byResourceClass = CollectionFactory.newMap();
050    
051            for (Map.Entry<String, AssetFactory> e : configuration.entrySet())
052            {
053                String prefix = e.getKey();
054                AssetFactory factory = e.getValue();
055    
056                Resource rootResource = factory.getRootResource();
057    
058                byResourceClass.put(rootResource.getClass(), factory);
059    
060                prefixToRootResource.put(prefix, rootResource);
061            }
062    
063            registry = StrategyRegistry.newInstance(AssetFactory.class, byResourceClass);
064        }
065    
066        public Asset getClasspathAsset(String path)
067        {
068            return getClasspathAsset(path, null);
069        }
070    
071        public Asset getClasspathAsset(String path, Locale locale)
072        {
073            return getAsset(null, path, locale);
074        }
075    
076        public Asset getContextAsset(String path, Locale locale)
077        {
078            return getAsset(prefixToRootResource.get(CONTEXT), path, locale);
079        }
080    
081        public Asset getAsset(Resource baseResource, String path, Locale locale)
082        {
083            return getAssetInLocale(baseResource, path, defaulted(locale));
084        }
085    
086        public Resource resourceForPath(String path)
087        {
088            return getUnlocalizedResource(null, path);
089        }
090    
091        private Asset getAssetInLocale(Resource baseResource, String path, Locale locale)
092        {
093            return getLocalizedAssetFromResource(getUnlocalizedResource(baseResource, path), locale);
094        }
095    
096        private Resource getUnlocalizedResource(Resource baseResource, String path)
097        {
098            Defense.notBlank(path, "path");
099    
100            int colonx = path.indexOf(':');
101    
102            if (colonx < 0)
103            {
104                Resource root = baseResource != null ? baseResource : prefixToRootResource.get(CLASSPATH);
105    
106                return root.forFile(path);
107            }
108    
109            String prefix = path.substring(0, colonx);
110    
111            Resource root = prefixToRootResource.get(prefix);
112    
113            if (root == null)
114                throw new IllegalArgumentException(ServicesMessages.unknownAssetPrefix(path));
115    
116    
117            return root.forFile(path.substring(colonx + 1));
118        }
119    
120    
121        private Asset getLocalizedAssetFromResource(Resource unlocalized, Locale locale)
122        {
123            Resource localized = locale == null
124                                 ? unlocalized
125                                 : unlocalized.forLocale(locale);
126    
127            if (localized == null)
128                throw new RuntimeException(ServicesMessages.assetDoesNotExist(unlocalized));
129    
130            return getAssetForResource(localized);
131        }
132    
133        private Asset getAssetForResource(Resource resource)
134        {
135            Asset result = cache.get(resource);
136    
137            if (result == null)
138            {
139                result = createAssetFromResource(resource);
140                cache.put(resource, result);
141            }
142    
143            return result;
144        }
145    
146        private Locale defaulted(Locale locale)
147        {
148            return locale != null ? locale : threadLocale.getLocale();
149        }
150    
151        private Asset createAssetFromResource(Resource resource)
152        {
153            // The class of the resource is derived from the class of the base resource.
154            // So we can then use the class of the resource as a key to locate the correct asset
155            // factory.
156    
157            Class resourceClass = resource.getClass();
158    
159            AssetFactory factory = registry.get(resourceClass);
160    
161            return factory.createAsset(resource);
162        }
163    }