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