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 }