/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.net.URL; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.jcr.AccessDeniedException; import javax.jcr.InvalidSerializedDataException; import javax.jcr.RepositoryException; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.Value; import javax.jcr.nodetype.InvalidNodeTypeDefinitionException; import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeDefinitionTemplate; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeDefinition; import javax.jcr.nodetype.NodeTypeIterator; import javax.jcr.nodetype.NodeTypeTemplate; import javax.jcr.nodetype.PropertyDefinition; import javax.jcr.nodetype.PropertyDefinitionTemplate; import org.modeshape.common.annotation.Immutable; import org.modeshape.common.collection.Problem; import org.modeshape.common.collection.Problems; import org.modeshape.common.collection.SimpleProblems; import org.modeshape.common.i18n.I18n; import org.modeshape.common.logging.Logger; import org.modeshape.common.util.CheckArg; import org.modeshape.common.util.IoUtil; import org.modeshape.jcr.JcrContentHandler.EnclosingSAXException; import org.modeshape.jcr.api.nodetype.NodeTypeManager; import org.modeshape.jcr.query.validate.Schemata; import org.modeshape.jcr.value.Name; import org.modeshape.jcr.value.NameFactory; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; /** * Local implementation of @{link NodeTypeManager}. This class handles translation between {@link Name}s and {@link String}s based * on the namespace registry from the session's execution context in order to support transient namespace remappings. All * {@link NodeType}s returned by this implementation are wrapped with the execution context of the session to allow proper ongoing * handling of names. This implies that reference equality is not a safe test for node type equivalence. * * @see RepositoryNodeTypeManager */ @Immutable public class JcrNodeTypeManager implements NodeTypeManager { private final JcrSession session; private final RepositoryNodeTypeManager repositoryTypeManager; private Schemata schemata; JcrNodeTypeManager( JcrSession session, RepositoryNodeTypeManager repositoryTypeManager ) { this.session = session; this.repositoryTypeManager = repositoryTypeManager; } private final ExecutionContext context() { return session.context(); } Schemata schemata() { if (schemata == null) { schemata = repositoryTypeManager.getRepositorySchemata().getSchemataForSession(session); assert schemata != null; } return schemata; } void signalNamespaceChanges() { this.schemata = null; } final NodeTypes nodeTypes() { return repositoryTypeManager.getNodeTypes(); } @Override public NodeTypeIterator getAllNodeTypes() throws RepositoryException { session.checkLive(); return new JcrNodeTypeIterator(nodeTypes().getAllNodeTypes()); } @Override public NodeTypeIterator getMixinNodeTypes() throws RepositoryException { session.checkLive(); Collection<JcrNodeType> rawTypes = nodeTypes().getMixinNodeTypes(); List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size()); // Need to return a version of the node type with the current context for (JcrNodeType type : rawTypes) { types.add(type.with(context(), session)); } return new JcrNodeTypeIterator(types); } @Override public JcrNodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException { session.checkLive(); Name ntName = context().getValueFactories().getNameFactory().create(nodeTypeName); JcrNodeType type = nodeTypes().getNodeType(ntName); if (type != null) { type = type.with(context(), session); return type; } throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName)); } /** * Returns the node type with the given name (if one exists) * * @param nodeTypeName the name of the node type to be returned * @return the node type with the given name (if one exists) * @see NodeTypes#getNodeType(Name) */ JcrNodeType getNodeType( Name nodeTypeName ) { JcrNodeType nodeType = nodeTypes().getNodeType(nodeTypeName); if (nodeType != null) { nodeType = nodeType.with(context(), session); } return nodeType; } /** * Returns true if and only if the node type with the given name exists. * <p> * This is equivalent to the following code: * * <pre> * try { * getNodeType(nodeTypeName); * return true; * } catch (NoSuchNodeTypeException nsnte) { * return false; * } * </pre> * * However, the implementation is more efficient that the approach listed above and does not rely upon exceptions. * </p> * * @param nodeTypeName the name of the node type * @return true if the named node type does exist, or false otherwise * @see NodeTypes#hasNodeType(Name) */ @Override public boolean hasNodeType( String nodeTypeName ) { Name ntName = context().getValueFactories().getNameFactory().create(nodeTypeName); return nodeTypes().hasNodeType(ntName); } @Override public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException { session.checkLive(); Collection<JcrNodeType> rawTypes = nodeTypes().getPrimaryNodeTypes(); List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size()); // Need to return a version of the node type with the current context for (JcrNodeType type : rawTypes) { types.add(type.with(context(), session)); } return new JcrNodeTypeIterator(types); } /** * Get the {@link NodeDefinition} for the root node. * * @return the definition; never null * @throws RepositoryException * @throws NoSuchNodeTypeException */ JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException { NodeTypes nodeTypes = nodeTypes(); for (NodeDefinition definition : nodeTypes.getNodeType(ModeShapeLexicon.ROOT).getChildNodeDefinitions()) { if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition; } assert false; // should not get here return null; } /** * Get the node definition given the supplied identifier. * * @param definitionId the identifier of the node definition * @return the node definition, or null if there is no such definition (or if the ID was null) */ JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) { if (definitionId == null) return null; return nodeTypes().getChildNodeDefinition(definitionId); } /** * Get the property definition given the supplied identifier. * * @param definitionId the identifier of the node definition * @return the property definition, or null if there is no such definition (or if the ID was null) */ JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId ) { if (definitionId == null) return null; return nodeTypes().getPropertyDefinition(definitionId); } /** * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the * given property name, property type, and value. * <p> * This method first attempts to find a single-valued property definition with the supplied property name and * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected. * The property definition is returned if it has a matching type (or has an {@link javax.jcr.PropertyType#UNDEFINED undefined * property type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints} . * Otherwise, the process continues with each of the mixin types, in the order they are named. * </p> * <p> * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property * type, and compatible constraints, starting with the primary type and continuing with each mixin type. * </p> * <p> * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the * definition's property type and still satisfy the definition's constraints. * </p> * <p> * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions. * </p> * <p> * If no matching property definition could be found (and the supplied property name is not the residual name), the whole * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*" * name}). * </p> * <p> * Finally, if no satisfactory property definition could be found, this method returns null. * </p> * * @param primaryTypeName the name of the primary type; may not be null * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically * look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best * residual property definition (if any). * @param value the value, or null if the property is being removed * @param checkMultiValuedDefinitions true if the type's multi-valued property definitions should be considered, or false if * only single-value property definitions should be considered * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if * this operation is being done from within internal implementations * @return the best property definition, or <code>null</code> if no property definition allows the property with the supplied * name, type and number of values */ final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName, Collection<Name> mixinTypeNames, Name propertyName, Value value, boolean checkMultiValuedDefinitions, boolean skipProtected ) { return nodeTypes().findPropertyDefinition(session, primaryTypeName, mixinTypeNames, propertyName, value, checkMultiValuedDefinitions, skipProtected); } final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName, Collection<Name> mixinTypeNames, Name propertyName, Value value, boolean checkMultiValuedDefinitions, boolean skipProtected, boolean checkTypesAndConstraints ) { return nodeTypes().findPropertyDefinition(session, primaryTypeName, mixinTypeNames, propertyName, value, checkMultiValuedDefinitions, skipProtected, checkTypesAndConstraints); } /** * Searches the supplied primary node type and the mixin node types for a property definition that is the best match for the * given property name, property type, and value. with the given name and property type that allows the supplied values. * <p> * This method first attempts to find a single-valued property definition with the supplied property name and * {@link Value#getType() value's property type} in the primary type, skipping any property definitions that are protected. * The property definition is returned if it has a matching type (or has an {@link javax.jcr.PropertyType#UNDEFINED undefined * property type}) and the value satisfies the {@link PropertyDefinition#getValueConstraints() definition's constraints} . * Otherwise, the process continues with each of the mixin types, in the order they are named. * </p> * <p> * If no matching property definition could be found (and <code>checkMultiValuedDefinitions</code> parameter is * <code>true</code>), the process is repeated except with multi-valued property definitions with the same name, property * type, and compatible constraints, starting with the primary type and continuing with each mixin type. * </p> * <p> * If no matching property definition could be found, and the process repeats by searching the primary type (and then mixin * types) for single-valued property definitions with a compatible type, where the values can be safely cast to the * definition's property type and still satisfy the definition's constraints. * </p> * <p> * If no matching property definition could be found, the previous step is repeated with multi-valued property definitions. * </p> * <p> * If no matching property definition could be found (and the supplied property name is not the residual name), the whole * process repeats for residual property definitions (e.g., those that are defined with a {@link JcrNodeType#RESIDUAL_NAME "*" * name}). * </p> * <p> * Finally, if no satisfactory property definition could be found, this method returns null. * </p> * * @param primaryTypeName the name of the primary type; may not be null * @param mixinTypeNames the names of the mixin types; may be null or empty if there are no mixins to include in the search * @param propertyName the name of the property for which the definition should be retrieved. This method will automatically * look for residual definitions, but you can use {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve only the best * residual property definition (if any). * @param values the values * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if * this operation is being done from within internal implementations * @return the best property definition, or <code>null</code> if no property definition allows the property with the supplied * name, type and number of values */ final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName, Collection<Name> mixinTypeNames, Name propertyName, Value[] values, boolean skipProtected ) { return nodeTypes().findPropertyDefinition(session, primaryTypeName, mixinTypeNames, propertyName, values, skipProtected); } final JcrPropertyDefinition findPropertyDefinition( Name primaryTypeName, Collection<Name> mixinTypeNames, Name propertyName, Value[] values, boolean skipProtected, boolean checkTypeAndConstraints ) { return nodeTypes().findPropertyDefinition(session, primaryTypeName, mixinTypeNames, propertyName, values, skipProtected, checkTypeAndConstraints); } /** * Determine if the property definitions of the supplied primary type and mixin types allow the property with the supplied * name to be removed. * * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins * to include in the search * @param propertyName the name of the property to be removed; may not be null * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if * this operation is being done from within internal implementations * @return true if at least one child node definition does not require children with the supplied name to exist, or false * otherwise */ boolean canRemoveProperty( Name primaryTypeNameOfParent, List<Name> mixinTypeNamesOfParent, Name propertyName, boolean skipProtected ) { return nodeTypes().canRemoveProperty(primaryTypeNameOfParent, mixinTypeNamesOfParent, propertyName, skipProtected); } /** * Determine if the child node definitions of the supplied primary type and mixin types of a parent node allow all of the * children with the supplied name to be removed. * * @param primaryTypeNameOfParent the name of the primary type for the parent node; may not be null * @param mixinTypeNamesOfParent the names of the mixin types for the parent node; may be null or empty if there are no mixins * to include in the search * @param childName the name of the child to be added to the parent; may not be null * @param skipProtected true if this operation is being done from within the public JCR node and property API, or false if * this operation is being done from within internal implementations * @return true if at least one child node definition does not require children with the supplied name to exist, or false * otherwise */ final boolean canRemoveAllChildren( Name primaryTypeNameOfParent, Collection<Name> mixinTypeNamesOfParent, Name childName, boolean skipProtected ) { return nodeTypes().canRemoveAllChildren(primaryTypeNameOfParent, mixinTypeNamesOfParent, childName, skipProtected); } /** * Registers a new node type or updates an existing node type using the specified definition and returns the resulting * {@link NodeType} object. * <p> * Typically, the object passed to this method will be a {@link NodeTypeTemplate} (a subclass of {@link NodeTypeDefinition}) * acquired from {@link JcrNodeTypeManager#createNodeTypeTemplate()} and then filled-in with definition information. * </p> * * @param template the new node type to register * @param allowUpdate this flag is not used * @return the {@code newly created node type} * @throws InvalidNodeTypeDefinitionException if the {@code NodeTypeDefinition} is invalid * @throws NodeTypeExistsException if {@code allowUpdate} is false and the {@code NodeTypeDefinition} specifies a node type * name that already exists * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if another error occurs */ @Override public NodeType registerNodeType( NodeTypeDefinition template, boolean allowUpdate ) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, AccessDeniedException, RepositoryException { session.checkLive(); try { session.checkWorkspacePermission(session.workspaceName(), ModeShapePermissions.REGISTER_TYPE); } catch (AccessControlException ace) { throw new AccessDeniedException(ace); } JcrNodeType jcrNodeType = this.repositoryTypeManager.registerNodeType(template, !allowUpdate); this.schemata = null; //clear the cached schemata to make sure it is reloaded return jcrNodeType; } /** * Registers or updates the specified Collection of {@code NodeTypeDefinition} objects. This method is used to register or * update a set of node types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects. * <p> * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated. * </p> * * @param templates the new node types to register * @param allowUpdates this flag is not used * @return the {@code newly created node types} * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection * specifies a node type name that already exists * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if another error occurs */ public NodeTypeIterator registerNodeTypes( Collection<NodeTypeDefinition> templates, boolean allowUpdates ) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, AccessDeniedException, RepositoryException { session.checkLive(); try { session.checkWorkspacePermission(session.workspaceName(), ModeShapePermissions.REGISTER_TYPE); } catch (AccessControlException ace) { throw new AccessDeniedException(ace); } List<JcrNodeType> jcrNodeTypes = repositoryTypeManager.registerNodeTypes(templates, !allowUpdates, false, true); this.schemata = null; //clear the cached schemata to make sure it is reloaded return new JcrNodeTypeIterator(jcrNodeTypes); } /** * Registers the node types from the given {@code JcrNodeTypeSource}. This method is used to register or update a set of node * types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects. * <p> * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated. * </p> * * @param nodeTypes the iterable object containing the new node types to register * @return the {@code newly created node types} * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection * specifies a node type name that already exists * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if another error occurs */ public NodeTypeIterator registerNodeTypes( Iterable<NodeTypeDefinition> nodeTypes ) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, AccessDeniedException, RepositoryException { try { session.checkWorkspacePermission(session.workspaceName(), ModeShapePermissions.REGISTER_TYPE); } catch (AccessControlException ace) { throw new AccessDeniedException(ace); } List<JcrNodeType> jcrNodeTypes = this.repositoryTypeManager.registerNodeTypes(nodeTypes); this.schemata = null; //clear the cached schemata to make sure it is reloaded return new JcrNodeTypeIterator(jcrNodeTypes); } /** * Registers or updates the specified array of {@code NodeTypeDefinition} objects. This method is used to register or update a * set of node types with mutual dependencies. Returns an iterator over the resulting {@code NodeType} objects. * <p> * The effect of the method is "all or nothing"; if an error occurs, no node types are registered or updated. * </p> * * @param ntds the new node types to register * @param allowUpdate must be {@code false}; ModeShape does not allow updating node types at this time * @return the {@code newly created node types} * @throws InvalidNodeTypeDefinitionException if a {@code NodeTypeDefinition} within the collection is invalid * @throws NodeTypeExistsException if {@code allowUpdate} is false and a {@code NodeTypeDefinition} within the collection * specifies a node type name that already exists * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if another error occurs */ @Override public NodeTypeIterator registerNodeTypes( NodeTypeDefinition[] ntds, boolean allowUpdate ) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, RepositoryException { try { session.checkWorkspacePermission(session.workspaceName(), ModeShapePermissions.REGISTER_TYPE); } catch (AccessControlException ace) { throw new AccessDeniedException(ace); } List<JcrNodeType> jcrNodeTypes = this.repositoryTypeManager.registerNodeTypes(Arrays.asList(ntds), !allowUpdate, false, true); this.schemata = null; //clear the cached schemata to make sure it is reloaded return new JcrNodeTypeIterator(jcrNodeTypes); } /** * Unregisters the named node type if it is not referenced by other node types as a supertype, a default primary type of a * child node (or nodes), or a required primary type of a child node (or nodes). * * @param nodeTypeName * @throws NoSuchNodeTypeException if node type name does not correspond to a registered node type * @throws InvalidNodeTypeDefinitionException if the node type with the given name cannot be unregistered because it is the * supertype, one of the required primary types, or a default primary type of another node type * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if any other error occurs */ @Override public void unregisterNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException { unregisterNodeTypes(Collections.singleton(nodeTypeName)); } /** * Allows the collection of node types to be unregistered if they are not referenced by other node types as supertypes, * default primary types of child nodes, or required primary types of child nodes. * * @param nodeTypeNames the names of the node types to be unregistered * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered node type * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot be unregistered because * they are the supertype, one of the required primary types, or a default primary type of a node type that is not * being unregistered. * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if any other error occurs */ public void unregisterNodeTypes( Collection<String> nodeTypeNames ) throws NoSuchNodeTypeException, InvalidNodeTypeDefinitionException, RepositoryException { NameFactory nameFactory = context().getValueFactories().getNameFactory(); try { session.checkWorkspacePermission(session.workspaceName(), ModeShapePermissions.REGISTER_TYPE); } catch (AccessControlException ace) { throw new AccessDeniedException(ace); } Collection<Name> names = new ArrayList<Name>(nodeTypeNames.size()); for (String name : nodeTypeNames) { names.add(nameFactory.create(name)); } // Unregister the node types, but perform a check to see if any of the node types are currently being used. // Unregistering a node type that is being used will likely cause the system to become unstable. boolean failIfNodeTypesAreUsed = true; repositoryTypeManager.unregisterNodeType(names, failIfNodeTypesAreUsed); this.schemata = null; //clear the cached schemata to make sure it is reloaded } /** * Allows the collection of node types to be unregistered if they are not referenced by other node types as supertypes, * default primary types of child nodes, or required primary types of child nodes. * * @param names the names of the node types to be unregistered * @throws NoSuchNodeTypeException if any of the node type names do not correspond to a registered node type * @throws InvalidNodeTypeDefinitionException if any of the node types with the given names cannot be unregistered because * they are the supertype, one of the required primary types, or a default primary type of a node type that is not * being unregistered. * @throws AccessDeniedException if the current session does not have the {@link ModeShapePermissions#REGISTER_TYPE register * type permission}. * @throws RepositoryException if any other error occurs */ @Override public void unregisterNodeTypes( String[] names ) throws NoSuchNodeTypeException, RepositoryException { unregisterNodeTypes(Arrays.asList(names)); } /** * Returns an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)} * * @return an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}. * @throws RepositoryException if another error occurs */ @Override public NodeTypeTemplate createNodeTypeTemplate() throws RepositoryException { return new JcrNodeTypeTemplate(context()); } /** * Returns a {@code NodeTypeTemplate} based on the definition given in {@code ntd}. This template can then be used to define a * node type and passed to {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)} * * @param ntd an existing node type definition; null values will be ignored * @return an empty {@code NodeTypeTemplate} which can then be used to define a node type and passed to * {@link JcrNodeTypeManager#registerNodeType(NodeTypeDefinition, boolean)}. * @throws RepositoryException if another error occurs */ @Override @SuppressWarnings( "unchecked" ) public NodeTypeTemplate createNodeTypeTemplate( NodeTypeDefinition ntd ) throws RepositoryException { NodeTypeTemplate ntt = new JcrNodeTypeTemplate(context(), true); if (ntd != null) { ntt.setName(ntd.getName()); ntt.setAbstract(ntd.isAbstract()); ntt.setDeclaredSuperTypeNames(ntd.getDeclaredSupertypeNames()); ntt.setMixin(ntd.isMixin()); ntt.setOrderableChildNodes(ntd.hasOrderableChildNodes()); ntt.setPrimaryItemName(ntd.getPrimaryItemName()); ntt.setQueryable(ntd.isQueryable()); // copy child nodes and props for (NodeDefinition nodeDefinition : ntd.getDeclaredChildNodeDefinitions()) { JcrNodeDefinitionTemplate ndt = new JcrNodeDefinitionTemplate(context()); ndt.setAutoCreated(nodeDefinition.isAutoCreated()); ndt.setDefaultPrimaryTypeName(nodeDefinition.getDefaultPrimaryTypeName()); ndt.setMandatory(nodeDefinition.isMandatory()); if (nodeDefinition.getName() != null) { ndt.setName(nodeDefinition.getName()); } ndt.setOnParentVersion(nodeDefinition.getOnParentVersion()); ndt.setProtected(nodeDefinition.isProtected()); ndt.setRequiredPrimaryTypeNames(nodeDefinition.getRequiredPrimaryTypeNames()); ndt.setSameNameSiblings(nodeDefinition.allowsSameNameSiblings()); ntt.getNodeDefinitionTemplates().add(ndt); } for (PropertyDefinition propertyDefinition : ntd.getDeclaredPropertyDefinitions()) { JcrPropertyDefinitionTemplate pdt = new JcrPropertyDefinitionTemplate(context()); pdt.setAutoCreated(propertyDefinition.isAutoCreated()); pdt.setAvailableQueryOperators(propertyDefinition.getAvailableQueryOperators()); pdt.setDefaultValues(propertyDefinition.getDefaultValues()); pdt.setFullTextSearchable(propertyDefinition.isFullTextSearchable()); pdt.setMandatory(propertyDefinition.isMandatory()); pdt.setMultiple(propertyDefinition.isMultiple()); if (propertyDefinition.getName() != null) { pdt.setName(propertyDefinition.getName()); } pdt.setOnParentVersion(propertyDefinition.getOnParentVersion()); pdt.setProtected(propertyDefinition.isProtected()); pdt.setQueryOrderable(propertyDefinition.isQueryOrderable()); pdt.setRequiredType(propertyDefinition.getRequiredType()); pdt.setValueConstraints(propertyDefinition.getValueConstraints()); ntt.getPropertyDefinitionTemplates().add(pdt); } } return ntt; } /** * Returns an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to * a {@code NodeTypeTemplate}. * * @return an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to * a {@code NodeTypeTemplate}. * @throws RepositoryException if another error occurs */ @Override public NodeDefinitionTemplate createNodeDefinitionTemplate() throws RepositoryException { return new JcrNodeDefinitionTemplate(context()); } /** * Returns an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to * a {@code NodeTypeTemplate}. * * @return an empty {@code PropertyDefinitionTemplate} which can then be used to create a property definition and attached to * a {@code NodeTypeTemplate}. * @throws RepositoryException if another error occurs */ @Override public PropertyDefinitionTemplate createPropertyDefinitionTemplate() throws RepositoryException { return new JcrPropertyDefinitionTemplate(context()); } /** * Determine if any of the test type names are equal to or have been derived from the primary type or any of the mixins. * * @param testTypeNames the names of the types or mixins being tested against (never <code>null</code>) * @param primaryTypeName the primary type name (never <code>null</code>) * @param mixinNames the mixin names (may be <code>null</code>) * @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 * @throws RepositoryException if there is an exception obtaining node types * @throws IllegalArgumentException if <code>testTypeNames</code> is <code>null</code> or empty or if * <code>primaryTypeName</code> is <code>null</code> or zero length */ public boolean isDerivedFrom( String[] testTypeNames, String primaryTypeName, String[] mixinNames ) throws RepositoryException { CheckArg.isNotEmpty(testTypeNames, "testTypeNames"); CheckArg.isNotEmpty(primaryTypeName, "primaryTypeName"); NameFactory nameFactory = context().getValueFactories().getNameFactory(); Name[] typeNames = nameFactory.create(testTypeNames); // first check primary type for (Name typeName : typeNames) { JcrNodeType nodeType = getNodeType(typeName); if ((nodeType != null) && nodeType.isNodeType(primaryTypeName)) { return true; } } // now check mixins if (mixinNames != null) { for (String mixin : mixinNames) { for (Name typeName : typeNames) { JcrNodeType nodeType = getNodeType(typeName); if ((nodeType != null) && nodeType.isNodeType(mixin)) { return true; } } } } return false; } protected String messageFrom( Problems problems ) { StringBuilder sb = new StringBuilder(); for (Problem problem : problems) { sb.append('\n').append(problem.getMessageString()); } return sb.toString(); } protected List<NodeTypeDefinition> importFromXml( InputSource source ) throws RepositoryException { XmlNodeTypeReader handler = new XmlNodeTypeReader(session); try { XMLReader parser = XMLReaderFactory.createXMLReader(); parser.setContentHandler(handler); parser.parse(source); } catch (EnclosingSAXException ese) { Exception cause = ese.getException(); if (cause instanceof RepositoryException) { throw (RepositoryException)cause; } throw new RepositoryException(cause); } catch (SAXParseException se) { throw new InvalidSerializedDataException(se); } catch (SAXException se) { throw new RepositoryException(se); } catch (IOException ioe) { throw new RepositoryException(ioe); } catch (RuntimeException t) { throw t; } catch (Throwable t) { throw new RepositoryException(t); } return handler.getNodeTypeDefinitions(); } @Override public NodeTypeIterator registerNodeTypes( File file, boolean allowUpdate ) throws IOException, RepositoryException { String content = IoUtil.read(file); if (content.startsWith("<?xml")) { // This is Jackrabbit XML format ... return registerNodeTypes(importFromXml(new InputSource(new FileInputStream(file))), allowUpdate); } // Assume this is CND format ... CndImporter importer = new CndImporter(context()); Problems problems = new SimpleProblems(); importer.importFrom(content, problems, file.getAbsolutePath()); // Check for (and report) any problems ... if (problems.hasProblems()) { // There are errors and/or warnings, so report them ... String summary = messageFrom(problems); if (problems.hasErrors()) { String msg = JcrI18n.errorsParsingNodeTypeDefinitions.text(file.getAbsolutePath(), summary); throw new RepositoryException(msg); } // Otherwise, there are warnings, so log them ... I18n msg = JcrI18n.warningsParsingNodeTypeDefinitions; Logger.getLogger(getClass()).warn(msg, file.getAbsolutePath(), summary); } // Register the node types ... return registerNodeTypes(importer.getNodeTypeDefinitions(), allowUpdate); } @Override public NodeTypeIterator registerNodeTypes( InputStream stream, boolean allowUpdate ) throws IOException, javax.jcr.nodetype.InvalidNodeTypeDefinitionException, javax.jcr.nodetype.NodeTypeExistsException, UnsupportedRepositoryOperationException, RepositoryException { String content = IoUtil.read(stream); if (content.startsWith("<?xml")) { // This is Jackrabbit XML format ... return registerNodeTypes(importFromXml(new InputSource(new StringReader(content))), allowUpdate); } // Assume this is CND format ... CndImporter importer = new CndImporter(context()); Problems problems = new SimpleProblems(); importer.importFrom(content, problems, "stream"); // Check for (and report) any problems ... if (problems.hasProblems()) { // There are errors and/or warnings, so report them ... String summary = messageFrom(problems); if (problems.hasErrors()) { String msg = JcrI18n.errorsParsingStreamOfNodeTypeDefinitions.text(summary); throw new RepositoryException(msg); } // Otherwise, there are warnings, so log them ... I18n msg = JcrI18n.warningsParsingStreamOfNodeTypeDefinitions; Logger.getLogger(getClass()).warn(msg, summary); } // Register the node types ... return registerNodeTypes(importer.getNodeTypeDefinitions(), allowUpdate); } @Override public NodeTypeIterator registerNodeTypes( URL url, boolean allowUpdate ) throws IOException, RepositoryException { String content = IoUtil.read(url.openStream()); if (content.startsWith("<?xml")) { // This is Jackrabbit XML format ... return registerNodeTypes(importFromXml(new InputSource(new StringReader(content))), allowUpdate); } // Assume this is CND format ... CndImporter importer = new CndImporter(context()); Problems problems = new SimpleProblems(); importer.importFrom(content, problems, url.toExternalForm()); // Check for (and report) any problems ... if (problems.hasProblems()) { // There are errors and/or warnings, so report them ... String summary = messageFrom(problems); if (problems.hasErrors()) { String msg = JcrI18n.errorsParsingNodeTypeDefinitions.text(url.toExternalForm(), summary); throw new RepositoryException(msg); } // Otherwise, there are warnings, so log them ... I18n msg = JcrI18n.warningsParsingNodeTypeDefinitions; Logger.getLogger(getClass()).warn(msg, url.toExternalForm(), summary); } // Register the node types ... return registerNodeTypes(importer.getNodeTypeDefinitions(), allowUpdate); } }