/*
* 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;
}
}