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 }