001// Copyright 2006, 2007, 2009, 2011 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.ioc.internal.util.CollectionFactory;
018import org.apache.tapestry5.ioc.util.AvailableValues;
019import org.apache.tapestry5.ioc.util.UnknownValueException;
020import org.apache.tapestry5.services.ClasspathAssetAliasManager;
021import org.apache.tapestry5.services.assets.AssetPathConstructor;
022
023import java.util.Collections;
024import java.util.Comparator;
025import java.util.List;
026import java.util.Map;
027
028public class ClasspathAssetAliasManagerImpl implements ClasspathAssetAliasManager
029{
030    private final AssetPathConstructor assetPathConstructor;
031
032    /**
033     * Map from alias to path.
034     */
035    private final Map<String, String> aliasToPathPrefix = CollectionFactory.newMap();
036
037    /**
038     * Map from path to alias.
039     */
040    private final Map<String, String> pathPrefixToAlias = CollectionFactory.newMap();
041
042    private final List<String> sortedAliases;
043
044    private final List<String> sortedPathPrefixes;
045
046    /**
047     * Configuration is a map of aliases (short names) to complete names. Keys and values should end with a slash, but
048     * one will be provided as necessary, so don't both.
049     */
050    public ClasspathAssetAliasManagerImpl(AssetPathConstructor assetPathConstructor,
051
052                                          Map<String, String> configuration)
053    {
054        this.assetPathConstructor = assetPathConstructor;
055
056        for (Map.Entry<String, String> e : configuration.entrySet())
057        {
058            String alias = verify("folder name", e.getKey());
059
060
061            String path = verify("path", e.getValue());
062
063            aliasToPathPrefix.put(alias, path);
064            pathPrefixToAlias.put(path, alias);
065        }
066
067        Comparator<String> sortDescendingByLength = new Comparator<String>()
068        {
069            public int compare(String o1, String o2)
070            {
071                return o2.length() - o1.length();
072            }
073        };
074
075        sortedAliases = CollectionFactory.newList(aliasToPathPrefix.keySet());
076        Collections.sort(sortedAliases, sortDescendingByLength);
077
078        sortedPathPrefixes = CollectionFactory.newList(aliasToPathPrefix.values());
079        Collections.sort(sortedPathPrefixes, sortDescendingByLength);
080    }
081
082    private String verify(String name, String input)
083    {
084
085        if (input.startsWith("/") || input.endsWith("/"))
086            throw new RuntimeException(String.format("Contribution of %s '%s' is invalid as it may not start with or end with a slash.",
087                    name, input));
088
089        return input;
090
091    }
092
093    public String toClientURL(String resourcePath)
094    {
095        for (String pathPrefix : sortedPathPrefixes)
096        {
097            if (resourcePath.startsWith(pathPrefix))
098            {
099                String virtualFolder = pathPrefixToAlias.get(pathPrefix);
100
101                String virtualPath = resourcePath.substring(pathPrefix.length() + 1);
102
103                return assetPathConstructor.constructAssetPath(virtualFolder, virtualPath);
104            }
105        }
106
107        // This is a minor misuse of the UnknownValueException but the exception reporting
108        // is too useful to pass up.
109
110        throw new UnknownValueException(
111                String.format(
112                        "Unable to create a client URL for classpath resource %s: The resource path was not within an aliased path.",
113                        resourcePath), new AvailableValues("Aliased paths", aliasToPathPrefix.values()));
114    }
115
116    public Map<String, String> getMappings()
117    {
118        return Collections.unmodifiableMap(aliasToPathPrefix);
119    }
120
121}