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.engine;
016
017import java.util.ArrayList;
018import java.util.Collections;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.hivemind.ApplicationRuntimeException;
026import org.apache.hivemind.Location;
027import org.apache.hivemind.Resource;
028import org.apache.tapestry.INamespace;
029import org.apache.tapestry.Tapestry;
030import org.apache.tapestry.services.NamespaceResources;
031import org.apache.tapestry.spec.IComponentSpecification;
032import org.apache.tapestry.spec.ILibrarySpecification;
033
034/**
035 * Implementation of {@link org.apache.tapestry.INamespace} that works with a
036 * {@link org.apache.tapestry.services.NamespaceResources} to obtain page and component
037 * specifications as needed.
038 * 
039 * @author Howard Lewis Ship
040 * @since 2.2
041 */
042
043public class Namespace implements INamespace
044{
045    private final ILibrarySpecification _specification;
046
047    private final String _id;
048
049    private String _extendedId;
050
051    private final INamespace _parent;
052
053    private final boolean _frameworkNamespace;
054
055    private final boolean _applicationNamespace;
056
057    /** @since 4.0 */
058
059    private final NamespaceResources _resources;
060
061    /**
062     * Map of {@link org.apache.tapestry.spec.ComponentSpecification}keyed on page name. The map is
063     * synchronized because different threads may try to update it simultaneously (due to dynamic
064     * page discovery in the application namespace).
065     */
066
067    private final Map _pages = Collections.synchronizedMap(new HashMap());
068
069    /**
070     * Map of {@link org.apache.tapestry.spec.ComponentSpecification}keyed on component alias.
071     */
072
073    private final Map _components = Collections.synchronizedMap(new HashMap());
074
075    /**
076     * Map, keyed on id, of {@link INamespace}.
077     */
078
079    private final Map _children = Collections.synchronizedMap(new HashMap());
080
081    public Namespace(String id, INamespace parent, ILibrarySpecification specification,
082            NamespaceResources resources)
083    {
084        _id = id;
085        _parent = parent;
086        _specification = specification;
087        _resources = resources;
088
089        _applicationNamespace = (_id == null);
090        _frameworkNamespace = FRAMEWORK_NAMESPACE.equals(_id);
091    }
092
093    public String toString()
094    {
095        StringBuffer buffer = new StringBuffer("Namespace@");
096        buffer.append(Integer.toHexString(hashCode()));
097        buffer.append('[');
098
099        if (_applicationNamespace)
100            buffer.append("<application>");
101        else
102            buffer.append(getExtendedId());
103
104        buffer.append(']');
105
106        return buffer.toString();
107    }
108
109    public String getId()
110    {
111        return _id;
112    }
113
114    public String getExtendedId()
115    {
116        if (_applicationNamespace)
117            return null;
118
119        if (_extendedId == null)
120            _extendedId = buildExtendedId();
121
122        return _extendedId;
123    }
124
125    public INamespace getParentNamespace()
126    {
127        return _parent;
128    }
129
130    public INamespace getChildNamespace(String id)
131    {
132        String firstId = id;
133        String nextIds = null;
134
135        // Split the id into first and next if it is a dot separated sequence
136        int index = id.indexOf('.');
137        if (index >= 0)
138        {
139            firstId = id.substring(0, index);
140            nextIds = id.substring(index + 1);
141        }
142
143        // Get the first namespace
144        INamespace result = (INamespace) _children.get(firstId);
145
146        if (result == null)
147        {
148            result = createNamespace(firstId);
149
150            _children.put(firstId, result);
151        }
152
153        // If the id is a dot separated sequence, recurse to find
154        // the needed namespace
155        if (result != null && nextIds != null)
156            result = result.getChildNamespace(nextIds);
157
158        return result;
159    }
160
161    public List getChildIds()
162    {
163        return _specification.getLibraryIds();
164    }
165
166    public IComponentSpecification getPageSpecification(String name)
167    {
168        IComponentSpecification result = (IComponentSpecification) _pages.get(name);
169
170        if (result == null)
171        {
172            result = locatePageSpecification(name);
173
174            _pages.put(name, result);
175        }
176
177        return result;
178    }
179
180    public List getPageNames()
181    {
182        Set names = new HashSet();
183
184        names.addAll(_pages.keySet());
185        names.addAll(_specification.getPageNames());
186
187        List result = new ArrayList(names);
188
189        Collections.sort(result);
190
191        return result;
192    }
193
194    public IComponentSpecification getComponentSpecification(String alias)
195    {
196        IComponentSpecification result = (IComponentSpecification) _components.get(alias);
197
198        if (result == null)
199        {
200            result = locateComponentSpecification(alias);
201            _components.put(alias, result);
202        }
203
204        return result;
205    }
206
207    public ILibrarySpecification getSpecification()
208    {
209        return _specification;
210    }
211
212    private String buildExtendedId()
213    {
214        if (_parent == null)
215            return _id;
216
217        String parentId = _parent.getExtendedId();
218
219        // If immediate child of application namespace
220
221        if (parentId == null)
222            return _id;
223
224        return parentId + "." + _id;
225    }
226
227    /**
228     * Returns a string identifying the namespace, for use in error messages. I.e., "Application
229     * namespace" or "namespace 'foo'".
230     */
231
232    public String getNamespaceId()
233    {
234        if (_frameworkNamespace)
235            return Tapestry.getMessage("Namespace.framework-namespace");
236
237        if (_applicationNamespace)
238            return Tapestry.getMessage("Namespace.application-namespace");
239
240        return Tapestry.format("Namespace.nested-namespace", getExtendedId());
241    }
242
243    /**
244     * Gets the specification from the specification source.
245     * 
246     * @throws ApplicationRuntimeException
247     *             if the named page is not defined.
248     */
249
250    private IComponentSpecification locatePageSpecification(String name)
251    {
252        String path = _specification.getPageSpecificationPath(name);
253
254        if (path == null)
255            throw new ApplicationRuntimeException(Tapestry.format(
256                    "Namespace.no-such-page",
257                    name,
258                    getNamespaceId()));
259
260        // We don't record line-precise data about <page> elements
261        // so use the location for the specification as a whole (at least identifying
262        // the right file)
263
264        return _resources.getPageSpecification(getSpecificationLocation(), path, getLocation());
265    }
266
267    private IComponentSpecification locateComponentSpecification(String type)
268    {
269        String path = _specification.getComponentSpecificationPath(type);
270
271        if (path == null)
272            throw new ApplicationRuntimeException(Tapestry.format(
273                    "Namespace.no-such-alias",
274                    type,
275                    getNamespaceId()));
276
277        // We don't record line-precise data about <component-type> elements
278        // so use the location for the specification as a whole (at least identifying
279        // the right file)
280
281        return _resources
282                .getComponentSpecification(getSpecificationLocation(), path, getLocation());
283    }
284
285    private INamespace createNamespace(String id)
286    {
287        String path = _specification.getLibrarySpecificationPath(id);
288
289        if (path == null)
290            throw new ApplicationRuntimeException(Tapestry.format(
291                    "Namespace.library-id-not-found",
292                    id,
293                    getNamespaceId()));
294
295        // We don't record line-precise data about <library> elements
296        // so use the location for the specification as a whole (at least identifying
297        // the right file)
298
299        ILibrarySpecification ls = _resources.findChildLibrarySpecification(
300                getSpecificationLocation(),
301                path,
302                getLocation());
303
304        return new Namespace(id, this, ls, _resources);
305    }
306
307    public synchronized boolean containsPage(String name)
308    {
309        return _pages.containsKey(name) || (_specification.getPageSpecificationPath(name) != null);
310    }
311
312    /** @since 2.3 * */
313
314    public String constructQualifiedName(String pageName)
315    {
316        String prefix = getExtendedId();
317
318        if (prefix == null)
319            return pageName;
320
321        return prefix + SEPARATOR + pageName;
322    }
323
324    /** @since 3.0 * */
325
326    public Resource getSpecificationLocation()
327    {
328        return _specification.getSpecificationLocation();
329    }
330
331    /** @since 3.0 * */
332
333    public boolean isApplicationNamespace()
334    {
335        return _applicationNamespace;
336    }
337
338    /** @since 3.0 * */
339
340    public synchronized void installPageSpecification(String pageName,
341            IComponentSpecification specification)
342    {
343        _pages.put(pageName, specification);
344    }
345
346    /** @since 3.0 * */
347
348    public synchronized void installComponentSpecification(String type,
349            IComponentSpecification specification)
350    {
351        _components.put(type, specification);
352    }
353
354    /** @since 3.0 * */
355
356    public synchronized boolean containsComponentType(String type)
357    {
358        return _components.containsKey(type)
359                || (_specification.getComponentSpecificationPath(type) != null);
360    }
361
362    /** @since 3.0 * */
363
364    public Location getLocation()
365    {
366        if (_specification == null)
367            return null;
368
369        return _specification.getLocation();
370    }
371
372    /**
373     * Returns property values defined in the namespace's library specification.
374     * 
375     * @return the property, or null if not provided in the specification.
376     * @since 4.0
377     */
378
379    public String getPropertyValue(String propertyName)
380    {
381        return _specification.getProperty(propertyName);
382    }
383}