001 package org.apache.tapestry.resolver; 002 003 import org.apache.hivemind.Resource; 004 import org.apache.hivemind.util.ContextResource; 005 import org.apache.tapestry.IComponent; 006 import org.apache.tapestry.IRequestCycle; 007 import org.apache.tapestry.TapestryUtils; 008 import org.apache.tapestry.asset.AssetFactory; 009 import org.apache.tapestry.web.WebContextResource; 010 011 import java.util.Locale; 012 013 /** 014 * Implementation of {@link IComponentResourceResolver}. 015 */ 016 public class ComponentResourceResolverImpl implements IComponentResourceResolver { 017 018 /** 019 * Used to find resources under the context WEB-INF/ directory. 020 */ 021 private static final String WEB_INF = "WEB-INF/"; 022 023 private AssetFactory _classpathAssetFactory; 024 private AssetFactory _contextAssetFactory; 025 026 /** Application id of the current tapestry app - as in applicationId.application */ 027 private String _applicationId; 028 029 /** Root web context directory */ 030 private Resource _contextRoot; 031 /** The location of the WEB-INF context directory */ 032 private Resource _webInfLocation; 033 /** The location of the application within the WEB-INF/ folder */ 034 private Resource _webInfAppLocation; 035 036 /** 037 * Called by hivemind automatically. 038 */ 039 public void initializeService() 040 { 041 _webInfLocation = _contextRoot.getRelativeResource(WEB_INF); 042 043 _webInfAppLocation = _webInfLocation.getRelativeResource(_applicationId + "/"); 044 } 045 046 public Resource findComponentResource(IComponent component, IRequestCycle cycle, String path, String extension, Locale locale) 047 { 048 Resource base = component.getSpecification().getSpecificationLocation(); 049 String baseName = path == null ? extractBaseName(base) : path; 050 Resource resource = null; 051 052 // have to do explicit check for context resource first 053 // as it might be a classpath based spec and then we need to manually figure out 054 // the best location to start from as context paths always get resolved first 055 // before classpath resources 056 057 if (WebContextResource.class.isInstance(base) || ContextResource.class.isInstance(base)) 058 { 059 resource = base.getRelativeResource(baseName + extension); 060 061 if (resource != null) 062 return localizeResource(resource, locale); 063 } 064 065 resource = findComponentClassResource(component, cycle, baseName, extension, locale); 066 067 // In some cases the generic classpath resource path is fine - such as bundled component properties 068 069 if (resource == null) 070 { 071 resource = base.getRelativeResource(baseName + extension); 072 073 if (resource != null) 074 resource = localizeResource(resource, locale); 075 } 076 077 return resource; 078 } 079 080 String extractBaseName(Resource baseResourceLocation) 081 { 082 String fileName = baseResourceLocation.getName(); 083 int dotx = fileName.lastIndexOf('.'); 084 085 return dotx > -1 ? fileName.substring(0, dotx) : fileName; 086 } 087 088 Resource localizeResource(Resource resource, Locale locale) 089 { 090 if (locale == null) 091 return resource; 092 093 Resource localized = resource.getLocalization(locale); 094 if (localized != null && localized.getResourceURL() != null) 095 return localized; 096 097 return resource; 098 } 099 100 Resource findComponentClassResource(IComponent component, IRequestCycle cycle, String baseName, String extension, Locale locale) 101 { 102 Resource base = component.getSpecification().getSpecificationLocation(); 103 String componentPackages = component.getNamespace().getPropertyValue("org.apache.tapestry.component-class-packages"); 104 105 // this relies on finding things from the component class name 106 107 if (componentPackages == null) 108 return null; 109 110 String className = component.getSpecification().getComponentClassName(); 111 if (className == null) 112 return null; 113 114 String[] packages = TapestryUtils.split(componentPackages); 115 for (int i=0; i < packages.length; i++) 116 { 117 // find matching package in class 118 int index = className.lastIndexOf(packages[i]); 119 if (index < 0) 120 continue; 121 122 // First try context 123 124 String templateName = className.substring((index + packages[i].length()) + 1, className.length()).replaceAll("\\.", "/"); 125 templateName = templateName + extension; 126 127 if (_contextAssetFactory.assetExists(component.getSpecification(), _webInfAppLocation, templateName, locale)) 128 { 129 return _contextAssetFactory.createAsset(_webInfAppLocation, component.getSpecification(), templateName, locale, component.getLocation()).getResourceLocation(); 130 } else if (_contextAssetFactory.assetExists(component.getSpecification(), _webInfLocation, templateName, locale)) 131 { 132 return _contextAssetFactory.createAsset(_webInfLocation, component.getSpecification(), templateName, locale, component.getLocation()).getResourceLocation(); 133 } 134 135 // else classpath 136 137 String resourceName = baseName + extension; 138 139 if (_classpathAssetFactory.assetExists(component.getSpecification(), base, resourceName, locale)) 140 { 141 return _classpathAssetFactory.createAsset(base, component.getSpecification(), resourceName, locale, component.getLocation()).getResourceLocation(); 142 } 143 144 // if all else fails try package name context paths 145 146 String[] packageSegments = packages[i].split("\\."); 147 148 if (packageSegments != null && packageSegments.length > 0) 149 { 150 // start with last segment and slowly build the path up with all of them 151 String packagePath = ""; 152 153 for (int s=packageSegments.length - 1; s > -1; s--) 154 { 155 packagePath += packageSegments[s] + "/"; 156 157 String templatePath = packagePath + templateName; 158 159 if (_contextAssetFactory.assetExists(component.getSpecification(), _webInfAppLocation, templatePath, locale)) 160 { 161 return _contextAssetFactory.createAsset(_webInfAppLocation, component.getSpecification(), templatePath, locale, component.getLocation()).getResourceLocation(); 162 } else if (_contextAssetFactory.assetExists(component.getSpecification(), _webInfLocation, templatePath, locale)) 163 { 164 return _contextAssetFactory.createAsset(_webInfLocation, component.getSpecification(), templatePath, locale, component.getLocation()).getResourceLocation(); 165 } 166 } 167 } 168 } 169 170 return null; 171 } 172 173 public void setContextRoot(Resource contextRoot) 174 { 175 _contextRoot = contextRoot; 176 } 177 178 public void setClasspathAssetFactory(AssetFactory classpathAssetFactory) 179 { 180 _classpathAssetFactory = classpathAssetFactory; 181 } 182 183 public void setContextAssetFactory(AssetFactory contextAssetFactory) 184 { 185 _contextAssetFactory = contextAssetFactory; 186 } 187 188 public void setApplicationId(String applicationId) 189 { 190 _applicationId = applicationId; 191 } 192 }