/*
* 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.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jcr.PropertyType;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.jcr.nodetype.PropertyDefinition;
import org.modeshape.common.annotation.ThreadSafe;
import org.modeshape.common.util.CheckArg;
import org.modeshape.jcr.NodeTypes.NodeDefinitionSet;
import org.modeshape.jcr.api.Namespaced;
import org.modeshape.jcr.cache.NodeKey;
import org.modeshape.jcr.cache.SiblingCounter;
import org.modeshape.jcr.value.Name;
import org.modeshape.jcr.value.basic.BasicName;
/**
* ModeShape implementation of JCR {@link NodeType}s.
*/
@ThreadSafe
class JcrNodeType implements NodeType, Namespaced {
public static final String RESIDUAL_ITEM_NAME = "*";
public static final Name RESIDUAL_NAME = new BasicName("", RESIDUAL_ITEM_NAME);
private final NodeKey key;
/** The name of the node type (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code>) */
private final Name name;
/** The name of the node's primary item */
private final Name primaryItemName;
/** The supertypes for this node. */
private final List<JcrNodeType> declaredSupertypes;
/**
* The list of all supertypes for this node, beginning with the immediate supertypes, followed by the supertypes of those
* supertypes, etc.
*/
private final List<JcrNodeType> allSupertypes;
/**
* The list of this type and all supertypes for this node, beginning with this type, continuing with the immediate supertypes,
* followed by the supertypes of those supertypes, etc.
*/
private final List<JcrNodeType> thisAndAllSupertypes;
private final Set<Name> thisAndAllSupertypesNames;
/** Indicates whether this node type is a mixin type (as opposed to a primary type). */
private final boolean mixin;
/** Indicates whether the child nodes of nodes of this type can be ordered. */
private final boolean orderableChildNodes;
/** Indicates whether this node type is abstract */
private final boolean isAbstract;
/** Indicates whether this node is queryable (i.e., should be included in query results) */
private final boolean queryable;
/**
* The child node definitions that are defined on this node type.
*/
private final List<JcrNodeDefinition> childNodeDefinitions;
/**
* The property definitions that are defined on this node type.
*/
private final List<JcrPropertyDefinition> propertyDefinitions;
/**
* A local cache of all defined and inherited child node definitions and property definitions. Residual definitions are
* included. This class's methods to find a property definition and find child node definitions, and since they're frequently
* used by SessionCache this cache provides very quick access.
*/
private final DefinitionCache allDefinitions;
/**
* A reference to the execution context in which this node type exists, used to remap the internal names to their appropriate
* prefixed version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>"nt:base"</code>.).
*/
private ExecutionContext context;
/**
* A reference to a user session, in the context of which the node type is created. May be null during system initialization.
*/
private JcrSession session;
/** Link to the repository node type manager for the repository to which this node type belongs. */
private RepositoryNodeTypeManager nodeTypeManager;
JcrNodeType( NodeKey prototypeKey,
ExecutionContext context,
JcrSession session,
RepositoryNodeTypeManager nodeTypeManager,
Name name,
List<JcrNodeType> declaredSupertypes,
Name primaryItemName,
Collection<JcrNodeDefinition> childNodeDefinitions,
Collection<JcrPropertyDefinition> propertyDefinitions,
boolean mixin,
boolean isAbstract,
boolean queryable,
boolean orderableChildNodes ) {
assert context != null;
this.context = context;
this.session = session;
this.nodeTypeManager = nodeTypeManager;
this.name = name;
this.primaryItemName = primaryItemName;
this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<JcrNodeType>emptyList();
this.mixin = mixin;
this.queryable = queryable;
this.isAbstract = isAbstract;
this.orderableChildNodes = orderableChildNodes;
this.propertyDefinitions = new ArrayList<JcrPropertyDefinition>(propertyDefinitions.size());
for (JcrPropertyDefinition property : propertyDefinitions) {
this.propertyDefinitions.add(property.with(this));
}
this.childNodeDefinitions = new ArrayList<JcrNodeDefinition>(childNodeDefinitions.size());
for (JcrNodeDefinition childNode : childNodeDefinitions) {
this.childNodeDefinitions.add(childNode.with(this));
}
// Build the list of all types, including supertypes ...
List<JcrNodeType> thisAndAllSupertypes = new LinkedList<JcrNodeType>();
Set<Name> typeNames = new HashSet<Name>();
thisAndAllSupertypes.add(this);
typeNames.add(this.name);
for (int i = 0; i != thisAndAllSupertypes.size(); ++i) {
JcrNodeType superType = thisAndAllSupertypes.get(i);
for (NodeType superSuperType : superType.getDeclaredSupertypes()) {
JcrNodeType jcrSuperSuperType = (JcrNodeType)superSuperType;
if (jcrSuperSuperType == null) {
assert JcrNtLexicon.BASE.equals(name);
continue;
}
if (typeNames.add(jcrSuperSuperType.getInternalName())) {
thisAndAllSupertypes.add(jcrSuperSuperType);
}
}
}
this.thisAndAllSupertypes = Collections.unmodifiableList(thisAndAllSupertypes);
// Make the list of all supertypes to be a sublist of the first ...
this.allSupertypes = thisAndAllSupertypes.size() > 1 ? thisAndAllSupertypes.subList(1, thisAndAllSupertypes.size()) : Collections.<JcrNodeType>emptyList();
// Set up the set of all supertype names ...
this.thisAndAllSupertypesNames = Collections.unmodifiableSet(typeNames);
this.allDefinitions = new DefinitionCache(this);
this.key = prototypeKey.withId("/jcr:system/jcr:nodeTypes/" + name.getString());
}
final NodeKey key() {
return key;
}
List<JcrNodeType> getTypeAndSupertypes() {
return thisAndAllSupertypes;
}
List<JcrNodeType> supertypes() {
return allSupertypes;
}
/**
* Get the child definitions defined on this node type (excluding inherited definitions).
*
* @return this node's child node definitions; never null
*/
List<JcrNodeDefinition> childNodeDefinitions() {
return childNodeDefinitions;
}
/**
* Determine if there is at least one child node definition.
*
* @return true if there is at least one child node definitions, or false if there are none
*/
boolean hasChildNodeDefinitions() {
return allDefinitions.anyChildNodeDefinitions();
}
/**
* Get the property definitions defined on this node type (excluding inherited definitions).
*
* @return this node's property definitions; never null
*/
List<JcrPropertyDefinition> propertyDefinitions() {
return propertyDefinitions;
}
/**
* Determine if there is at least one property definition.
*
* @return true if there is at least one child node definitions, or false if there are none
*/
boolean hasPropertyDefinitions() {
return allDefinitions.anyPropertyDefinitions();
}
/**
* Get all of the property definitions defined on this node type and its supertypes.
*
* @return this node's explicit and inherited property definitions; never null
*/
Collection<JcrPropertyDefinition> allPropertyDefinitions() {
return allDefinitions.allPropertyDefinitions();
}
Collection<JcrPropertyDefinition> allSingleValuePropertyDefinitions( Name propertyName ) {
return allDefinitions.allSingleValuePropertyDefinitions(propertyName);
}
Collection<JcrPropertyDefinition> allMultiValuePropertyDefinitions( Name propertyName ) {
return allDefinitions.allMultiValuePropertyDefinitions(propertyName);
}
Collection<JcrPropertyDefinition> allPropertyDefinitions( Name propertyName ) {
return allDefinitions.allPropertyDefinitions(propertyName);
}
/**
* Get all of the child node definitions defined on this node type and its supertypes.
*
* @return this node's explicit and inherited child node definitions; never null
*/
Collection<JcrNodeDefinition> allChildNodeDefinitions() {
return allDefinitions.allChildNodeDefinitions();
}
Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
boolean requireSns ) {
return allDefinitions.allChildNodeDefinitions(childName, requireSns);
}
Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName ) {
return allDefinitions.allChildNodeDefinitions(childName);
}
JcrNodeDefinition childNodeDefinition( NodeDefinitionId nodeDefnId ) {
List<Name> requiredPrimaryTypeNames = Arrays.asList(nodeDefnId.getRequiredPrimaryTypes());
for (JcrNodeDefinition nodeDefn : allChildNodeDefinitions(nodeDefnId.getChildDefinitionName())) {
if (nodeDefn.requiredPrimaryTypeNameSet().size() == requiredPrimaryTypeNames.size()
&& nodeDefn.requiredPrimaryTypeNameSet().containsAll(requiredPrimaryTypeNames)) {
return nodeDefn;
}
}
return null;
}
@Override
public boolean canAddChildNode( String childNodeName ) {
CheckArg.isNotNull(childNodeName, "childNodeName");
Name childName = context.getValueFactories().getNameFactory().create(childNodeName);
boolean skipProtected = true;
NodeDefinitionSet childDefns = nodeTypes().findChildNodeDefinitions(this.name, null);
JcrNodeDefinition childNodeDefinition = childDefns.findBestDefinitionForChild(childName, null, skipProtected,
SiblingCounter.constant(0));
// No primary node type was given, which means the child node definition (if found) must have a default type ...
return childNodeDefinition != null && childNodeDefinition.getDefaultPrimaryType() != null;
}
@Override
public boolean canAddChildNode( String childNodeName,
String primaryNodeTypeName ) {
CheckArg.isNotNull(childNodeName, "childNodeName");
CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
Name childName = context.getValueFactories().getNameFactory().create(childNodeName);
Name childPrimaryTypeName = context.getValueFactories().getNameFactory().create(primaryNodeTypeName);
NodeTypes nodeTypes = nodeTypes();
boolean skipProtected = true;
NodeDefinitionSet childDefns = nodeTypes.findChildNodeDefinitions(this.name, null);
JcrNodeDefinition childNodeDefinition = childDefns.findBestDefinitionForChild(childName, childPrimaryTypeName,
skipProtected, SiblingCounter.constant(0));
if (childNodeDefinition == null) {
// We found no valid child node definition ...
return false;
}
if (RESIDUAL_ITEM_NAME.equals(childNodeDefinition.getName())) {
// the TCK expects that for residual children definitions, this returns true
return true;
}
if (childPrimaryTypeName != null) {
// The child's primary node type name was given, so make sure that it is not abstract or a mixin.
// If it were not for the residual item check (previous `if`), then we could do this at the top of the method.
// But it has to go here.
JcrNodeType childType = nodeTypes.getNodeType(childPrimaryTypeName);
if (childType.isAbstract() || childType.isMixin()) return false;
}
return true;
}
@Override
public boolean canRemoveNode( String itemName ) {
CheckArg.isNotNull(itemName, "itemName");
Name childName = context.getValueFactories().getNameFactory().create(itemName);
return nodeTypes().canRemoveAllChildren(this.name, null, childName, true);
}
/**
* {@inheritDoc}
* <p>
* In JCR 1.0, this method applied to all children. However, this was changed in the JSR-283 specification to apply only to
* nodes, and it is also deprecated.
* </p>
*
* @see javax.jcr.nodetype.NodeType#canRemoveItem(java.lang.String)
*/
@Override
@SuppressWarnings("deprecation")
public boolean canRemoveItem( String itemName ) {
CheckArg.isNotNull(itemName, "itemName");
Name childName = context.getValueFactories().getNameFactory().create(itemName);
return nodeTypes().canRemoveItem(this.name, null, childName, true);
}
/**
* Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
* conversion rules in section 3.6.4 of the JCR 2.0 specification AND <code>value</code> satisfies the constraints (if any)
* for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
* will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
* type specified in <code>value.getType()</code>.
*
* @param session the session in which the constraints are to be checked; may not be null
* @param propertyDefinition the property definition to validate against
* @param value the value to be validated
* @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
* satisfies the constraints for the property (if any exist).
* @see PropertyDefinition#getValueConstraints()
* @see JcrPropertyDefinition#satisfiesConstraints(Value,JcrSession)
*/
boolean canCastToTypeAndMatchesConstraints( JcrSession session,
JcrPropertyDefinition propertyDefinition,
Value value ) {
try {
assert value instanceof JcrValue : "Illegal implementation of Value interface";
((JcrValue)value).asType(propertyDefinition.getRequiredType()); // throws ValueFormatException if there's a problem
return propertyDefinition.satisfiesConstraints(value, session);
} catch (javax.jcr.ValueFormatException vfe) {
// Cast failed
return false;
}
}
/**
* Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
* conversion rules in section 3.6.4 of the JCR 2.0 specification AND <code>value</code> satisfies the constraints (if any)
* for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
* will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
* type specified in <code>value.getType()</code>.
*
* @param session the session in which the constraints are to be checked; may not be null
* @param propertyDefinition the property definition to validate against
* @param values the values to be validated
* @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
* satisfies the constraints for the property (if any exist).
* @see PropertyDefinition#getValueConstraints()
* @see JcrPropertyDefinition#satisfiesConstraints(Value,JcrSession)
*/
boolean canCastToTypeAndMatchesConstraints( JcrSession session,
JcrPropertyDefinition propertyDefinition,
Value[] values ) {
for (Value value : values) {
if (!canCastToTypeAndMatchesConstraints(session, propertyDefinition, value)) return false;
}
return true;
}
@Override
public boolean canSetProperty( String propertyName,
Value value ) {
return canSetProperty(session, propertyName, value);
}
@Override
public boolean canSetProperty( String propertyName,
Value[] values ) {
return canSetProperty(session, propertyName, values);
}
public boolean canSetProperty( JcrSession session,
String propertyName,
Value value ) {
CheckArg.isNotNull(propertyName, "propertyName");
Name name = context.getValueFactories().getNameFactory().create(propertyName);
// Reuse the logic in RepositoryNodeTypeManager ...
return nodeTypes().findPropertyDefinition(session, this.name, null, name, value, false, true) != null;
}
public boolean canSetProperty( JcrSession session,
String propertyName,
Value[] values ) {
CheckArg.isNotNull(propertyName, "propertyName");
if (values == null || values.length == 0) {
return canRemoveProperty(propertyName);
}
Name name = context.getValueFactories().getNameFactory().create(propertyName);
// Reuse the logic in RepositoryNodeTypeManager ...
return nodeTypes().findPropertyDefinition(session, this.name, null, name, values, true) != null;
}
@Override
public boolean canRemoveProperty( String propertyName ) {
CheckArg.isNotNull(propertyName, "propertyName");
Name name = context.getValueFactories().getNameFactory().create(propertyName);
// Reuse the logic in RepositoryNodeTypeManager ...
return nodeTypes().canRemoveProperty(this.name, null, name, true);
}
@Override
public JcrNodeDefinition[] getDeclaredChildNodeDefinitions() {
// Always have to make a copy to prevent changes ...
return childNodeDefinitions.toArray(new JcrNodeDefinition[childNodeDefinitions.size()]);
}
@Override
public JcrNodeDefinition[] getChildNodeDefinitions() {
// Always have to make a copy to prevent changes ...
Collection<JcrNodeDefinition> definitions = this.allDefinitions.allChildNodeDefinitions();
return definitions.toArray(new JcrNodeDefinition[definitions.size()]);
}
@Override
public JcrPropertyDefinition[] getPropertyDefinitions() {
// Always have to make a copy to prevent changes ...
Collection<JcrPropertyDefinition> definitions = this.allDefinitions.allPropertyDefinitions();
return definitions.toArray(new JcrPropertyDefinition[definitions.size()]);
}
@Override
public JcrNodeType[] getDeclaredSupertypes() {
// Always have to make a copy to prevent changes ...
return declaredSupertypes.toArray(new JcrNodeType[declaredSupertypes.size()]);
}
/**
* {@inheritDoc}
*
* @return the array of names of supertypes declared for this node; possibly empty, never null
*/
@Override
public String[] getDeclaredSupertypeNames() {
List<String> supertypeNames = new ArrayList<String>(declaredSupertypes.size());
for (JcrNodeType declaredSupertype : declaredSupertypes) {
supertypeNames.add(declaredSupertype.getName());
}
// Always have to make a copy to prevent changes ...
return supertypeNames.toArray(new String[supertypeNames.size()]);
}
@Override
public NodeTypeIterator getSubtypes() {
return new JcrNodeTypeIterator(nodeTypes().subtypesFor(this));
}
@Override
public NodeTypeIterator getDeclaredSubtypes() {
return new JcrNodeTypeIterator(nodeTypes().declaredSubtypesFor(this));
}
@Override
public String getName() {
// Translate the name to the correct prefix. Need to check the session to support url-remapping.
return name.getString(context.getNamespaceRegistry());
}
/**
* Returns the internal {@link Name} object for the node type. This method exists outside the JCR API and should not be
* exposed outside of the package.
*
* @return the internal {@link Name} object for the node type.
*/
final Name getInternalName() {
return name;
}
/**
* Returns the internal {@link Name} object for the primary item of this node type. This method exists outside the JCR API and
* should not be exposed outside of the package.
*
* @return the internal {@link Name} object for the primary item of this node type.
*/
Name getInternalPrimaryItemName() {
return primaryItemName;
}
@Override
public String getPrimaryItemName() {
if (primaryItemName == null) {
return null;
}
// Translate the name to the correct prefix. Need to check the session to support url-remapping.
return primaryItemName.getString(context.getNamespaceRegistry());
}
@Override
public JcrPropertyDefinition[] getDeclaredPropertyDefinitions() {
return propertyDefinitions.toArray(new JcrPropertyDefinition[propertyDefinitions.size()]);
}
@Override
public NodeType[] getSupertypes() {
return allSupertypes.toArray(new NodeType[allSupertypes.size()]);
}
@Override
public boolean hasOrderableChildNodes() {
return orderableChildNodes;
}
@Override
public boolean isMixin() {
return mixin;
}
@Override
public boolean isAbstract() {
return isAbstract;
}
@Override
public boolean isQueryable() {
return queryable;
}
@Override
public boolean isNodeType( String nodeTypeName ) {
if (nodeTypeName == null) return false;
Name name = context.getValueFactories().getNameFactory().create(nodeTypeName);
return this.thisAndAllSupertypesNames.contains(name);
}
boolean isNodeType( Name nodeTypeName ) {
if (nodeTypeName == null) return false;
return this.thisAndAllSupertypesNames.contains(nodeTypeName);
}
boolean isNodeTypeOneOf( Name... nodeTypeNames ) {
if (nodeTypeNames == null || nodeTypeNames.length == 0) return false;
for (Name nodeTypeName : nodeTypeNames) {
if (this.thisAndAllSupertypesNames.contains(nodeTypeName)) return true;
}
return false;
}
@Override
public String getLocalName() {
return name.getLocalName();
}
@Override
public String getNamespaceURI() {
return name.getNamespaceUri();
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public boolean equals( Object obj ) {
if (obj == this) return true;
if (obj instanceof JcrNodeType) {
JcrNodeType that = (JcrNodeType)obj;
return this.name.equals(that.name);
}
if (obj instanceof NodeType) {
NodeType that = (NodeType)obj;
return this.getName().equals(that.getName());
}
return false;
}
@Override
public String toString() {
return getName();
}
/**
* Returns a {@link JcrNodeType} that is equivalent to this {@link JcrNodeType}, except with a different repository node type
* manager. This method should only be called during the initialization of the repository node type manager, unless some kind
* of cross-repository type shipping is implemented.
*
* @param nodeTypeManager the new repository node type manager
* @return a new {@link JcrNodeType} that has the same state as this node type, but with the given node type manager.
*/
final JcrNodeType with( RepositoryNodeTypeManager nodeTypeManager ) {
return new JcrNodeType(this.key, this.context, this.session, nodeTypeManager, this.name, this.declaredSupertypes,
this.primaryItemName, this.childNodeDefinitions, this.propertyDefinitions, this.mixin,
this.isAbstract, this.queryable, this.orderableChildNodes);
}
/**
* Returns a {@link JcrNodeType} that is equivalent to this {@link JcrNodeType}, except with a different execution context.
*
* @param context the new execution context
* @param session an active user session, in the context of which the node type is created. May be null, during system
* initialization.
* @return a new {@link JcrNodeType} that has the same state as this node type, but with the given node type manager.
*/
final JcrNodeType with( ExecutionContext context,
JcrSession session ) {
return new JcrNodeType(this.key, context, session, this.nodeTypeManager, this.name, this.declaredSupertypes,
this.primaryItemName, this.childNodeDefinitions, this.propertyDefinitions, this.mixin,
this.isAbstract, this.queryable, this.orderableChildNodes);
}
final NodeTypes nodeTypes() {
return nodeTypeManager.getNodeTypes();
}
final RepositoryNodeTypeManager nodeTypeManager() {
return nodeTypeManager;
}
/**
* Returns whether this node type is in conflict with the provided primary node type or mixin types.
* <p>
* A node type is in conflict with another set of node types if either of the following is true:
* <ol>
* <li>This node type has the same name as any of the other node types</li>
* <li>This node type defines a property (or inherits the definition of a property) with the same name as a property defined
* in any of the other node types <i>unless</i> this node type and the other node type both inherited the property definition
* from the same ancestor node type</li>
* <li>This node type defines a child node (or inherits the definition of a child node) with the same name as a child node
* defined in any of the other node types <i>unless</i> this node type and the other node type both inherited the child node
* definition from the same ancestor node type</li>
* </ol>
* </p>
*
* @param primaryNodeType the primary node type to check
* @param mixinNodeTypes the mixin node types to check
* @return true if this node type conflicts with the provided primary or mixin node types as defined below
*/
final boolean conflictsWith( NodeType primaryNodeType,
NodeType[] mixinNodeTypes ) {
Map<PropertyDefinitionId, JcrPropertyDefinition> props = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
/*
* Need to have the same parent name for all PropertyDefinitionIds and NodeDefinitionIds as we're checking for conflicts on the definition name
*/
final Name DEFAULT_NAME = this.name;
for (JcrPropertyDefinition property : propertyDefinitions()) {
/*
* I'm trying really hard to reuse existing code, but it's a stretch in this case. I don't care about the property
* types or where they were declared... if more than one definition with the given name exists (not counting definitions
* inherited from the same root definition), then there is a conflict.
*/
PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
property.isMultiple());
props.put(pid, property);
}
/*
* The specification does not mandate whether this should or should not be consider a conflict. However, the Apache
* TCK canRemoveMixin test cases assume that this will generate a conflict.
*/
if (primaryNodeType.getName().equals(getName())) {
// This node type has already been applied to the node
return true;
}
for (JcrPropertyDefinition property : ((JcrNodeType)primaryNodeType).propertyDefinitions()) {
PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
property.isMultiple());
JcrPropertyDefinition oldProp = props.put(pid, property);
if (oldProp != null) {
String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
String propTypeName = property.getDeclaringNodeType().getName();
if (!oldPropTypeName.equals(propTypeName)) {
// The two types conflict as both separately declare a property with the same name
return true;
}
}
}
for (NodeType mixinNodeType : mixinNodeTypes) {
/*
* The specification does not mandate whether this should or should not be consider a conflict. However, the Apache
* TCK canRemoveMixin test cases assume that this will generate a conflict.
*/
if (mixinNodeType.getName().equals(getName())) {
// This node type has already been applied to the node
return true;
}
for (JcrPropertyDefinition property : ((JcrNodeType)mixinNodeType).propertyDefinitions()) {
PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
property.isMultiple());
JcrPropertyDefinition oldProp = props.put(pid, property);
if (oldProp != null) {
String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
String propTypeName = property.getDeclaringNodeType().getName();
if (!oldPropTypeName.equals(propTypeName)) {
// The two types conflict as both separately declare a property with the same name
return true;
}
}
}
}
Map<NodeDefinitionId, JcrNodeDefinition> childNodes = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
for (JcrNodeDefinition childNode : childNodeDefinitions()) {
NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
childNodes.put(nid, childNode);
}
for (JcrNodeDefinition childNode : ((JcrNodeType)primaryNodeType).childNodeDefinitions()) {
NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
JcrNodeDefinition oldNode = childNodes.put(nid, childNode);
if (oldNode != null) {
String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
String childNodeTypeName = childNode.getDeclaringNodeType().getName();
if (!oldNodeTypeName.equals(childNodeTypeName)) {
// The two types conflict as both separately declare a child node with the same name
return true;
}
}
}
for (NodeType mixinNodeType : mixinNodeTypes) {
for (JcrNodeDefinition childNode : ((JcrNodeType)mixinNodeType).childNodeDefinitions()) {
NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
JcrNodeDefinition oldNode = childNodes.put(nid, childNode);
if (oldNode != null) {
String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
String childNodeTypeName = childNode.getDeclaringNodeType().getName();
if (!oldNodeTypeName.equals(childNodeTypeName)) {
// The two types conflict as both separately declare a child node with the same name
return true;
}
}
}
}
return false;
}
}