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                    return _classpathAssetFactory.createAsset(base, component.getSpecification(), resourceName, locale, component.getLocation()).getResourceLocation();
141            }
142    
143            return null;
144        }
145    
146        public void setContextRoot(Resource contextRoot)
147        {
148            _contextRoot = contextRoot;
149        }
150    
151        public void setClasspathAssetFactory(AssetFactory classpathAssetFactory)
152        {
153            _classpathAssetFactory = classpathAssetFactory;
154        }
155    
156        public void setContextAssetFactory(AssetFactory contextAssetFactory)
157        {
158            _contextAssetFactory = contextAssetFactory;
159        }
160    
161        public void setApplicationId(String applicationId)
162        {
163            _applicationId = applicationId;
164        }
165    }