View Javadoc

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 }