    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    }