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
015package org.apache.tapestry5.internal.services;
016
017import org.apache.tapestry5.Asset;
018import org.apache.tapestry5.internal.AssetConstants;
019import org.apache.tapestry5.internal.TapestryInternalUtils;
020import org.apache.tapestry5.ioc.Resource;
021import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
022import org.apache.tapestry5.ioc.internal.util.InternalUtils;
023import org.apache.tapestry5.ioc.services.SymbolSource;
024import org.apache.tapestry5.ioc.services.ThreadLocale;
025import org.apache.tapestry5.ioc.util.StrategyRegistry;
026import org.apache.tapestry5.services.AssetFactory;
027import org.apache.tapestry5.services.AssetSource;
028
029import java.lang.ref.SoftReference;
030import java.util.Locale;
031import java.util.Map;
032
033@SuppressWarnings("all")
034public 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}