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, ILibrarySpecification specification,
080                         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
100                buffer.append(getExtendedId());
101    
102            buffer.append(']');
103    
104            return buffer.toString();
105        }
106    
107        public String getId()
108        {
109            return _id;
110        }
111    
112        public String getExtendedId()
113        {
114            if (_applicationNamespace)
115                return null;
116    
117            if (_extendedId == null)
118                _extendedId = buildExtendedId();
119    
120            return _extendedId;
121        }
122    
123        public INamespace getParentNamespace()
124        {
125            return _parent;
126        }
127    
128        public INamespace getChildNamespace(String id)
129        {
130            String firstId = id;
131            String nextIds = null;
132    
133            // Split the id into first and next if it is a dot separated sequence
134            int index = id.indexOf('.');
135            if (index >= 0)
136            {
137                firstId = id.substring(0, index);
138                nextIds = id.substring(index + 1);
139            }
140    
141            // Get the first namespace
142            INamespace result = (INamespace) _children.get(firstId);
143    
144            if (result == null)
145            {
146                result = createNamespace(firstId);
147    
148                _children.put(firstId, result);
149            }
150    
151            // If the id is a dot separated sequence, recurse to find
152            // the needed namespace
153            if (result != null && nextIds != null)
154                result = result.getChildNamespace(nextIds);
155    
156            return result;
157        }
158    
159        public List getChildIds()
160        {
161            return _specification.getLibraryIds();
162        }
163    
164        public IComponentSpecification getPageSpecification(String name)
165        {
166            IComponentSpecification result = (IComponentSpecification) _pages.get(name);
167    
168            if (result == null)
169            {
170                result = locatePageSpecification(name);
171    
172                _pages.put(name, result);
173            }
174    
175            return result;
176        }
177    
178        public List getPageNames()
179        {
180            Set names = new HashSet();
181    
182            names.addAll(_pages.keySet());
183            names.addAll(_specification.getPageNames());
184    
185            List result = new ArrayList(names);
186    
187            Collections.sort(result);
188    
189            return result;
190        }
191    
192        public IComponentSpecification getComponentSpecification(String alias)
193        {
194            IComponentSpecification result = (IComponentSpecification) _components.get(alias);
195    
196            if (result == null)
197            {
198                result = locateComponentSpecification(alias);
199                _components.put(alias, result);
200            }
201    
202            return result;
203        }
204    
205        public ILibrarySpecification getSpecification()
206        {
207            return _specification;
208        }
209    
210        private String buildExtendedId()
211        {
212            if (_parent == null)
213                return _id;
214    
215            String parentId = _parent.getExtendedId();
216    
217            // If immediate child of application namespace
218    
219            if (parentId == null)
220                return _id;
221    
222            return parentId + "." + _id;
223        }
224    
225        /**
226         * Returns a string identifying the namespace, for use in error messages.
227         * I.e., "Application namespace" or "namespace 'foo'".
228         */
229    
230        public String getNamespaceId()
231        {
232            if (_frameworkNamespace)
233                return Tapestry.getMessage("Namespace.framework-namespace");
234    
235            if (_applicationNamespace)
236                return Tapestry.getMessage("Namespace.application-namespace");
237    
238            return Tapestry.format("Namespace.nested-namespace", getExtendedId());
239        }
240    
241        /**
242         * Gets the specification from the specification source.
243         *
244         * @throws ApplicationRuntimeException
245         *             if the named page is not defined.
246         */
247    
248        private IComponentSpecification locatePageSpecification(String name)
249        {
250            String path = _specification.getPageSpecificationPath(name);
251    
252            if (path == null)
253                throw new ApplicationRuntimeException(Tapestry.format("Namespace.no-such-page", name, getNamespaceId()));
254    
255            // We don't record line-precise data about <page> elements
256            // so use the location for the specification as a whole (at least
257            // identifying
258            // the right file)
259    
260            return _resources.getPageSpecification(getSpecificationLocation(), path, getLocation());
261        }
262    
263        private IComponentSpecification locateComponentSpecification(String type)
264        {
265            String path = _specification.getComponentSpecificationPath(type);
266    
267            if (path == null)
268                throw new ApplicationRuntimeException(Tapestry.format("Namespace.no-such-alias", type, getNamespaceId()));
269    
270            // We don't record line-precise data about <component-type> elements
271            // so use the location for the specification as a whole (at least
272            // identifying
273            // the right file)
274    
275            return _resources.getComponentSpecification(getSpecificationLocation(), path, getLocation());
276        }
277    
278        private INamespace createNamespace(String id)
279        {
280            String path = _specification.getLibrarySpecificationPath(id);
281    
282            if (path == null)
283                throw new ApplicationRuntimeException(Tapestry.format("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 =
291              _resources.findChildLibrarySpecification(getSpecificationLocation(), path, getLocation());
292    
293            return new Namespace(id, this, ls, _resources);
294        }
295    
296        public boolean containsPage(String name)
297        {
298            return _pages.containsKey(name) || (_specification.getPageSpecificationPath(name) != null);
299        }
300    
301        /** @since 2.3 * */
302    
303        public String constructQualifiedName(String pageName)
304        {
305            String prefix = getExtendedId();
306    
307            if (prefix == null)
308                return pageName;
309    
310            return prefix + SEPARATOR + pageName;
311        }
312    
313        /** @since 3.0 * */
314    
315        public Resource getSpecificationLocation()
316        {
317            return _specification.getSpecificationLocation();
318        }
319    
320        /** @since 3.0 * */
321    
322        public boolean isApplicationNamespace()
323        {
324            return _applicationNamespace;
325        }
326    
327        /** @since 3.0 * */
328    
329        public void installPageSpecification(String pageName, IComponentSpecification specification)
330        {
331            _pages.put(pageName, specification);
332        }
333    
334        /** @since 3.0 * */
335    
336        public void installComponentSpecification(String type, IComponentSpecification specification)
337        {
338            _components.put(type, specification);
339        }
340    
341        /** @since 3.0 * */
342    
343        public boolean containsComponentType(String type)
344        {
345            return _components.containsKey(type) || (_specification.getComponentSpecificationPath(type) != null);
346        }
347    
348        /** @since 3.0 * */
349    
350        public Location getLocation()
351        {
352            if (_specification == null)
353                return null;
354    
355            return _specification.getLocation();
356        }
357    
358        /**
359         * Returns property values defined in the namespace's library specification.
360         *
361         * @return the property, or null if not provided in the specification.
362         * @since 4.0
363         */
364    
365        public String getPropertyValue(String propertyName)
366        {
367            String ret = _specification.getProperty(propertyName);
368    
369            if (ret == null && _parent != null)
370            {
371                return _parent.getPropertyValue(propertyName);
372            }
373    
374            return ret;
375        }
376    }