| ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
int permissions = Permission.NODE_TYPE_MNGMT;
session.getValidator().checkModify(this, options, permissions);
// check if mixin is assigned
final NodeState state = data.getNodeState();
if (!state.getMixinTypeNames().contains(mixinName)) {
throw new NoSuchNodeTypeException();
}
NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
// build effective node type of remaining mixin's & primary type
Set<Name> remainingMixins = new HashSet<Name>(state.getMixinTypeNames());
// remove name of target mixin
remainingMixins.remove(mixinName);
EffectiveNodeType entResulting;
try {
// build effective node type representing primary type including remaining mixin's
entResulting = ntReg.getEffectiveNodeType(
state.getNodeTypeName(), remainingMixins);
} catch (NodeTypeConflictException e) {
throw new ConstraintViolationException(e.getMessage(), e);
}
/**
* mix:referenceable needs special handling because it has
* special semantics:
* it can only be removed if there no more references to this node
*/
NodeTypeImpl mixin = ntMgr.getNodeType(mixinName);
if ((NameConstants.MIX_REFERENCEABLE.equals(mixinName)
|| mixin.isDerivedFrom(NameConstants.MIX_REFERENCEABLE))
&& !entResulting.includesNodeType(NameConstants.MIX_REFERENCEABLE)) {
// removing this mixin would effectively remove mix:referenceable:
// make sure no references exist
PropertyIterator iter = getReferences();
if (iter.hasNext()) {
throw new ConstraintViolationException(mixinName + " can not be removed: the node is being referenced"
+ " through at least one property of type REFERENCE");
}
}
/*
* mix:lockable: the mixin cannot be removed if the node is currently
* locked even if the editing session is the lock holder.
*/
if ((NameConstants.MIX_LOCKABLE.equals(mixinName)
|| mixin.isDerivedFrom(NameConstants.MIX_LOCKABLE))
&& !entResulting.includesNodeType(NameConstants.MIX_LOCKABLE)
&& isLocked()) {
throw new ConstraintViolationException(mixinName + " can not be removed: the node is locked.");
}
NodeState thisState = (NodeState) getOrCreateTransientItemState();
// collect information about properties and nodes which require
// further action as a result of the mixin removal;
// we need to do this *before* actually changing the assigned the mixin types,
// otherwise we wouldn't be able to retrieve the current definition
// of an item.
Map<PropertyId, PropertyDefinition> affectedProps = new HashMap<PropertyId, PropertyDefinition>();
Map<ChildNodeEntry, NodeDefinition> affectedNodes = new HashMap<ChildNodeEntry, NodeDefinition>();
try {
Set<Name> names = thisState.getPropertyNames();
for (Name propName : names) {
PropertyId propId = new PropertyId(thisState.getNodeId(), propName);
PropertyState propState = (PropertyState) stateMgr.getItemState(propId);
PropertyDefinition oldDef = itemMgr.getDefinition(propState);
// check if property has been defined by mixin type (or one of its supertypes)
NodeTypeImpl declaringNT = (NodeTypeImpl) oldDef.getDeclaringNodeType();
if (!entResulting.includesNodeType(declaringNT.getQName())) {
// the resulting effective node type doesn't include the
// node type that declared this property
affectedProps.put(propId, oldDef);
}
}
List<ChildNodeEntry> entries = thisState.getChildNodeEntries();
for (ChildNodeEntry entry : entries) {
NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
NodeDefinition oldDef = itemMgr.getDefinition(nodeState);
// check if node has been defined by mixin type (or one of its supertypes)
NodeTypeImpl declaringNT = (NodeTypeImpl) oldDef.getDeclaringNodeType();
if (!entResulting.includesNodeType(declaringNT.getQName())) {
// the resulting effective node type doesn't include the
// node type that declared this child node
affectedNodes.put(entry, oldDef);
}
}
} catch (ItemStateException e) {
throw new RepositoryException("Internal Error: Failed to determine effect of removing mixin " + session.getJCRName(mixinName), e);
}
// modify the state of this node
thisState.setMixinTypeNames(remainingMixins);
// set jcr:mixinTypes property
setMixinTypesProperty(remainingMixins);
// process affected nodes & properties:
// 1. try to redefine item based on the resulting
// new effective node type (see JCR-2130)
// 2. remove item if 1. fails
boolean success = false;
try {
for (PropertyId id : affectedProps.keySet()) {
PropertyImpl prop = (PropertyImpl) itemMgr.getItem(id);
PropertyDefinition oldDef = affectedProps.get(id);
if (oldDef.isProtected()) {
// remove 'orphaned' protected properties immediately
removeChildProperty(id.getName());
continue;
}
// try to find new applicable definition first and
// redefine property if possible (JCR-2130)
try {
PropertyDefinitionImpl newDef = getApplicablePropertyDefinition(
id.getName(), prop.getType(),
oldDef.isMultiple(), false);
if (newDef.getRequiredType() != PropertyType.UNDEFINED
&& newDef.getRequiredType() != prop.getType()) {
// value conversion required
if (oldDef.isMultiple()) {
// convert value
Value[] values =
ValueHelper.convert(
prop.getValues(),
newDef.getRequiredType(),
session.getValueFactory());
// redefine property
prop.onRedefine(newDef.unwrap());
// set converted values
prop.setValue(values);
} else {
// convert value
Value value =
ValueHelper.convert(
prop.getValue(),
newDef.getRequiredType(),
session.getValueFactory());
// redefine property
prop.onRedefine(newDef.unwrap());
// set converted values
prop.setValue(value);
}
} else {
// redefine property
prop.onRedefine(newDef.unwrap());
}
} catch (ValueFormatException vfe) {
// value conversion failed, remove it
removeChildProperty(id.getName());
} catch (ConstraintViolationException cve) {
// no suitable definition found for this property,
// remove it
removeChildProperty(id.getName());
}
}
for (ChildNodeEntry entry : affectedNodes.keySet()) {
NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
NodeImpl node = (NodeImpl) itemMgr.getItem(entry.getId());
NodeDefinition oldDef = affectedNodes.get(entry);
if (oldDef.isProtected()) {
// remove 'orphaned' protected child node immediately
removeChildNode(entry.getName(), entry.getIndex());
continue;
}
// try to find new applicable definition first and
// redefine node if possible (JCR-2130)
try {
NodeDefinitionImpl newDef = getApplicableChildNodeDefinition(
entry.getName(),
nodeState.getNodeTypeName());
// redefine node
node.onRedefine(newDef.unwrap());
} catch (ConstraintViolationException cve) {
// no suitable definition found for this child node,
// remove it