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 }