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.engine;
016    
017    import java.util.ArrayList;
018    import java.util.Collections;
019    import java.util.HashMap;
020    import java.util.HashSet;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Set;
024    
025    import org.apache.hivemind.ApplicationRuntimeException;
026    import org.apache.hivemind.Location;
027    import org.apache.hivemind.Resource;
028    import org.apache.tapestry.INamespace;
029    import org.apache.tapestry.Tapestry;
030    import org.apache.tapestry.services.NamespaceResources;
031    import org.apache.tapestry.spec.IComponentSpecification;
032    import 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    
043    public 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    }