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 }