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    
015    package org.apache.tapestry5.internal.services;
016    
017    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
018    import org.apache.tapestry5.ioc.util.AvailableValues;
019    import org.apache.tapestry5.ioc.util.UnknownValueException;
020    import org.apache.tapestry5.services.ClasspathAssetAliasManager;
021    import org.apache.tapestry5.services.assets.AssetPathConstructor;
022    
023    import java.util.Collections;
024    import java.util.Comparator;
025    import java.util.List;
026    import java.util.Map;
027    
028    public 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    }