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