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.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.List;
32 import javax.jcr.AccessDeniedException;
33 import javax.jcr.PropertyType;
34 import javax.jcr.RepositoryException;
35 import javax.jcr.UnsupportedRepositoryOperationException;
36 import javax.jcr.Value;
37 import javax.jcr.nodetype.NoSuchNodeTypeException;
38 import javax.jcr.nodetype.NodeDefinition;
39 import javax.jcr.nodetype.NodeType;
40 import javax.jcr.nodetype.NodeTypeDefinition;
41 import javax.jcr.nodetype.NodeTypeIterator;
42 import javax.jcr.nodetype.NodeTypeManager;
43 import javax.jcr.nodetype.PropertyDefinition;
44 import net.jcip.annotations.Immutable;
45 import org.modeshape.common.util.CheckArg;
46 import org.modeshape.graph.ExecutionContext;
47 import org.modeshape.graph.property.Name;
48 import org.modeshape.graph.property.NameFactory;
49 import org.modeshape.graph.property.Path;
50 import org.modeshape.graph.query.validate.Schemata;
51 import org.modeshape.jcr.nodetype.InvalidNodeTypeDefinitionException;
52 import org.modeshape.jcr.nodetype.NodeDefinitionTemplate;
53 import org.modeshape.jcr.nodetype.NodeTypeExistsException;
54 import org.modeshape.jcr.nodetype.NodeTypeTemplate;
55 import org.modeshape.jcr.nodetype.PropertyDefinitionTemplate;
56
57 /**
58 * Local implementation of @{link NodeTypeManager}. This class handles translation between {@link Name}s and {@link String}s based
59 * on the namespace registry from the session's execution context in order to support transient namespace remappings. All
60 * {@link NodeType}s returned by this implementation are wrapped with the execution context of the session to allow proper ongoing
61 * handling of names. This implies that reference equality is not a safe test for node type equivalence.
62 *
63 * @see RepositoryNodeTypeManager
64 */
65 @Immutable
66 public class JcrNodeTypeManager implements NodeTypeManager {
67
68 private final JcrSession session;
69 private final RepositoryNodeTypeManager repositoryTypeManager;
70 private Schemata schemata;
71
72 JcrNodeTypeManager( JcrSession session,
73 RepositoryNodeTypeManager repositoryTypeManager ) {
74 this.session = session;
75 this.repositoryTypeManager = repositoryTypeManager;
76 }
77
78 private final ExecutionContext context() {
79 return session.getExecutionContext();
80 }
81
82 Schemata schemata() {
83 if (schemata == null) {
84 schemata = repositoryTypeManager.getRepositorySchemata().getSchemataForSession(session);
85 assert schemata != null;
86 }
87 return schemata;
88 }
89
90 void signalNamespaceChanges() {
91 this.schemata = null;
92 }
93
94 void signalExternalNodeTypeChanges() {
95 this.schemata = null;
96 }
97
98 /**
99 * {@inheritDoc}
100 *
101 * @see javax.jcr.nodetype.NodeTypeManager#getAllNodeTypes()
102 */
103 public NodeTypeIterator getAllNodeTypes() throws RepositoryException {
104 session.checkLive();
105 return new JcrNodeTypeIterator(repositoryTypeManager.getAllNodeTypes());
106 }
107
108 /**
109 * {@inheritDoc}
110 *
111 * @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes()
112 */
113 public NodeTypeIterator getMixinNodeTypes() throws RepositoryException {
114 session.checkLive();
115 Collection<JcrNodeType> rawTypes = repositoryTypeManager.getMixinNodeTypes();
116 List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size());
117
118 // Need to return a version of the node type with the current context
119 for (JcrNodeType type : rawTypes) {
120 types.add(type.with(context()));
121 }
122
123 return new JcrNodeTypeIterator(types);
124 }
125
126 /**
127 * {@inheritDoc}
128 *
129 * @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
130 */
131 public JcrNodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
132 session.checkLive();
133 Name ntName = context().getValueFactories().getNameFactory().create(nodeTypeName);
134 JcrNodeType type = repositoryTypeManager.getNodeType(ntName);
135 if (type != null) {
136 type = type.with(context());
137 return type;
138 }
139 throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName));
140 }
141
142 /**
143 * Returns the node type with the given name (if one exists)
144 *
145 * @param nodeTypeName the name of the node type to be returned
146 * @return the node type with the given name (if one exists)
147 * @see RepositoryNodeTypeManager#getNodeType(Name)
148 */
149 JcrNodeType getNodeType( Name nodeTypeName ) {
150 JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
151
152 if (nodeType != null) {
153 nodeType = nodeType.with(context());
154 }
155
156 return nodeType;
157 }
158
159 /**
160 * Returns true if and only if the node type with the given name exists.
161 * <p>
162 * This is equivalent to the following code:
163 *
164 * <pre>
165 * try {
166 * getNodeType(nodeTypeName);
167 * return true;
168 * } catch (NoSuchNodeTypeException nsnte) {
169 * return false;
170 * }
171 * </pre>
172 *
173 * However, the implementation is more efficient that the approach listed above and does not rely upon exceptions.
174 * </p>
175 *
176 * @param nodeTypeName the name of the node type
177 * @return true if the named node type does exist, or false otherwise
178 * @see RepositoryNodeTypeManager#hasNodeType(Name)
179 */
180 public boolean hasNodeType( String nodeTypeName ) {
181 Name ntName = context().getValueFactories().getNameFactory().create(nodeTypeName);
182 return repositoryTypeManager.hasNodeType(ntName);
183 }
184
185 /**
186 * {@inheritDoc}
187 *
188 * @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes()
189 */
190 public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException {
191 session.checkLive();
192 Collection<JcrNodeType> rawTypes = repositoryTypeManager.getPrimaryNodeTypes();
193 List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size());
194
195 // Need to return a version of the node type with the current context
196 for (JcrNodeType type : rawTypes) {
197 types.add(type.with(context()));
198 }
199
200 return new JcrNodeTypeIterator(types);
201 }
202
203 /**
204 * Get the {@link NodeDefinition} for the root node.
205 *
206 * @return the definition; never null
207 * @throws RepositoryException
208 * @throws NoSuchNodeTypeException
209 */
210 JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException {
211 for (NodeDefinition definition : repositoryTypeManager.getNodeType(ModeShapeLexicon.ROOT).getChildNodeDefinitions()) {
212 if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition;
213 }
214
215 assert false; // should not get here
216 return null;
217 }
218
219 /**
220 * Get the node definition given the supplied identifier.
221 *
222 * @param definitionId the identifier of the node definition
223 * @return the node definition, or null if there is no such definition (or if the ID was null)
224 */
225 JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) {
226 if (definitionId == null) return null;
227 return repositoryTypeManager.getChildNodeDefinition(definitionId);
228 }
229
230 /**
231 * Get the property definition given the supplied identifier.
232 *
233 * @param definitionId the identifier of the node definition
234 * @return the property definition, or null if there is no such definition (or if the ID was null)
235 */
236 JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId ) {
237 if (definitionId == null) return null;
238 return repositoryTypeManager.getPropertyDefinition(definitionId);
239 }
240
241 /**
242 * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
243 * given property name, property type, and value.
244 * <p>
245 * This method first attempts to find a single-valued property definition with the supplied property name and
246 * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
247 * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
248 * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints} . Otherwise,
249 * the process continues with each of the mixin types, in the order they are named.
250 * </p>
251 * <p>
252 * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
253 * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
254 * type, and compatible constraints, starting with the primary type and continuing with each mixin type.
255 * </p>
256 * <p>
257 * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
258 * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
259 * definition's property type and still satisfy the definition's constraints.
260 * </p>
261 * <p>
262 * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
263 * </p>
264 * <p>
265 * If no matching property definition could be found (and the supplied property name is not the residual name), the whole
266 * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
267 * name}).
268 * </p>
269 * <p>
270 * Finally, if no satisfactory property definition could be found, this method returns null.
271 * </p>
272 *
273 * @param primaryTypeName the name of the primary type; may not be null
274 * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
275 * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically
276 * look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
277 * residual property definition (if any).
278 * @param value the value, or null if the property is being removed
279 * @param checkMultiValuedDefinitions true if the type's multi-valued property definitions should be considered, or false if
280 * only single-value property definitions should be considered
281 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
282 * this operation is being done from within internal implementations
283 * @return the best property definition, or <code>null</code> if no property definition allows the property with the supplied
284 * name, type and number of values
285 */
286 final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName,
287 List<Name> mixinTypeNames,
288 Name propertyName,
289 Value value,
290 boolean checkMultiValuedDefinitions,
291 boolean skipProtected ) {
292 return repositoryTypeManager.findPropertyDefinition(primaryTypeName,
293 mixinTypeNames,
294 propertyName,
295 value,
296 checkMultiValuedDefinitions,
297 skipProtected);
298 }
299
300 /**
301 * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the
302 * given property name, property type, and value. with the given name and property type that allows the supplied values.
303 * <p>
304 * This method first attempts to find a single-valued property definition with the supplied property name and
305 * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected.
306 * The property definition is returned if it has a matching type (or has an {@link PropertyType#UNDEFINED undefined property
307 * type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints} . Otherwise,
308 * the process continues with each of the mixin types, in the order they are named.
309 * </p>
310 * <p>
311 * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is
312 * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property
313 * type, and compatible constraints, starting with the primary type and continuing with each mixin type.
314 * </p>
315 * <p>
316 * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin
317 * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the
318 * definition's property type and still satisfy the definition's constraints.
319 * </p>
320 * <p>
321 * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions.
322 * </p>
323 * <p>
324 * If no matching property definition could be found (and the supplied property name is not the residual name), the whole
325 * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*"
326 * name}).
327 * </p>
328 * <p>
329 * Finally, if no satisfactory property definition could be found, this method returns null.
330 * </p>
331 *
332 * @param primaryTypeName the name of the primary type; may not be null
333 * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search
334 * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically
335 * look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best
336 * residual property definition (if any).
337 * @param values the values
338 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
339 * this operation is being done from within internal implementations
340 * @return the best property definition, or <code>null</code> if no property definition allows the property with the supplied
341 * name, type and number of values
342 */
343 final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName,
344 List<Name> mixinTypeNames,
345 Name propertyName,
346 Value[] values,
347 boolean skipProtected ) {
348 return repositoryTypeManager.findPropertyDefinition(primaryTypeName, mixinTypeNames, propertyName, values, skipProtected);
349 }
350
351 /**
352 * Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied
353 * name to be removed.
354 *
355 * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null
356 * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins
357 * to include in the search
358 * @param propertyName the name of the property to be removed; may not be null
359 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
360 * this operation is being done from within internal implementations
361 * @return true if at least one child node definition does not require children with the supplied name to exist, or false
362 * otherwise
363 */
364 boolean canRemoveProperty( Name primaryTypeNameOfParent,
365 List<Name> mixinTypeNamesOfParent,
366 Name propertyName,
367 boolean skipProtected ) {
368 return repositoryTypeManager.canRemoveProperty(primaryTypeNameOfParent,
369 mixinTypeNamesOfParent,
370 propertyName,
371 skipProtected);
372 }
373
374 /**
375 * Searches the supplied primary node type and the mixin node types of a parent node for a child node definition that is the
376 * best match for a new child with the given name, primary node type name, and whether there are existing children with the
377 * same name.
378 *
379 * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null
380 * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins
381 * to include in the search
382 * @param childName the name of the child to be added to the parent; may not be null
383 * @param childPrimaryNodeType the name of the primary node type for the child node, or null if the primary type is not known
384 * and the {@link NodeDefinition#getDefaultPrimaryType() definition's default primary type} will be used
385 * @param numberOfExistingChildrenWithSameName the number of existing children with the same name as the child to be added, or
386 * 0 if this new child will be the first child with this name (or if the number of children is not known)
387 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
388 * this operation is being done from within internal implementations
389 * @return the best child node definition, or <code>null</code> if no node definition allows a new child with the supplied
390 * name, primary type, and whether there are already children with the same name
391 */
392 final JcrNodeDefinition findChildNodeDefinition( Name primaryTypeNameOfParent,
393 List<Name> mixinTypeNamesOfParent,
394 Name childName,
395 Name childPrimaryNodeType,
396 int numberOfExistingChildrenWithSameName,
397 boolean skipProtected ) {
398 return repositoryTypeManager.findChildNodeDefinition(primaryTypeNameOfParent,
399 mixinTypeNamesOfParent,
400 childName,
401 childPrimaryNodeType,
402 numberOfExistingChildrenWithSameName,
403 skipProtected);
404 }
405
406 /**
407 * Determine if the child node definitions of the supplied primary type and mixin types of a parent node allow all of the
408 * children with the supplied name to be removed.
409 *
410 * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null
411 * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins
412 * to include in the search
413 * @param childName the name of the child to be added to the parent; may not be null
414 * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if
415 * this operation is being done from within internal implementations
416 * @return true if at least one child node definition does not require children with the supplied name to exist, or false
417 * otherwise
418 */
419 final boolean canRemoveAllChildren( Name primaryTypeNameOfParent,
420 List<Name> mixinTypeNamesOfParent,
421 Name childName,
422 boolean skipProtected ) {
423 return repositoryTypeManager.canRemoveAllChildren(primaryTypeNameOfParent,
424 mixinTypeNamesOfParent,
425 childName,
426 skipProtected);
427 }
428
429 /**
430 * Registers a new node type or updates an existing node type using the specified definition and returns the resulting
431 * {@link NodeType} object.
432 * <p>
433 * Typically, the object passed to this method will be a {@link NodeTypeTemplate} (a subclass of {@link NodeTypeDefinition})
434 * acquired from {@link JcrNodeTypeManager#createNodeTypeTemplate()} and then filled-in with definition information.
435 * </p>
436 *
437 * @param template the new node type to register
438 * @param allowUpdate this flag is not used
439 * @return the {@code newly created node type}
440 * @throws InvalidNodeTypeDefinitionException if the {@code NodeTypeDefinition} is invalid
441 * @throws NodeTypeExistsException if {@code allowUpdate} is false and the {@code NodeTypeDefinition} specifies a node type
442 * name that already exists
443 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node
444 * types at this time.
445 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
446 * type permission}.
447 * @throws RepositoryException if another error occurs
448 */
449 public NodeType registerNodeType( NodeTypeDefinition template,
450 boolean allowUpdate )
451 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException,
452 AccessDeniedException, RepositoryException {
453
454 session.checkLive();
455 try {
456 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE);
457 } catch (AccessControlException ace) {
458 throw new AccessDeniedException(ace);
459 }
460 try {
461 return this.repositoryTypeManager.registerNodeType(template);
462 } finally {
463 schemata = null;
464 }
465 }
466
467 /**
468 * Registers or updates the specified Collection of {@code NodeTypeDefinition} objects. This method is used to register or
469 * update a set of node types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects.
470 * <p>
471 * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated.
472 * </p>
473 *
474 * @param templates the new node types to register
475 * @param allowUpdates this flag is not used
476 * @return the {@code newly created node types}
477 * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid
478 * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection
479 * specifies a node type name that already exists
480 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node
481 * types at this time.
482 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
483 * type permission}.
484 * @throws RepositoryException if another error occurs
485 */
486 public NodeTypeIterator registerNodeTypes( Collection<NodeTypeDefinition> templates,
487 boolean allowUpdates )
488 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException,
489 AccessDeniedException, RepositoryException {
490
491 session.checkLive();
492 try {
493 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE);
494 } catch (AccessControlException ace) {
495 throw new AccessDeniedException(ace);
496 }
497 try {
498 return new JcrNodeTypeIterator(repositoryTypeManager.registerNodeTypes(templates));
499 } finally {
500 schemata = null;
501 }
502 }
503
504 /**
505 * Registers the node types from the given {@code JcrNodeTypeSource}. This method is used to register or update a set of node
506 * types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects.
507 * <p>
508 * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated.
509 * </p>
510 *
511 * @param nodeTypes the iterable object containing the new node types to register
512 * @return the {@code newly created node types}
513 * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid
514 * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection
515 * specifies a node type name that already exists
516 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node
517 * types at this time.
518 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
519 * type permission}.
520 * @throws RepositoryException if another error occurs
521 */
522 public NodeTypeIterator registerNodeTypes( Iterable<NodeTypeDefinition> nodeTypes )
523 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException,
524 AccessDeniedException, RepositoryException {
525
526 try {
527 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE);
528 } catch (AccessControlException ace) {
529 throw new AccessDeniedException(ace);
530 }
531
532 try {
533 return new JcrNodeTypeIterator(this.repositoryTypeManager.registerNodeTypes(nodeTypes));
534 } finally {
535 schemata = null;
536 }
537 }
538
539 /**
540 * Registers or updates the specified array of {@code NodeTypeDefinition} objects. This method is used to register or update a
541 * set of node types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects.
542 * <p>
543 * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated.
544 * </p>
545 *
546 * @param ntds the new node types to register
547 * @param allowUpdate must be {@code false}; ModeShape does not allow updating node types at this time
548 * @return the {@code newly created node types}
549 * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid
550 * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection
551 * specifies a node type name that already exists
552 * @throws UnsupportedRepositoryOperationException if {@code allowUpdate} is true; ModeShape does not allow updating node
553 * types at this time.
554 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
555 * type permission}.
556 * @throws RepositoryException if another error occurs
557 */
558 public NodeTypeIterator registerNodeTypes( NodeTypeDefinition[] ntds,
559 boolean allowUpdate )
560 throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException,
561 RepositoryException {
562
563 try {
564 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE);
565 } catch (AccessControlException ace) {
566 throw new AccessDeniedException(ace);
567 }
568
569 try {
570 return new JcrNodeTypeIterator(this.repositoryTypeManager.registerNodeTypes(ntds));
571 } finally {
572 schemata = null;
573 }
574 }
575
576 /**
577 * Unregisters the named node type if it is not referenced by other node types as a supertype, a default primary type of a
578 * child node (or nodes), or a required primary type of a child node (or nodes).
579 *
580 * @param nodeTypeName
581 * @throws NoSuchNodeTypeException if node type name does not correspond to a registered node type
582 * @throws InvalidNodeTypeDefinitionException if the node type with the given name cannot be unregistered because it is the
583 * supertype, one of the required primary types, or a default primary type of another node type
584 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
585 * type permission}.
586 * @throws RepositoryException if any other error occurs
587 */
588 public void unregisterNodeType( String nodeTypeName )
589 throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException {
590 unregisterNodeTypes(Collections.singleton(nodeTypeName));
591 }
592
593 /**
594 * Allows the collection of node types to be unregistered if they are not referenced by other node types as supertypes,
595 * default primary types of child nodes, or required primary types of child nodes.
596 *
597 * @param nodeTypeNames the names of the node types to be unregistered
598 * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered node type
599 * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot be unregistered because
600 * they are the supertype, one of the required primary types, or a default primary type of a node type that is not
601 * being unregistered.
602 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
603 * type permission}.
604 * @throws RepositoryException if any other error occurs
605 */
606 public void unregisterNodeTypes( Collection<String> nodeTypeNames )
607 throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException {
608 NameFactory nameFactory = context().getValueFactories().getNameFactory();
609
610 try {
611 session.checkPermission((Path)null, ModeShapePermissions.REGISTER_TYPE);
612 } catch (AccessControlException ace) {
613 throw new AccessDeniedException(ace);
614 }
615
616 Collection<Name> names = new ArrayList<Name>(nodeTypeNames.size());
617 for (String name : nodeTypeNames) {
618 names.add(nameFactory.create(name));
619 }
620 repositoryTypeManager.unregisterNodeType(names);
621 schemata = null;
622 }
623
624 /**
625 * Allows the collection of node types to be unregistered if they are not referenced by other node types as supertypes,
626 * default primary types of child nodes, or required primary types of child nodes.
627 *
628 * @param names the names of the node types to be unregistered
629 * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered node type
630 * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot be unregistered because
631 * they are the supertype, one of the required primary types, or a default primary type of a node type that is not
632 * being unregistered.
633 * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register
634 * type permission}.
635 * @throws RepositoryException if any other error occurs
636 */
637 public void unregisterNodeTypes( String[] names ) throws NoSuchNodeTypeException, RepositoryException {
638 unregisterNodeTypes(Arrays.asList(names));
639 }
640
641 /**
642 * Returns an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to
643 * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}
644 *
645 * @return an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to
646 * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}.
647 * @throws RepositoryException if another error occurs
648 */
649 public NodeTypeTemplate createNodeTypeTemplate() throws RepositoryException {
650 return new JcrNodeTypeTemplate(context());
651 }
652
653 /**
654 * Returns a {@code NodeTypeTemplate} based on the definition given in {@code ntd}. This template can then be used to define a
655 * node type and passed to {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}
656 *
657 * @param ntd an existing node type definition; null values will be ignored
658 * @return an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to
659 * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}.
660 * @throws RepositoryException if another error occurs
661 */
662 @SuppressWarnings( "unchecked" )
663 public NodeTypeTemplate createNodeTypeTemplate( NodeTypeDefinition ntd ) throws RepositoryException {
664 NodeTypeTemplate ntt = new JcrNodeTypeTemplate(context(), true);
665
666 if (ntd != null) {
667 ntt.setName(ntd.getName());
668 ntt.setAbstract(ntd.isAbstract());
669 ntt.setDeclaredSuperTypeNames(ntd.getDeclaredSupertypeNames());
670 ntt.setMixin(ntd.isMixin());
671 ntt.setOrderableChildNodes(ntd.hasOrderableChildNodes());
672 ntt.setPrimaryItemName(ntd.getPrimaryItemName());
673 ntt.setQueryable(ntd.isQueryable());
674
675 // copy child nodes and props
676 for (NodeDefinition nodeDefinition : ntd.getDeclaredChildNodeDefinitions()) {
677 JcrNodeDefinitionTemplate ndt = new JcrNodeDefinitionTemplate(context());
678
679 ndt.setAutoCreated(nodeDefinition.isAutoCreated());
680 ndt.setDefaultPrimaryType(nodeDefinition.getDefaultPrimaryTypeName());
681 ndt.setMandatory(nodeDefinition.isMandatory());
682 if (nodeDefinition.getName() != null) {
683 ndt.setName(nodeDefinition.getName());
684 }
685 ndt.setOnParentVersion(nodeDefinition.getOnParentVersion());
686 ndt.setProtected(nodeDefinition.isProtected());
687 ndt.setRequiredPrimaryTypeNames(nodeDefinition.getRequiredPrimaryTypeNames());
688 ndt.setSameNameSiblings(nodeDefinition.allowsSameNameSiblings());
689
690 ntt.getNodeDefinitionTemplates().add(ndt);
691 }
692
693 for (PropertyDefinition propertyDefinition : ntd.getDeclaredPropertyDefinitions()) {
694 JcrPropertyDefinitionTemplate pdt = new JcrPropertyDefinitionTemplate(context());
695
696 pdt.setAutoCreated(propertyDefinition.isAutoCreated());
697 pdt.setAvailableQueryOperators(propertyDefinition.getAvailableQueryOperators());
698 pdt.setDefaultValues(propertyDefinition.getDefaultValues());
699 pdt.setFullTextSearchable(propertyDefinition.isFullTextSearchable());
700 pdt.setMandatory(propertyDefinition.isMandatory());
701 pdt.setMultiple(propertyDefinition.isMultiple());
702 if (propertyDefinition.getName() != null) {
703 pdt.setName(propertyDefinition.getName());
704 }
705 pdt.setOnParentVersion(propertyDefinition.getOnParentVersion());
706 pdt.setProtected(propertyDefinition.isProtected());
707 pdt.setQueryOrderable(propertyDefinition.isQueryOrderable());
708 pdt.setRequiredType(propertyDefinition.getRequiredType());
709 pdt.setValueConstraints(propertyDefinition.getValueConstraints());
710
711 ntt.getPropertyDefinitionTemplates().add(pdt);
712 }
713 }
714
715 return ntt;
716 }
717
718 /**
719 * Returns an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to
720 * a {@code NodeTypeTemplate}.
721 *
722 * @return an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to
723 * a {@code NodeTypeTemplate}.
724 * @throws RepositoryException if another error occurs
725 */
726 public NodeDefinitionTemplate createNodeDefinitionTemplate() throws RepositoryException {
727 return new JcrNodeDefinitionTemplate(context());
728 }
729
730 /**
731 * Returns an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to
732 * a {@code NodeTypeTemplate}.
733 *
734 * @return an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to
735 * a {@code NodeTypeTemplate}.
736 * @throws RepositoryException if another error occurs
737 */
738 public PropertyDefinitionTemplate createPropertyDefinitionTemplate() throws RepositoryException {
739 return new JcrPropertyDefinitionTemplate(context());
740 }
741
742 /**
743 * Determine if any of the test type names are equal to or have been derived from the primary type or any of the mixins.
744 *
745 * @param testTypeNames the names of the types or mixins being tested against (never <code>null</code>)
746 * @param primaryTypeName the primary type name (never <code>null</code>)
747 * @param mixinNames the mixin names (may be <code>null</code>)
748 * @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
749 * @throws RepositoryException if there is an exception obtaining node types
750 * @throws IllegalArgumentException if <code>testTypeNames</code> is <code>null</code> or empty or if
751 * <code>primaryTypeName</code> is <code>null</code> or zero length
752 */
753 public boolean isDerivedFrom( String[] testTypeNames,
754 String primaryTypeName,
755 String[] mixinNames ) throws RepositoryException {
756 CheckArg.isNotEmpty(testTypeNames, "testTypeNames");
757 CheckArg.isNotEmpty(primaryTypeName, "primaryTypeName");
758
759 NameFactory nameFactory = context().getValueFactories().getNameFactory();
760 Name[] typeNames = nameFactory.create(testTypeNames);
761
762 // first check primary type
763 for (Name typeName : typeNames) {
764 JcrNodeType nodeType = getNodeType(typeName);
765
766 if ((nodeType != null) && nodeType.isNodeType(primaryTypeName)) {
767 return true;
768 }
769 }
770
771 // now check mixins
772 if (mixinNames != null) {
773 for (String mixin : mixinNames) {
774 for (Name typeName : typeNames) {
775 JcrNodeType nodeType = getNodeType(typeName);
776
777 if ((nodeType != null) && nodeType.isNodeType(mixin)) {
778 return true;
779 }
780 }
781 }
782 }
783
784 return false;
785 }
786 }