/* * 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.Iterator; import java.util.Set; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.NodeIterator; 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; import javax.jcr.nodetype.PropertyDefinition; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Tests if the node type hierarchy is correctly mapped to the methods * defined in {@link NodeType}. * */ public class NodeTypeTest extends AbstractJCRTest { private static Logger log = LoggerFactory.getLogger(NodeTypeTest.class); /** * The session we use for the tests */ private Session session; /** * The node type manager of the session */ private NodeTypeManager manager; /** * The root node of the default workspace */ private Node rootNode; /** * Sets up the fixture for the test cases. */ protected void setUp() throws Exception { isReadOnly = true; super.setUp(); session = getHelper().getReadOnlySession(); manager = session.getWorkspace().getNodeTypeManager(); rootNode = session.getRootNode(); } /** * Releases the session aquired in {@link #setUp()}. */ protected void tearDown() throws Exception { if (session != null) { session.logout(); session = null; } manager = null; rootNode = null; super.tearDown(); } /** * Test if getNode() returns the name of a node type. */ public void testGetName() throws RepositoryException { NodeType type = manager.getNodeType(ntBase); assertEquals("getName() must return the name of the node", ntBase, type.getName()); } /** * Test if isMixin() returns false if applied on a primary node type and true * on a mixin node type. */ public void testIsMixin() throws RepositoryException { NodeTypeIterator primaryTypes = manager.getPrimaryNodeTypes(); assertFalse("testIsMixin() must return false if applied on a " + "primary node type", primaryTypes.nextNodeType().isMixin()); // if a mixin node type exist, test if isMixin() returns true NodeTypeIterator mixinTypes = manager.getMixinNodeTypes(); if (getSize(mixinTypes) > 0) { // need to re-aquire iterator {@link #getSize} may consume iterator mixinTypes = manager.getMixinNodeTypes(); assertTrue("testIsMixin() must return true if applied on a " + "mixin node type", mixinTypes.nextNodeType().isMixin()); } // else skip the test for mixin node types } /** * Test if node.getPrimaryItemName() returns the same name as * node.getPrimaryItem().getName() */ public void testGetPrimaryItemName() throws NotExecutableException, RepositoryException { Node node = locateNodeWithPrimaryItem(rootNode); if (node == null) { throw new NotExecutableException("Workspace does not contain a node with primary item defined"); } String name = node.getPrimaryItem().getName(); NodeType type = node.getPrimaryNodeType(); assertEquals("node.getPrimaryNodeType().getPrimaryItemName() " + "must return the same name as " + "node.getPrimaryItem().getName()", name, type.getPrimaryItemName()); } /** * Test if node.getPrimaryItemName() returns null if no primary item is * defined */ public void testGetPrimaryItemNameNotExisting() throws NotExecutableException, RepositoryException { Node node = locateNodeWithoutPrimaryItem(rootNode); if (node == null) { throw new NotExecutableException("Workspace does not contain a node without primary item defined"); } NodeType type = node.getPrimaryNodeType(); assertNull("getPrimaryItemName() must return null if NodeType " + "does not define a primary item", type.getPrimaryItemName()); } /** * Test if getSupertypes() of a primary node that is not "nt:base" returns at * least "nt:base". NotExecutableException is thrown if no primary node type * apart from "nt:base". */ public void testGetSupertypes() throws NotExecutableException, RepositoryException { // find a primary node type but not "nt:base" NodeTypeIterator types = manager.getPrimaryNodeTypes(); NodeType type = null; while (types.hasNext()) { type = types.nextNodeType(); if (!type.getName().equals(ntBase)) { break; } } // note: type is never null, since at least "nt:base" must exist if (type.getName().equals("nt:base")) { throw new NotExecutableException("Workspace does not have sufficient primary node types to run " + "this test. At least nt:base plus anther type are required."); } NodeType supertypes[] = type.getSupertypes(); boolean hasNTBase = false; for (int i = 0; i < supertypes.length; i++) { if (supertypes[i].getName().equals(ntBase)) { hasNTBase = true; break; } } assertTrue("getSupertypes() of a primary node type that is not " + "\"nt:base\" must at least return \"nt:base\"", hasNTBase); } /** * Test if all node types returned by getDeclaredSupertypes() are also * returned by getSupertypes(). All existing node types are tested. */ public void testGetDeclaredSupertypes() throws RepositoryException { for (NodeTypeIterator types = manager.getAllNodeTypes(); types.hasNext(); ) { NodeType type = types.nextNodeType(); Set<String> declaredSupertypeNames = asSetOfNames(type.getDeclaredSupertypes()); Set<String> supertypeNames = asSetOfNames(type.getSupertypes()); assertTrue("all declared supertypes must be supertypes: " + (new HashSet<String>(declaredSupertypeNames).removeAll(supertypeNames)), supertypeNames.containsAll(declaredSupertypeNames)); assertEquals("getDeclaredSuperTypes and getDeclaredSuperTypeNames must be consistent", declaredSupertypeNames, new HashSet<String>(Arrays.asList(type.getDeclaredSupertypeNames()))); } } /** * Test if all node types returned by getDeclaredSubtypes() are also * returned by getSubtypes(), and that the information is consistent * with getSuperTypes/getDeclaredSuperTypes. All existing node types are tested. * * @since JCR 2.0 */ public void testGetDeclaredSubtypes() throws RepositoryException { for (NodeTypeIterator types = manager.getAllNodeTypes(); types.hasNext(); ) { NodeType type = types.nextNodeType(); String name = type.getName(); Set<String> declaredSubtypeNames = asSetOfNames(type.getDeclaredSubtypes()); Set<String> subtypeNames = asSetOfNames(type.getSubtypes()); assertTrue("all declared subtypes must be subtypes: " + (new HashSet<String>(declaredSubtypeNames).removeAll(subtypeNames)), subtypeNames.containsAll(declaredSubtypeNames)); // check the reverse relation for (Iterator<String> it = subtypeNames.iterator(); it.hasNext(); ) { String subtypename = it.next(); boolean isDeclared = declaredSubtypeNames.contains(subtypename); NodeType subtype = manager.getNodeType(subtypename); Set<String> supertypeNames = asSetOfNames(subtype.getSupertypes()); assertTrue(name + " should occur in set of super types: " + supertypeNames, supertypeNames.contains(name)); if (isDeclared) { Set<String> declaredSupertypeNames = asSetOfNames(subtype.getDeclaredSupertypes()); assertTrue(name + " should occur in set of declared super types: " + declaredSupertypeNames, declaredSupertypeNames.contains(name)); } } } } /** * Test if isNodeType(String nodeTypeName) returns true if nodeTypeName is * the name of the node itself. Also, primary node types must return true if * nodeTypeName is "nt:base", and mixin node types must return false in that * case. */ public void testIsNodeType() throws RepositoryException { // find a primary node type but not "nt:base" NodeTypeIterator types = manager.getPrimaryNodeTypes(); while (types.hasNext()) { NodeType type = types.nextNodeType(); assertTrue("isNodeType(String nodeTypeName) must return true if " + "NodeType is nodeTypeName", type.isNodeType(type.getName())); if (type.isMixin()) { assertFalse("isNodeType(String nodeTypeName) must return " + "false if NodeType is not a subtype of " + "nodeTypeName", type.isNodeType(ntBase)); } else { assertTrue("isNodeType(String nodeTypeName) must return true if " + "NodeType is a subtype of nodeTypeName", type.isNodeType(ntBase)); } } } /** * Like {@link #testIsNodeType()}, but using qualified names */ public void testIsNodeTypeQName() throws RepositoryException { // find a primary node type but not "nt:base" NodeTypeIterator types = manager.getPrimaryNodeTypes(); while (types.hasNext()) { NodeType type = types.nextNodeType(); String typename = type.getName(); String ns = session.getNamespaceURI(AbstractJCRTest.getPrefix(typename)); if (ns.length() != 0 && !ns.contains(":")) { log.warn("Node type '" + typename + "' has invalid namespace '" + ns + "', thus skipping testIsNodeTypeQName() for this type"); } else { String qn = AbstractJCRTest.getQualifiedName(session, typename); assertTrue("isNodeType(String nodeTypeName) must return true if " + "NodeType is nodeTypeName", type.isNodeType(qn)); } if (type.isMixin()) { assertFalse("isNodeType(String nodeTypeName) must return " + "false if NodeType is not a subtype of " + "nodeTypeName", type.isNodeType(NodeType.NT_BASE)); } else { assertTrue("isNodeType(String nodeTypeName) must return true if " + "NodeType is a subtype of nodeTypeName", type.isNodeType(NodeType.NT_BASE)); } } } /** * Test if all property defs returned by getDeclatedPropertyDefs() are also * returned by getPropertyDefs(). All existing node types are tested. */ public void testGetDeclaredPropertyDefs() throws RepositoryException { NodeTypeIterator types = manager.getAllNodeTypes(); while (types.hasNext()) { NodeType type = types.nextNodeType(); PropertyDefinition declaredDefs[] = type.getDeclaredPropertyDefinitions(); PropertyDefinition defs[] = type.getPropertyDefinitions(); try { for (int i = 0; i < declaredDefs.length; i++) { boolean exists = false; for (int j = 0; j < defs.length; j++) { if (defs[j].getName().equals(declaredDefs[i].getName())) { exists = true; break; } } assertTrue("All property defs returned by " + "getDeclaredPropertyDefs() must also be " + "returned by getPropertyDefs()", exists); } } catch (ArrayIndexOutOfBoundsException e) { fail("The array returned by " + "getDeclaredPropertyDefs() must not exceed " + "the one returned by getPropertyDefs()"); } } } /** * Test if getPropertyDefs() of a primary node returns also "jcr:primaryType" * which is inherited from "nt:base". */ public void testGetPropertyDefs() throws NotExecutableException, RepositoryException { // find a primary node type but not "nt:base" NodeTypeIterator types = manager.getPrimaryNodeTypes(); while (types.hasNext()) { NodeType type = types.nextNodeType(); PropertyDefinition defs[] = type.getPropertyDefinitions(); boolean hasJCRPrimaryType = false; for (int i = 0; i < defs.length; i++) { if (defs[i].getName().equals(jcrPrimaryType)) { hasJCRPrimaryType = true; break; } } assertTrue("getPropertyDefs() of a primary node type " + "must return also \"jcr:primaryType\".", hasJCRPrimaryType); } } /** * Test if all node defs returned by getDeclaredChildNodeDefs() are also * returned by getChildNodeDefs(). All existing node types are tested. */ public void testGetDeclaredChildNodeDefs() throws RepositoryException { NodeTypeIterator types = manager.getAllNodeTypes(); while (types.hasNext()) { NodeType type = types.nextNodeType(); NodeDefinition declaredDefs[] = type.getDeclaredChildNodeDefinitions(); NodeDefinition defs[] = type.getChildNodeDefinitions(); try { for (int i = 0; i < declaredDefs.length; i++) { boolean exists = false; for (int j = 0; j < defs.length; j++) { if (defs[j].getName().equals(declaredDefs[i].getName())) { exists = true; break; } } assertTrue("All node defs returned by " + "getDeclaredChildNodeDefs() must also be " + "returned by getChildNodeDefs().", exists); } } catch (ArrayIndexOutOfBoundsException e) { fail("The array returned by " + "getDeclaredChildNodeDefs() must not exceed " + "the one returned by getChildNodeDefs()"); } } } //-----------------------< internal >--------------------------------------- /** * Returns the first descendant of <code>node</code> which defines primary * item * * @param node <code>Node</code> to start traversal. * @return first node with primary item */ private Node locateNodeWithPrimaryItem(Node node) throws RepositoryException { try { node.getPrimaryItem(); return node; } catch (ItemNotFoundException e) { } Node skippedFolder = null; NodeIterator nodes = node.getNodes(); while (nodes.hasNext()) { Node testNode = nodes.nextNode(); if (testNode.getPath().equals("/jcr:system")) { skippedFolder = testNode; } else { Node returnedNode = locateNodeWithPrimaryItem(testNode); if (returnedNode != null) { return returnedNode; } } } // check jcr:system if we skipped it before if (skippedFolder != null) { Node returnedNode = locateNodeWithPrimaryItem(skippedFolder); if (returnedNode != null) { return returnedNode; } } return null; } /** * Returns the first descendant of <code>node</code> without a primary item * * @param node * @return first node without primary item */ private Node locateNodeWithoutPrimaryItem(Node node) throws RepositoryException { try { node.getPrimaryItem(); } catch (ItemNotFoundException e) { return node; } NodeIterator nodes = node.getNodes(); while (nodes.hasNext()) { Node returnedNode = this.locateNodeWithoutPrimaryItem(nodes.nextNode()); if (returnedNode != null) { return returnedNode; } } return null; } /** * Return the set of node type names for the specified node types. */ private Set<String> asSetOfNames(NodeType[] types) { Set<String> result = new HashSet<String>(); for (int i = 0; i < types.length; i++) { result.add(types[i].getName()); } return result; } /** * Return the set of node type names for the specified node types. */ private Set<String> asSetOfNames(NodeTypeIterator it) { Set<String> result = new HashSet<String>(); while (it.hasNext()) { result.add(it.nextNodeType().getName()); } return result; } }