Program: 172189
Project: 172189
Buggy source:
/** 
 * Does differencing to add and/or remove properties.  Internally, the existing properties will be retrieved and a difference performed to work out which properties need to be created, updated or deleted.  It is only necessary to pass in old and new values for <i>changes</i> i.e. when setting a single property, it is only necessary to pass that property's value in the <b>old</b> and </b>new</b> maps; this improves execution speed significantly - although it has no effect on the number of resulting DB operations. <p/> Note: The cached properties are not updated
 * @param nodeId                the node ID
 * @param newProps              the properties to add or update
 * @param isAddOnly             <tt>true</tt> if the new properties are just an update or<tt>false</tt> if the properties are a complete set
 * @return                      Returns <tt>true</tt> if any properties were changed
 */
private boolean setNodePropertiesImpl(Long nodeId,Map<QName,Serializable> newProps,boolean isAddOnly){
  Node node=getNodeNotNull(nodeId);
  newProps=new HashMap<QName,Serializable>(newProps);
  newProps.keySet().removeAll(AuditablePropertiesEntity.getAuditablePropertyQNames());
  ReferenceablePropertiesEntity.removeReferenceableProperties(node,newProps);
  Map<NodePropertyKey,NodePropertyValue> oldPropsRaw=selectNodeProperties(nodeId);
  Set<Long> qnameIdsOfInterest=qnameDAO.convertQNamesToIds(newProps.keySet(),true);
  Map<NodePropertyKey,NodePropertyValue> newPropsRaw=nodePropertyHelper.convertToPersistentProperties(newProps);
  Map<NodePropertyKey,NodePropertyValue> propsToDelete=new HashMap<NodePropertyKey,NodePropertyValue>(oldPropsRaw);
  Map<NodePropertyKey,NodePropertyValue> propsToAdd=new HashMap<NodePropertyKey,NodePropertyValue>(newPropsRaw);
  Map<NodePropertyKey,MapValueComparison> persistableDiff=EqualsHelper.getMapComparison(propsToDelete,propsToAdd);
  for (  Map.Entry<NodePropertyKey,MapValueComparison> entry : persistableDiff.entrySet()) {
    NodePropertyKey key=entry.getKey();
    QName qname=qnameDAO.getQName(key.getQnameId()).getSecond();
    PropertyDefinition removePropDef=dictionaryService.getProperty(qname);
    boolean isContent=(removePropDef != null && removePropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT));
switch (entry.getValue()) {
case NULL:
case EQUAL:
      propsToDelete.remove(key);
    propsToAdd.remove(key);
  continue;
case RIGHT_ONLY:
propsToDelete.remove(key);
if (isContent) {
NodePropertyValue newPropValue=propsToAdd.get(key);
ContentData newContentData=(ContentData)newPropValue.getValue(DataTypeDefinition.CONTENT);
if (newContentData != null) {
  Long newContentDataId=contentDataDAO.createContentData(newContentData).getFirst();
  newPropValue=new NodePropertyValue(DataTypeDefinition.CONTENT,newContentDataId);
  propsToAdd.put(key,newPropValue);
}
}
continue;
case LEFT_ONLY:
propsToAdd.remove(key);
if (isAddOnly && !qnameIdsOfInterest.contains(key.getQnameId())) {
propsToDelete.remove(key);
continue;
}
if (isContent) {
NodePropertyValue valueToDelete=propsToDelete.get(key);
Long contentDataId=(Long)valueToDelete.getValue(DataTypeDefinition.CONTENT);
if (contentDataId != null) {
contentDataDAO.deleteContentData(contentDataId);
}
}
continue;
case NOT_EQUAL:
if (isContent) {
NodePropertyValue valueToDelete=propsToDelete.get(key);
Long contentDataIdToDelete=(Long)valueToDelete.getValue(DataTypeDefinition.CONTENT);
ContentData contentDataToDelete=(contentDataIdToDelete == null) ? null : contentDataDAO.getContentData(contentDataIdToDelete).getSecond();
NodePropertyValue newPropValue=propsToAdd.get(key);
ContentData newContentData=(ContentData)newPropValue.getValue(DataTypeDefinition.CONTENT);
if (EqualsHelper.nullSafeEquals(contentDataToDelete,newContentData)) {
propsToDelete.remove(key);
propsToAdd.remove(key);
}
 else {
if (contentDataIdToDelete != null) {
contentDataDAO.deleteContentData(contentDataIdToDelete);
}
if (newContentData != null) {
Long newContentDataId=contentDataDAO.createContentData(newContentData).getFirst();
newPropValue=new NodePropertyValue(DataTypeDefinition.CONTENT,newContentDataId);
propsToAdd.put(key,newPropValue);
}
}
}
continue;
default :
throw new IllegalStateException("String_Node_Str" + entry.getValue());
}
}
if (propsToDelete.isEmpty() && propsToAdd.isEmpty()) {
return false;
}
List<NodePropertyKey> propKeysToDeleteList=new ArrayList<NodePropertyKey>(propsToDelete.keySet());
deleteNodeProperties(nodeId,propKeysToDeleteList);
try {
insertNodeProperties(nodeId,propsToAdd);
}
 catch (RuntimeException e) {
propertiesCache.removeByKey(nodeId);
throw e;
}
boolean updated=propsToDelete.size() > 0 || propsToAdd.size() > 0;
if (updated) {
newProps=nodePropertyHelper.convertToPublicProperties(newPropsRaw);
Map<QName,Serializable> propsToCache=null;
if (isAddOnly) {
propsToCache=nodePropertyHelper.convertToPublicProperties(oldPropsRaw);
propsToCache.putAll(newProps);
}
 else {
propsToCache=newProps;
}
setNodePropertiesCached(nodeId,propsToCache);
touchNodeImpl(nodeId);
}
if (isDebugEnabled && updated) {
logger.debug("String_Node_Str" + nodeId + "String_Node_Str"+ "String_Node_Str"+ propsToDelete+ "String_Node_Str"+ "String_Node_Str"+ propsToAdd);
}
return updated;
}

Fixed version (ground truth):
/** 
 * Does differencing to add and/or remove properties.  Internally, the existing properties will be retrieved and a difference performed to work out which properties need to be created, updated or deleted.  It is only necessary to pass in old and new values for <i>changes</i> i.e. when setting a single property, it is only necessary to pass that property's value in the <b>old</b> and </b>new</b> maps; this improves execution speed significantly - although it has no effect on the number of resulting DB operations. <p/> Note: The cached properties are not updated
 * @param nodeId                the node ID
 * @param newProps              the properties to add or update
 * @param isAddOnly             <tt>true</tt> if the new properties are just an update or<tt>false</tt> if the properties are a complete set
 * @return                      Returns <tt>true</tt> if any properties were changed
 */
private boolean setNodePropertiesImpl(Long nodeId,Map<QName,Serializable> newProps,boolean isAddOnly){
  if (isAddOnly && newProps.size() == 0) {
    return false;
  }
  Node node=getNodeNotNull(nodeId);
  newProps=new HashMap<QName,Serializable>(newProps);
  newProps.keySet().removeAll(AuditablePropertiesEntity.getAuditablePropertyQNames());
  ReferenceablePropertiesEntity.removeReferenceableProperties(node,newProps);
  Map<NodePropertyKey,NodePropertyValue> oldPropsRaw=selectNodeProperties(nodeId);
  Set<Long> qnameIdsOfInterest=qnameDAO.convertQNamesToIds(newProps.keySet(),true);
  Map<NodePropertyKey,NodePropertyValue> newPropsRaw=nodePropertyHelper.convertToPersistentProperties(newProps);
  Map<NodePropertyKey,NodePropertyValue> propsToDelete=new HashMap<NodePropertyKey,NodePropertyValue>(oldPropsRaw);
  Map<NodePropertyKey,NodePropertyValue> propsToAdd=new HashMap<NodePropertyKey,NodePropertyValue>(newPropsRaw);
  Map<NodePropertyKey,MapValueComparison> persistableDiff=EqualsHelper.getMapComparison(propsToDelete,propsToAdd);
  for (  Map.Entry<NodePropertyKey,MapValueComparison> entry : persistableDiff.entrySet()) {
    NodePropertyKey key=entry.getKey();
    QName qname=qnameDAO.getQName(key.getQnameId()).getSecond();
    PropertyDefinition removePropDef=dictionaryService.getProperty(qname);
    boolean isContent=(removePropDef != null && removePropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT));
switch (entry.getValue()) {
case NULL:
case EQUAL:
      propsToDelete.remove(key);
    propsToAdd.remove(key);
  continue;
case RIGHT_ONLY:
propsToDelete.remove(key);
if (isContent) {
NodePropertyValue newPropValue=propsToAdd.get(key);
ContentData newContentData=(ContentData)newPropValue.getValue(DataTypeDefinition.CONTENT);
if (newContentData != null) {
  Long newContentDataId=contentDataDAO.createContentData(newContentData).getFirst();
  newPropValue=new NodePropertyValue(DataTypeDefinition.CONTENT,newContentDataId);
  propsToAdd.put(key,newPropValue);
}
}
continue;
case LEFT_ONLY:
propsToAdd.remove(key);
if (isAddOnly && !qnameIdsOfInterest.contains(key.getQnameId())) {
propsToDelete.remove(key);
continue;
}
if (isContent) {
NodePropertyValue valueToDelete=propsToDelete.get(key);
Long contentDataId=(Long)valueToDelete.getValue(DataTypeDefinition.CONTENT);
if (contentDataId != null) {
contentDataDAO.deleteContentData(contentDataId);
}
}
continue;
case NOT_EQUAL:
if (isContent) {
NodePropertyValue valueToDelete=propsToDelete.get(key);
Long contentDataIdToDelete=(Long)valueToDelete.getValue(DataTypeDefinition.CONTENT);
ContentData contentDataToDelete=(contentDataIdToDelete == null) ? null : contentDataDAO.getContentData(contentDataIdToDelete).getSecond();
NodePropertyValue newPropValue=propsToAdd.get(key);
ContentData newContentData=(ContentData)newPropValue.getValue(DataTypeDefinition.CONTENT);
if (EqualsHelper.nullSafeEquals(contentDataToDelete,newContentData)) {
propsToDelete.remove(key);
propsToAdd.remove(key);
}
 else {
if (contentDataIdToDelete != null) {
contentDataDAO.deleteContentData(contentDataIdToDelete);
}
if (newContentData != null) {
Long newContentDataId=contentDataDAO.createContentData(newContentData).getFirst();
newPropValue=new NodePropertyValue(DataTypeDefinition.CONTENT,newContentDataId);
propsToAdd.put(key,newPropValue);
}
}
}
continue;
default :
throw new IllegalStateException("String_Node_Str" + entry.getValue());
}
}
if (propsToDelete.isEmpty() && propsToAdd.isEmpty()) {
return false;
}
List<NodePropertyKey> propKeysToDeleteList=new ArrayList<NodePropertyKey>(propsToDelete.keySet());
deleteNodeProperties(nodeId,propKeysToDeleteList);
try {
insertNodeProperties(nodeId,propsToAdd);
}
 catch (RuntimeException e) {
propertiesCache.removeByKey(nodeId);
throw e;
}
boolean updated=propsToDelete.size() > 0 || propsToAdd.size() > 0;
if (updated) {
newProps=nodePropertyHelper.convertToPublicProperties(newPropsRaw);
Map<QName,Serializable> propsToCache=null;
if (isAddOnly) {
propsToCache=nodePropertyHelper.convertToPublicProperties(oldPropsRaw);
propsToCache.putAll(newProps);
}
 else {
propsToCache=newProps;
}
setNodePropertiesCached(nodeId,propsToCache);
touchNodeImpl(nodeId);
}
if (isDebugEnabled && updated) {
logger.debug("String_Node_Str" + nodeId + "String_Node_Str"+ "String_Node_Str"+ propsToDelete+ "String_Node_Str"+ "String_Node_Str"+ propsToAdd);
}
return updated;
}