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