/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.jackrabbit.test.api.nodetype; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeIterator; import javax.jcr.nodetype.NodeTypeManager; /** * Tests if node definitions are respected in node instances in the workspace. * */ public class NodeDefTest extends AbstractJCRTest { /** * The session we use for the tests */ private Session session; /** * The node type manager of the session */ private NodeTypeManager manager; /** * If <code>true</code> indicates that the test found a mandatory node */ private boolean foundMandatoryNode = false; /** * Sets up the fixture for the test cases. */ protected void setUp() throws Exception { isReadOnly = true; super.setUp(); session = getHelper().getReadOnlySession(); manager = session.getWorkspace().getNodeTypeManager(); // re-fetch testRootNode with read-only session testRootNode = (Node) session.getItem(testRoot); } /** * Releases the session aquired in {@link #setUp()}. */ protected void tearDown() throws Exception { if (session != null) { session.logout(); session = null; } manager = null; super.tearDown(); } /** * Test getDeclaringNodeType() returns the node type which is defining the * requested child node def. Test runs for all existing node types. */ public void testGetDeclaringNodeType() throws RepositoryException { NodeTypeIterator types = manager.getAllNodeTypes(); // loop all node types while (types.hasNext()) { NodeType currentType = types.nextNodeType(); NodeDefinition defsOfCurrentType[] = currentType.getChildNodeDefinitions(); // loop all child node defs of each node type for (int i = 0; i < defsOfCurrentType.length; i++) { NodeDefinition def = defsOfCurrentType[i]; NodeType type = def.getDeclaringNodeType(); // check if def is part of the child node defs of the // declaring node type NodeDefinition defs[] = type.getChildNodeDefinitions(); boolean hasType = false; for (int j = 0; j < defs.length; j++) { if (defs[j].getName().equals(def.getName())) { hasType = true; break; } } assertTrue("getDeclaringNodeType() must return the node " + "which defines the corresponding child node def.", hasType); } } } /** * Tests if auto create nodes are not a residual set definition (getName() * does not return "*") */ public void testIsAutoCreate() throws RepositoryException { NodeTypeIterator types = manager.getAllNodeTypes(); // loop all node types while (types.hasNext()) { NodeType type = types.nextNodeType(); NodeDefinition defs[] = type.getChildNodeDefinitions(); for (int i = 0; i < defs.length; i++) { if (defs[i].isAutoCreated()) { assertFalse("An auto create node must not be a " + "residual set definition.", defs[i].getName().equals("*")); } } } } /** * This test checks if item definitions with mandatory constraints are * respected. * <p> * If the default workspace does not contain a node with a node type * definition that specifies a mandatory child node a {@link * org.apache.jackrabbit.test.NotExecutableException} is thrown. */ public void testIsMandatory() throws RepositoryException, NotExecutableException { traverse(testRootNode); if (!foundMandatoryNode) { throw new NotExecutableException("Workspace does not contain any node with a mandatory child node definition"); } } /** * Tests if getRequiredPrimaryTypes() does not return an empty array. Test * runs for all existing node types. */ public void testGetRequiredPrimaryTypes() throws RepositoryException { // loop all node types for (NodeTypeIterator types = manager.getAllNodeTypes(); types.hasNext(); ) { NodeType type = types.nextNodeType(); NodeDefinition defs[] = type.getChildNodeDefinitions(); for (int i = 0; i < defs.length; i++) { assertTrue("getRequiredPrimaryTypes() must never return an " + "empty array.", defs[i].getRequiredPrimaryTypes().length > 0); } } } /** * Tests that the information from getRequiredPrimaryTypeNames() * matches getRequiredPrimaryTypes(). * * @since JCR 2.0 */ public void testGetRequiredPrimaryTypeNames() throws RepositoryException { // loop all node types for (NodeTypeIterator types = manager.getAllNodeTypes(); types.hasNext(); ) { NodeType type = types.nextNodeType(); NodeDefinition defs[] = type.getChildNodeDefinitions(); for (int i = 0; i < defs.length; i++) { NodeType requiredPrimaryTypes[] = defs[i].getRequiredPrimaryTypes(); Set<String> rptnames = new HashSet<String>(); for (int j = 0; j < requiredPrimaryTypes.length; j++) { rptnames.add(requiredPrimaryTypes[j].getName()); } Set<String> rptnames2 = new HashSet<String>(Arrays.asList(defs[i].getRequiredPrimaryTypeNames())); assertEquals("names returned from getRequiredPrimaryTypeNames should match types returned from getRequiredPrimaryTypes", rptnames, rptnames2); } } } /** * Tests if the default primary type is of the same or a sub node type as the * the required primary types. Test runs for all existing node types. Also * tests the string based access ({@link NodeDefinition#getDefaultPrimaryTypeName()}. * * @since JCR 2.0 */ public void testGetDefaultPrimaryTypes() throws RepositoryException { // loop all node types for (NodeTypeIterator types = manager.getAllNodeTypes(); types.hasNext(); ) { NodeType type = types.nextNodeType(); NodeDefinition defs[] = type.getChildNodeDefinitions(); for (int i = 0; i < defs.length; i++) { NodeDefinition def = defs[i]; NodeType defaultType = def.getDefaultPrimaryType(); String defaultTypeName = def.getDefaultPrimaryTypeName(); if (defaultType != null) { NodeType requiredTypes[] = def.getRequiredPrimaryTypes(); for (int j = 0; j < requiredTypes.length; j++) { NodeType requiredType = requiredTypes[j]; boolean isSubType = compareWithRequiredType(requiredType, defaultType); assertTrue("The NodeType returned by " + "getDefaultPrimaryType or one of its " + "supertypes must match all NodeTypes " + "returned by getRequiredPrimaryTypes()", isSubType); } assertEquals("type names obtained from getDefaultPrimaryType and getDefaultPrimaryTypeName should match", defaultType.getName(), defaultTypeName); NodeType tmpType = manager.getNodeType(defaultTypeName); assertEquals(tmpType.getName(), defaultTypeName); } else { assertNull("getDefaultPrimaryTypeName should return null when getDefaultPrimaryType does", defaultTypeName); } } } } //-----------------------< internal >--------------------------------------- /** * Traverses the node hierarchy and applies * {@link #checkMandatoryConstraint(javax.jcr.Node, javax.jcr.nodetype.NodeType)} * to all descendant nodes of <code>parentNode</code>. */ private void traverse(Node parentNode) throws RepositoryException { NodeIterator nodes = parentNode.getNodes(); while (nodes.hasNext()) { Node node = nodes.nextNode(); NodeType primaryType = node.getPrimaryNodeType(); checkMandatoryConstraint(node, primaryType); NodeType mixins[] = node.getMixinNodeTypes(); for (int i = 0; i < mixins.length; i++) { checkMandatoryConstraint(node, mixins[i]); } traverse(node); } } /** * Checks if mandatory node definitions are respected. */ private void checkMandatoryConstraint(Node node, NodeType type) throws RepositoryException { // test if node contains all mandatory nodes of current type NodeDefinition nodeDefs[] = type.getChildNodeDefinitions(); for (int i = 0; i < nodeDefs.length; i++) { NodeDefinition nodeDef = nodeDefs[i]; if (nodeDef.isMandatory()) { foundMandatoryNode = true; try { node.getNode(nodeDef.getName()); } catch (PathNotFoundException e) { fail("Mandatory child " + nodeDef.getName() + " for " + node.getPath() + " does not exist."); } } } } /** * Returns true if defaultType or one of its supertypes is of the same * NodeType as requiredType. * * @param requiredType one of the required primary types of a NodeDef * @param defaultType the default primary type of a NodeDef */ private boolean compareWithRequiredType(NodeType requiredType, NodeType defaultType) { // if (defaultType == requiredType) return true; // rather use: if (defaultType.getName().equals(requiredType.getName())) { return true; } NodeType superTypes[] = defaultType.getSupertypes(); for (int i = 0; i < superTypes.length; i++) { // if (superTypes[i] == requiredType) return true; // rather use: if (superTypes[i].getName().equals(requiredType.getName())) { return true; } } return false; } }