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    }