1 /* 2 * ModeShape (http://www.modeshape.org) 3 * See the COPYRIGHT.txt file distributed with this work for information 4 * regarding copyright ownership. Some portions may be licensed 5 * to Red Hat, Inc. under one or more contributor license agreements. 6 * See the AUTHORS.txt file in the distribution for a full listing of 7 * individual contributors. 8 * 9 * Unless otherwise indicated, all code in ModeShape is licensed 10 * to you under the terms of the GNU Lesser General Public License as 11 * published by the Free Software Foundation; either version 2.1 of 12 * the License, or (at your option) any later version. 13 * 14 * ModeShape is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this software; if not, write to the Free 21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 */ 24 package org.modeshape.jcr; 25 26 import java.security.AccessControlException; 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.List; 30 import javax.jcr.AccessDeniedException; 31 import javax.jcr.PropertyType; 32 import javax.jcr.RepositoryException; 33 import javax.jcr.UnsupportedRepositoryOperationException; 34 import javax.jcr.Value; 35 import javax.jcr.nodetype.NoSuchNodeTypeException; 36 import javax.jcr.nodetype.NodeDefinition; 37 import javax.jcr.nodetype.NodeType; 38 import javax.jcr.nodetype.NodeTypeIterator; 39 import javax.jcr.nodetype.NodeTypeManager; 40 import javax.jcr.nodetype.PropertyDefinition; 41 import net.jcip.annotations.Immutable; 42 import org.modeshape.common.util.CheckArg; 43 import org.modeshape.graph.ExecutionContext; 44 import org.modeshape.graph.property.Name; 45 import org.modeshape.graph.property.NameFactory; 46 import org.modeshape.graph.property.Path; 47 import org.modeshape.graph.query.validate.Schemata; 48 import org.modeshape.jcr.nodetype.InvalidNodeTypeDefinitionException; 49 import org.modeshape.jcr.nodetype.NodeDefinitionTemplate; 50 import org.modeshape.jcr.nodetype.NodeTypeDefinition; 51 import org.modeshape.jcr.nodetype.NodeTypeExistsException; 52 import org.modeshape.jcr.nodetype.NodeTypeTemplate; 53 import org.modeshape.jcr.nodetype.PropertyDefinitionTemplate; 54 55 /** 56 * Local implementation of @{link NodeTypeManager}. This class handles translation between {@link Name}s and {@link String}s based 57 * on the namespace registry from the session's execution context in order to support transient namespace remappings. All 58 * {@link NodeType}s returned by this implementation are wrapped with the execution context of the session to allow proper ongoing 59 * handling of names. This implies that reference equality is not a safe test for node type equivalence. 60 * 61 * @see RepositoryNodeTypeManager 62 */ 63 @Immutable 64 public class JcrNodeTypeManager implements NodeTypeManager { 65 66 private final JcrSession session; 67 private final RepositoryNodeTypeManager repositoryTypeManager; 68 private Schemata schemata; 69 70 JcrNodeTypeManager( JcrSession session, 71 RepositoryNodeTypeManager repositoryTypeManager ) { 72 this.session = session; 73 this.repositoryTypeManager = repositoryTypeManager; 74 } 75 76 private final ExecutionContext context() { 77 return session.getExecutionContext(); 78 } 79 80 Schemata schemata() { 81 if (schemata == null) { 82 schemata = repositoryTypeManager.getRepositorySchemata().getSchemataForSession(session); 83 assert schemata != null; 84 } 85 return schemata; 86 } 87 88 void signalNamespaceChanges() { 89 this.schemata = null; 90 } 91 92 /** 93 * {@inheritDoc} 94 * 95 * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes() 96 */ 97 public NodeTypeIterator getAllNodeTypes() { 98 return new JcrNodeTypeIterator(repositoryTypeManager.getAllNodeTypes()); 99 } 100 101 /** 102 * {@inheritDoc} 103 * 104 * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes() 105 */ 106 public NodeTypeIterator getMixinNodeTypes() { 107 Collection<JcrNodeType> rawTypes = repositoryTypeManager.getMixinNodeTypes(); 108 List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size()); 109 110 // Need to return a version of the node type with the current context 111 for (JcrNodeType type : rawTypes) { 112 types.add(type.with(context())); 113 } 114 115 return new JcrNodeTypeIterator(types); 116 } 117 118 /** 119 * {@inheritDoc} 120 * 121 * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String) 122 */ 123 public JcrNodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException { 124 Name ntName = context().getValueFactories().getNameFactory().create(nodeTypeName); 125 JcrNodeType type = repositoryTypeManager.getNodeType(ntName); 126 if (type != null) { 127 type = type.with(context()); 128 return type; 129 } 130 throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName)); 131 } 132 133 /** 134 * Returns the node type with the given name (if one exists) 135 * 136 * @param nodeTypeName the name of the node type to be returned 137 * @return the node type with the given name (if one exists) 138 * @see RepositoryNodeTypeManager#getNodeType(Name) 139 */ 140 JcrNodeType getNodeType( Name nodeTypeName ) { 141 JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName); 142 143 if (nodeType != null) { 144 nodeType = nodeType.with(context()); 145 } 146 147 return nodeType; 148 } 149 150 /** 151 * {@inheritDoc} 152 * 153 * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes() 154 */ 155 public NodeTypeIterator getPrimaryNodeTypes() { 156 Collection<JcrNodeType> rawTypes = repositoryTypeManager.getPrimaryNodeTypes(); 157 List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size()); 158 159 // Need to return a version of the node type with the current context 160 for (JcrNodeType type : rawTypes) { 161 types.add(type.with(context())); 162 } 163 164 return new JcrNodeTypeIterator(types); 165 } 166 167 /** 168 * Get the {@link NodeDefinition} for the root node. 169 * 170 * @return the definition; never null 171 * @throws RepositoryException 172 * @throws NoSuchNodeTypeException 173 */ 174 JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException { 175 for (NodeDefinition definition : repositoryTypeManager.getNodeType(ModeShapeLexicon.ROOT).getChildNodeDefinitions()) { 176 if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition; 177 } 178 assert false; // should not get here 179 return null; 180 } 181 182 /** 183 * Get the node definition given the supplied identifier. 184 * 185 * @param definitionId the identifier of the node definition 186 * @return the node definition, or null if there is no such definition (or if the ID was null) 187 */ 188 JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) { 189 if (definitionId == null) return null; 190 return repositoryTypeManager.getChildNodeDefinition(definitionId); 191 } 192 193 /** 194 * Get the property definition given the supplied identifier. 195 * 196 * @param definitionId the identifier of the node definition 197 * @return the property definition, or null if there is no such definition (or if the ID was null) 198 */ 199 JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId ) { 200 if (definitionId == null) return null; 201 return repositoryTypeManager.getPropertyDefinition(definitionId); 202 } 203 204 /** 205 * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the 206 * given property name, property type, and value. 207 * <p> 208 * This method first attempts to find a single-valued property definition with the supplied property name and 209 * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected. 210 * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property 211 * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints} . Otherwise, 212 * the process continues with each of the mixin types, in the order they are named. 213 * </p> 214 * <p> 215 * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is 216 * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property 217 * type, and compatible constraints, starting with the primary type and continuing with each mixin type. 218 * </p> 219 * <p> 220 * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin 221 * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the 222 * definition's property type and still satisfy the definition's constraints. 223 * </p> 224 * <p> 225 * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions. 226 * </p> 227 * <p> 228 * If no matching property definition could be found (and the supplied property name is not the residual name), the whole 229 * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*" 230 * name}). 231 * </p> 232 * <p> 233 * Finally, if no satisfactory property definition could be found, this method returns null. 234 * </p> 235 * 236 * @param primaryTypeName the name of the primary type; may not be null 237 * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search 238 * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically 239 * look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best 240 * residual property definition (if any). 241 * @param value the value, or null if the property is being removed 242 * @param checkMultiValuedDefinitions true if the type's multi-valued property definitions should be considered, or false if 243 * only single-value property definitions should be considered 244 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if 245 * this operation is being done from within internal implementations 246 * @return the best property definition, or <code>null</code> if no property definition allows the property with the supplied 247 * name, type and number of values 248 */ 249 final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName, 250 List<Name> mixinTypeNames, 251 Name propertyName, 252 Value value, 253 boolean checkMultiValuedDefinitions, 254 boolean skipProtected ) { 255 return repositoryTypeManager.findPropertyDefinition(primaryTypeName, 256 mixinTypeNames, 257 propertyName, 258 value, 259 checkMultiValuedDefinitions, 260 skipProtected); 261 } 262 263 /** 264 * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the 265 * given property name, property type, and value. with the given name and property type that allows the supplied values. 266 * <p> 267 * This method first attempts to find a single-valued property definition with the supplied property name and 268 * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected. 269 * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property 270 * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints} . Otherwise, 271 * the process continues with each of the mixin types, in the order they are named. 272 * </p> 273 * <p> 274 * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is 275 * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property 276 * type, and compatible constraints, starting with the primary type and continuing with each mixin type. 277 * </p> 278 * <p> 279 * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin 280 * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the 281 * definition's property type and still satisfy the definition's constraints. 282 * </p> 283 * <p> 284 * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions. 285 * </p> 286 * <p> 287 * If no matching property definition could be found (and the supplied property name is not the residual name), the whole 288 * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*" 289 * name}). 290 * </p> 291 * <p> 292 * Finally, if no satisfactory property definition could be found, this method returns null. 293 * </p> 294 * 295 * @param primaryTypeName the name of the primary type; may not be null 296 * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search 297 * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically 298 * look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best 299 * residual property definition (if any). 300 * @param values the values 301 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if 302 * this operation is being done from within internal implementations 303 * @return the best property definition, or <code>null</code> if no property definition allows the property with the supplied 304 * name, type and number of values 305 */ 306 final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName, 307 List<Name> mixinTypeNames, 308 Name propertyName, 309 Value[] values, 310 boolean skipProtected ) { 311 return repositoryTypeManager.findPropertyDefinition(primaryTypeName, mixinTypeNames, propertyName, values, skipProtected); 312 } 313 314 /** 315 * Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied 316 * name to be removed. 317 * 318 * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null 319 * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins 320 * to include in the search 321 * @param propertyName the name of the property to be removed; may not be null 322 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if 323 * this operation is being done from within internal implementations 324 * @return true if at least one child node definition does not require children with the supplied name to exist, or false 325 * otherwise 326 */ 327 boolean canRemoveProperty( Name primaryTypeNameOfParent, 328 List<Name> mixinTypeNamesOfParent, 329 Name propertyName, 330 boolean skipProtected ) { 331 return repositoryTypeManager.canRemoveProperty(primaryTypeNameOfParent, 332 mixinTypeNamesOfParent, 333 propertyName, 334 skipProtected); 335 } 336 337 /** 338 * Searches the supplied primary node type and the mixin node types of a parent node for a child node definition that is the 339 * best match for a new child with the given name, primary node type name, and whether there are existing children with the 340 * same name. 341 * 342 * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null 343 * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins 344 * to include in the search 345 * @param childName the name of the child to be added to the parent; may not be null 346 * @param childPrimaryNodeType the name of the primary node type for the child node, or null if the primary type is not known 347 * and the {@link NodeDefinition#getDefaultPrimaryType() definition's default primary type} will be used 348 * @param numberOfExistingChildrenWithSameName the number of existing children with the same name as the child to be added, or 349 * 0 if this new child will be the first child with this name (or if the number of children is not known) 350 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if 351 * this operation is being done from within internal implementations 352 * @return the best child node definition, or <code>null</code> if no node definition allows a new child with the supplied 353 * name, primary type, and whether there are already children with the same name 354 */ 355 final JcrNodeDefinition findChildNodeDefinition( Name primaryTypeNameOfParent, 356 List<Name> mixinTypeNamesOfParent, 357 Name childName, 358 Name childPrimaryNodeType, 359 int numberOfExistingChildrenWithSameName, 360 boolean skipProtected ) { 361 return repositoryTypeManager.findChildNodeDefinition(primaryTypeNameOfParent, 362 mixinTypeNamesOfParent, 363 childName, 364 childPrimaryNodeType, 365 numberOfExistingChildrenWithSameName, 366 skipProtected); 367 } 368 369 /** 370 * Determine if the child node definitions of the supplied primary type and mixin types of a parent node allow all of the 371 * children with the supplied name to be removed. 372 * 373 * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null 374 * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins 375 * to include in the search 376 * @param childName the name of the child to be added to the parent; may not be null 377 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if 378 * this operation is being done from within internal implementations 379 * @return true if at least one child node definition does not require children with the supplied name to exist, or false 380 * otherwise 381 */ 382 final boolean canRemoveAllChildren( Name primaryTypeNameOfParent, 383 List<Name> mixinTypeNamesOfParent, 384 Name childName, 385 boolean skipProtected ) { 386 return repositoryTypeManager.canRemoveAllChildren(primaryTypeNameOfParent, 387 mixinTypeNamesOfParent, 388 childName, 389 skipProtected); 390 } 391 392 /** 393 * Registers a new node type or updates an existing node type using the specified definition and returns the resulting 394 * {@link NodeType} object. 395 * <p> 396 * Typically, the object passed to this method will be a {@link NodeTypeTemplate} (a subclass of {@link NodeTypeDefinition}) 397 * acquired from {@link JcrNodeTypeManager#createNodeTypeTemplate()} and then filled-in with definition information. 398 * </p> 399 * 400 * @param template the new node type to register 401 * @param allowUpdate must be {@code false}; ModeShape does not allow updating node types at this time 402 * @return the {@code newly created node type} 403 * @throws InvalidNodeTypeDefinitionException if the {@code NodeTypeDefinition} is invalid 404 * @throws NodeTypeExistsException if {@code allowUpdate} is false and the {@code NodeTypeDefinition} specifies a node type 405 * name that already exists 406 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node 407 * types at this time. 408 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register 409 * type permission}. 410 * @throws RepositoryException if another error occurs 411 */ 412 public NodeType registerNodeType( NodeTypeDefinition template, 413 boolean allowUpdate ) 414 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, 415 AccessDeniedException, RepositoryException { 416 417 try { 418 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE); 419 } catch (AccessControlException ace) { 420 throw new AccessDeniedException(ace); 421 } 422 try { 423 return this.repositoryTypeManager.registerNodeType(template, allowUpdate); 424 } finally { 425 schemata = null; 426 } 427 } 428 429 /** 430 * Registers or updates the specified Collection of {@code NodeTypeDefinition} objects. This method is used to register or 431 * update a set of node types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects. 432 * <p> 433 * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated. 434 * </p> 435 * 436 * @param templates the new node types to register 437 * @param allowUpdates must be {@code false}; ModeShape does not allow updating node types at this time 438 * @return the {@code newly created node types} 439 * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid 440 * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection 441 * specifies a node type name that already exists 442 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node 443 * types at this time. 444 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register 445 * type permission}. 446 * @throws RepositoryException if another error occurs 447 */ 448 public NodeTypeIterator registerNodeTypes( Collection<NodeTypeDefinition> templates, 449 boolean allowUpdates ) 450 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, 451 AccessDeniedException, RepositoryException { 452 453 try { 454 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE); 455 } catch (AccessControlException ace) { 456 throw new AccessDeniedException(ace); 457 } 458 try { 459 return new JcrNodeTypeIterator(repositoryTypeManager.registerNodeTypes(templates, allowUpdates)); 460 } finally { 461 schemata = null; 462 } 463 } 464 465 /** 466 * Registers the node types from the given {@code JcrNodeTypeSource}. This method is used to register or update a set of node 467 * types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects. 468 * <p> 469 * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated. 470 * </p> 471 * 472 * @param source the new node types to register 473 * @return the {@code newly created node types} 474 * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid 475 * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection 476 * specifies a node type name that already exists 477 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node 478 * types at this time. 479 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register 480 * type permission}. 481 * @throws RepositoryException if another error occurs 482 */ 483 public NodeTypeIterator registerNodeTypes( JcrNodeTypeSource source ) 484 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, 485 AccessDeniedException, RepositoryException { 486 487 try { 488 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE); 489 } catch (AccessControlException ace) { 490 throw new AccessDeniedException(ace); 491 } 492 493 try { 494 return new JcrNodeTypeIterator(this.repositoryTypeManager.registerNodeTypes(source)); 495 } finally { 496 schemata = null; 497 } 498 } 499 500 /** 501 * Allows the collection of node types to be unregistered if they are not referenced by other node types as supertypes, 502 * default primary types of child nodes, or required primary types of child nodes. 503 * <p> 504 * <b>NOTE: This method does not check to see if any of the node types are currently being used. Unregistering a node type 505 * that is being used will cause the system to become unstable</b> 506 * </p> 507 * 508 * @param nodeTypeNames the names of the node types to be unregistered 509 * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered node type 510 * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot be unregistered because 511 * they are the supertype, one of the required primary types, or a default primary type of a node type that is not 512 * being unregistered. 513 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register 514 * type permission}. 515 * @throws RepositoryException if any other error occurs 516 */ 517 public void unregisterNodeType( Collection<String> nodeTypeNames ) 518 throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException { 519 NameFactory nameFactory = context().getValueFactories().getNameFactory(); 520 521 try { 522 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE); 523 } catch (AccessControlException ace) { 524 throw new AccessDeniedException(ace); 525 } 526 527 Collection<Name> names = new ArrayList<Name>(nodeTypeNames.size()); 528 for (String name : nodeTypeNames) { 529 names.add(nameFactory.create(name)); 530 } 531 repositoryTypeManager.unregisterNodeType(names); 532 schemata = null; 533 } 534 535 /** 536 * Returns an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to 537 * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)} 538 * 539 * @return an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to 540 * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}. 541 * @throws RepositoryException if another error occurs 542 */ 543 public NodeTypeTemplate createNodeTypeTemplate() throws RepositoryException { 544 return new JcrNodeTypeTemplate(context()); 545 } 546 547 /** 548 * Returns an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to 549 * a {@code NodeTypeTemplate}. 550 * 551 * @return an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to 552 * a {@code NodeTypeTemplate}. 553 * @throws RepositoryException if another error occurs 554 */ 555 public NodeDefinitionTemplate createNodeDefinitionTemplate() throws RepositoryException { 556 return new JcrNodeDefinitionTemplate(context()); 557 } 558 559 /** 560 * Returns an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to 561 * a {@code NodeTypeTemplate}. 562 * 563 * @return an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to 564 * a {@code NodeTypeTemplate}. 565 * @throws RepositoryException if another error occurs 566 */ 567 public PropertyDefinitionTemplate createPropertyDefinitionTemplate() throws RepositoryException { 568 return new JcrPropertyDefinitionTemplate(context()); 569 } 570 571 /** 572 * Determine if any of the test type names are equal to or have been derived from the primary type or any of the mixins. 573 * 574 * @param testTypeNames the names of the types or mixins being tested against (never <code>null</code>) 575 * @param primaryTypeName the primary type name (never <code>null</code>) 576 * @param mixinNames the mixin names (may be <code>null</code>) 577 * @return <code>true</code> if at least one test type name is equal to or derived from the primary type or one of the mixins 578 * @throws RepositoryException if there is an exception obtaining node types 579 * @throws IllegalArgumentException if <code>testTypeNames</code> is <code>null</code> or empty or if 580 * <code>primaryTypeName</code> is <code>null</code> or zero length 581 */ 582 public boolean isDerivedFrom( String[] testTypeNames, 583 String primaryTypeName, 584 String[] mixinNames ) throws RepositoryException { 585 CheckArg.isNotEmpty(testTypeNames, "testTypeNames"); 586 CheckArg.isNotEmpty(primaryTypeName, "primaryTypeName"); 587 588 NameFactory nameFactory = context().getValueFactories().getNameFactory(); 589 Name[] typeNames = nameFactory.create(testTypeNames); 590 591 // first check primary type 592 for (Name typeName : typeNames) { 593 JcrNodeType nodeType = getNodeType(typeName); 594 595 if ((nodeType != null) && nodeType.isNodeType(primaryTypeName)) { 596 return true; 597 } 598 } 599 600 // now check mixins 601 if (mixinNames != null) { 602 for (String mixin : mixinNames) { 603 for (Name typeName : typeNames) { 604 JcrNodeType nodeType = getNodeType(typeName); 605 606 if ((nodeType != null) && nodeType.isNodeType(mixin)) { 607 return true; 608 } 609 } 610 } 611 } 612 613 return false; 614 } 615 }