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