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