001    // Copyright 2004, 2005 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    
015    package org.apache.tapestry.resolver;
016    
017    import org.apache.commons.logging.Log;
018    import org.apache.hivemind.ApplicationRuntimeException;
019    import org.apache.hivemind.Location;
020    import org.apache.hivemind.Resource;
021    import org.apache.hivemind.impl.LocationImpl;
022    import org.apache.tapestry.INamespace;
023    import org.apache.tapestry.IRequestCycle;
024    import org.apache.tapestry.services.ClassFinder;
025    import org.apache.tapestry.spec.ComponentSpecification;
026    import org.apache.tapestry.spec.IComponentSpecification;
027    
028    /**
029     * Utility class that understands the rules of component types (which may optionally have a library
030     * prefix) and can resolve the type to a {@link org.apache.tapestry.INamespace}and a
031     * {@link org.apache.tapestry.spec.IComponentSpecification}.
032     * <p>
033     * Like {@link org.apache.tapestry.resolver.PageSpecificationResolver}, if the component is not
034     * defined explicitly in the namespace, a search may occur: Performs the tricky work of resolving a
035     * page name to a page specification. The search for pages in the application namespace is the most
036     * complicated, since Tapestry searches for pages that aren't explicitly defined in the application
037     * specification. The search, based on the <i>simple-name </i> of the page, goes as follows:
038     * <ul>
039     * <li>As declared in the application specification
040     * <li><i>type</i>.jwc in the same folder as the application specification
041     * <li><i>type</i> jwc in the WEB-INF/ <i>servlet-name </i> directory of the context root
042     * <li><i>type</i>.jwc in WEB-INF
043     * <li><i>type</i>.jwc in the application root (within the context root)
044     * <li>By searching the framework namespace
045     * <li>By searching for a named class file within the org.apache.tapestry.component-class-packages
046     * property (defined within the namespace)
047     * </ul>
048     * The search for components in library namespaces is more abbreviated:
049     * <li>As declared in the library specification
050     * <li><i>type </i>.jwc in the same folder as the library specification
051     * <li>By searching the framework namespace
052     * </ul>
053     * 
054     * @author Howard Lewis Ship
055     * @since 3.0
056     */
057    
058    public class ComponentSpecificationResolverImpl extends AbstractSpecificationResolver implements
059            ComponentSpecificationResolver
060    {
061        /** Set by container */
062        private Log _log;
063    
064        /** Set by resolve() */
065        private String _type;
066    
067        private ClassFinder _classFinder;
068    
069        protected void reset()
070        {
071            _type = null;
072    
073            super.reset();
074        }
075    
076        /**
077         * Passed the namespace of a container (to resolve the type in) and the type to resolve,
078         * performs the processing. A "bare type" (without a library prefix) may be in the
079         * containerNamespace, or the framework namespace (a search occurs in that order).
080         * 
081         * @param cycle
082         *            current request cycle
083         * @param containerNamespace
084         *            namespace that may contain a library referenced in the type
085         * @param type
086         *            the component specification to find, either a simple name, or prefixed with a
087         *            library id (defined for the container namespace)
088         * @see #getNamespace()
089         * @see #getSpecification()
090         */
091    
092        public void resolve(IRequestCycle cycle, INamespace containerNamespace, String type,
093                Location location)
094        {
095            int colonx = type.indexOf(':');
096    
097            if (colonx > 0)
098            {
099                String libraryId = type.substring(0, colonx);
100                String simpleType = type.substring(colonx + 1);
101    
102                resolve(cycle, containerNamespace, libraryId, simpleType, location);
103            }
104            else
105                resolve(cycle, containerNamespace, null, type, location);
106    
107            IComponentSpecification spec = getSpecification();
108    
109            if (spec.isDeprecated())
110                _log.warn(ResolverMessages.componentIsDeprecated(type, location));
111        }
112    
113        /**
114         * Like
115         * {@link #resolve(org.apache.tapestry.IRequestCycle, org.apache.tapestry.INamespace, java.lang.String, org.apache.tapestry.ILocation)},
116         * but used when the type has already been parsed into a library id and a simple type.
117         * 
118         * @param cycle
119         *            current request cycle
120         * @param containerNamespace
121         *            namespace that may contain a library referenced in the type
122         * @param libraryId
123         *            the library id within the container namespace, or null
124         * @param type
125         *            the component specification to find as a simple name (without a library prefix)
126         * @param location
127         *            of reference to be resolved
128         * @throws ApplicationRuntimeException
129         *             if the type cannot be resolved
130         */
131    
132        public void resolve(IRequestCycle cycle, INamespace containerNamespace, String libraryId,
133                String type, Location location)
134        {
135            reset();
136            _type = type;
137    
138            INamespace namespace = findNamespaceForId(containerNamespace, libraryId);
139    
140            setNamespace(namespace);
141    
142            if (namespace.containsComponentType(type))
143            {
144                setSpecification(namespace.getComponentSpecification(type));
145                return;
146            }
147    
148            IComponentSpecification spec = searchForComponent(cycle);
149    
150            // If not found after search, check to see if it's in
151            // the framework instead.
152    
153            if (spec == null)
154            {
155                throw new ApplicationRuntimeException(ResolverMessages.noSuchComponentType(
156                        type,
157                        namespace), location, null);
158    
159            }
160    
161            setSpecification(spec);
162    
163            // Install it into the namespace, to short-circuit any future search.
164    
165            install();
166        }
167    
168        // Hm. This could maybe go elsewhere, say onto ISpecificationSource
169    
170        private IComponentSpecification searchForComponent(IRequestCycle cycle)
171        {
172            IComponentSpecification result = null;
173            INamespace namespace = getNamespace();
174    
175            if (_log.isDebugEnabled())
176                _log.debug(ResolverMessages.resolvingComponent(_type, namespace));
177    
178            String expectedName = _type + ".jwc";
179            Resource namespaceLocation = namespace.getSpecificationLocation();
180    
181            // Look for appropriate file in same folder as the library (or application)
182            // specificaiton.
183    
184            result = check(namespaceLocation.getRelativeResource(expectedName));
185    
186            if (result != null)
187                return result;
188    
189            if (namespace.isApplicationNamespace())
190            {
191    
192                // The application namespace gets some extra searching.
193    
194                result = check(getWebInfAppLocation().getRelativeResource(expectedName));
195    
196                if (result == null)
197                    result = check(getWebInfLocation().getRelativeResource(expectedName));
198    
199                if (result == null)
200                    result = check((getContextRoot().getRelativeResource(expectedName)));
201    
202                if (result != null)
203                    return result;
204            }
205    
206            result = searchForComponentClass(namespace, _type);
207    
208            if (result != null)
209                return result;
210    
211            // Not in the library or app spec; does it match a component
212            // provided by the Framework?
213    
214            INamespace framework = getSpecificationSource().getFrameworkNamespace();
215    
216            if (framework.containsComponentType(_type))
217                return framework.getComponentSpecification(_type);
218    
219            return getDelegate().findComponentSpecification(cycle, namespace, _type);
220        }
221    
222        IComponentSpecification searchForComponentClass(INamespace namespace, String type)
223        {
224            String packages = namespace
225                    .getPropertyValue("org.apache.tapestry.component-class-packages");
226    
227            String className = type.replace('/', '.');
228    
229            Class componentClass = _classFinder.findClass(packages, className);
230    
231            if (componentClass == null)
232                return null;
233    
234            IComponentSpecification spec = new ComponentSpecification();
235    
236            Resource namespaceResource = namespace.getSpecificationLocation();
237    
238            Resource componentResource = namespaceResource.getRelativeResource(type + ".jwc");
239    
240            Location location = new LocationImpl(componentResource);
241    
242            spec.setLocation(location);
243            spec.setSpecificationLocation(componentResource);
244            spec.setComponentClassName(componentClass.getName());
245    
246            return spec;
247        }
248    
249        private IComponentSpecification check(Resource resource)
250        {
251            if (_log.isDebugEnabled())
252                _log.debug("Checking: " + resource);
253    
254            if (resource.getResourceURL() == null)
255                return null;
256    
257            return getSpecificationSource().getComponentSpecification(resource);
258        }
259    
260        private void install()
261        {
262            INamespace namespace = getNamespace();
263            IComponentSpecification specification = getSpecification();
264    
265            if (_log.isDebugEnabled())
266                _log.debug(ResolverMessages.installingComponent(_type, namespace, specification));
267    
268            namespace.installComponentSpecification(_type, specification);
269        }
270    
271        public String getType()
272        {
273            return _type;
274        }
275    
276        public void setLog(Log log)
277        {
278            _log = log;
279        }
280    
281        public void setClassFinder(ClassFinder classFinder)
282        {
283            _classFinder = classFinder;
284        }
285    
286    }