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 }