// the editing session has sufficient permission to change the primary type.
int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK
| ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
session.getValidator().checkModify(this, options, Permission.NODE_TYPE_MNGMT);
final NodeState state = data.getNodeState();
if (state.getParentId() == null) {
String msg = "changing the primary type of the root node is not supported";
log.debug(msg);
throw new RepositoryException(msg);
}
Name ntName = session.getQName(nodeTypeName);
if (ntName.equals(state.getNodeTypeName())) {
log.debug("Node already has " + nodeTypeName + " as primary node type.");
return;
}
NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
NodeType nt = ntMgr.getNodeType(ntName);
if (nt.isMixin()) {
throw new ConstraintViolationException(nodeTypeName + ": not a primary node type.");
} else if (nt.isAbstract()) {
throw new ConstraintViolationException(nodeTypeName + ": is an abstract node type.");
}
// build effective node type of new primary type & existing mixin's
// in order to detect conflicts
NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
EffectiveNodeType entNew, entOld, entAll;
try {
entNew = ntReg.getEffectiveNodeType(ntName);
entOld = ntReg.getEffectiveNodeType(state.getNodeTypeName());
// try to build new effective node type (will throw in case of conflicts)
entAll = ntReg.getEffectiveNodeType(ntName, state.getMixinTypeNames());
} catch (NodeTypeConflictException ntce) {
throw new ConstraintViolationException(ntce.getMessage());
}
// get applicable definition for this node using new primary type
QNodeDefinition nodeDef;
try {
NodeImpl parent = (NodeImpl) getParent();
nodeDef = parent.getApplicableChildNodeDefinition(getQName(), ntName).unwrap();
} catch (RepositoryException re) {
String msg = this + ": no applicable definition found in parent node's node type";
log.debug(msg);
throw new ConstraintViolationException(msg, re);
}
if (!nodeDef.equals(itemMgr.getDefinition(state).unwrap())) {
onRedefine(nodeDef);
}
Set<QItemDefinition> oldDefs = new HashSet<QItemDefinition>(Arrays.asList(entOld.getAllItemDefs()));
Set<QItemDefinition> newDefs = new HashSet<QItemDefinition>(Arrays.asList(entNew.getAllItemDefs()));
Set<QItemDefinition> allDefs = new HashSet<QItemDefinition>(Arrays.asList(entAll.getAllItemDefs()));
// added child item definitions
Set<QItemDefinition> addedDefs = new HashSet<QItemDefinition>(newDefs);
addedDefs.removeAll(oldDefs);
// referential integrity check
boolean referenceableOld = entOld.includesNodeType(NameConstants.MIX_REFERENCEABLE);
boolean referenceableNew = entNew.includesNodeType(NameConstants.MIX_REFERENCEABLE);
if (referenceableOld && !referenceableNew) {
// node would become non-referenceable;
// make sure no references exist
PropertyIterator iter = getReferences();
if (iter.hasNext()) {
throw new ConstraintViolationException(
"the new primary type cannot be set as it would render "
+ "this node 'non-referenceable' while it is still being "
+ "referenced through at least one property of type REFERENCE");
}
}
// do the actual modifications in content as mandated by the new primary type
// modify the state of this node
NodeState thisState = (NodeState) getOrCreateTransientItemState();
thisState.setNodeTypeName(ntName);
// set jcr:primaryType property
internalSetProperty(NameConstants.JCR_PRIMARYTYPE, InternalValue.create(ntName));
// walk through properties and child nodes and change definition as necessary
// use temp set to avoid ConcurrentModificationException
HashSet<Name> set = new HashSet<Name>(thisState.getPropertyNames());
for (Name propName : set) {
try {
PropertyState propState =
(PropertyState) stateMgr.getItemState(
new PropertyId(thisState.getNodeId(), propName));
if (!allDefs.contains(itemMgr.getDefinition(propState).unwrap())) {
// try to find new applicable definition first and
// redefine property if possible
try {
PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propState.getId());
if (prop.getDefinition().isProtected()) {
// remove 'orphaned' protected properties immediately
removeChildProperty(propName);
continue;
}
PropertyDefinitionImpl pdi = getApplicablePropertyDefinition(
propName, propState.getType(),
propState.isMultiValued(), false);
if (pdi.getRequiredType() != PropertyType.UNDEFINED
&& pdi.getRequiredType() != propState.getType()) {
// value conversion required
if (propState.isMultiValued()) {
// convert value
Value[] values =
ValueHelper.convert(
prop.getValues(),
pdi.getRequiredType(),
session.getValueFactory());
// redefine property
prop.onRedefine(pdi.unwrap());
// set converted values
prop.setValue(values);
} else {
// convert value
Value value =
ValueHelper.convert(
prop.getValue(),
pdi.getRequiredType(),
session.getValueFactory());
// redefine property
prop.onRedefine(pdi.unwrap());
// set converted values
prop.setValue(value);
}
} else {
// redefine property
prop.onRedefine(pdi.unwrap());
}
// update collection of added definitions
addedDefs.remove(pdi.unwrap());
} catch (ValueFormatException vfe) {
// value conversion failed, remove it
removeChildProperty(propName);
} catch (ConstraintViolationException cve) {
// no suitable definition found for this property,
// remove it
removeChildProperty(propName);
}
}
} catch (ItemStateException ise) {
String msg = propName + ": failed to retrieve property state";
log.error(msg, ise);
throw new RepositoryException(msg, ise);
}
}
// use temp array to avoid ConcurrentModificationException
ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(thisState.getChildNodeEntries());
// start from tail to avoid problems with same-name siblings
for (int i = list.size() - 1; i >= 0; i--) {
ChildNodeEntry entry = list.get(i);
try {
NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
if (!allDefs.contains(itemMgr.getDefinition(nodeState).unwrap())) {
// try to find new applicable definition first and
// redefine node if possible
try {
NodeImpl node = (NodeImpl) itemMgr.getItem(nodeState.getId());
if (node.getDefinition().isProtected()) {
// remove 'orphaned' protected child node immediately
removeChildNode(entry.getName(), entry.getIndex());
continue;
}
NodeDefinitionImpl ndi = getApplicableChildNodeDefinition(
entry.getName(),
nodeState.getNodeTypeName());
// redefine node
node.onRedefine(ndi.unwrap());
// update collection of added definitions
addedDefs.remove(ndi.unwrap());
} catch (ConstraintViolationException cve) {