private bool HasChanged(NonNativeObjectInfo nnoi1, NonNativeObjectInfo nnoi2, int objectRecursionLevel)
		{
			AbstractObjectInfo value1 = null;
			AbstractObjectInfo value2 = null;
			bool hasChanged = false;
			// If the object is already being checked, return false, this second
			// check will not affect the check
            int n = 0;
            alreadyCheckingObjects.TryGetValue(nnoi2, out n);
			if (n != 0)
			{
				return false;
			}
			// Put the object in the temporary cache
			alreadyCheckingObjects[nnoi1] = 1;
			alreadyCheckingObjects[nnoi2] = 1;
			// Warning ID Start with 1 and not 0
			for (int id = 1; id <= nnoi1.GetMaxNbattributes(); id++)
			{
				value1 = nnoi1.GetAttributeValueFromId(id);
				// Gets the value by the attribute id to be sure
				// Problem because a new object info may not have the right ids ?
				// Check if
				// the new oiD is ok.
				value2 = nnoi2.GetAttributeValueFromId(id);
				if (value2 == null)
				{
					// this means the object to have attribute id
					StoreChangedObject(nnoi1, nnoi2, id, objectRecursionLevel);
					hasChanged = true;
					continue;
				}
				if (value1 == null)
				{
					//throw new ODBRuntimeException("ObjectInfoComparator.hasChanged:attribute with id "+id+" does not exist on "+nnoi2);
					// This happens when this object was created with an version of ClassInfo (which has been refactored).
					// In this case,we simply tell that in place update is not supported so that the object will be rewritten with 
					// new metamodel
					supportInPlaceUpdate = false;
					continue;
				}
				// If both are null, no effect
				if (value1.IsNull() && value2.IsNull())
				{
					continue;
				}
				if (value1.IsNull() || value2.IsNull())
				{
					supportInPlaceUpdate = false;
					hasChanged = true;
					StoreActionSetAttributetoNull(nnoi1, id, objectRecursionLevel);
					continue;
				}
				if (!ClassAreCompatible(value1, value2))
				{
					if (value2 is NativeObjectInfo)
					{
						StoreChangedObject(nnoi1, nnoi2, id, objectRecursionLevel);
						StoreChangedAttributeAction(new ChangedNativeAttributeAction
							(nnoi1, nnoi2, nnoi1.GetHeader().GetAttributeIdentificationFromId(id), (NativeObjectInfo
							)value2, objectRecursionLevel, false, nnoi1.GetClassInfo().GetAttributeInfoFromId
							(id).GetName()));
					}
					if (value2 is ObjectReference)
					{
						NonNativeObjectInfo nnoi = (NonNativeObjectInfo
							)value1;
						ObjectReference oref = (ObjectReference
							)value2;
						if (!nnoi.GetOid().Equals(oref.GetOid()))
						{
							StoreChangedObject(nnoi1, nnoi2, id, objectRecursionLevel);
							int attributeIdThatHasChanged = id;
							// this is the exact position where the object reference
							// definition is stored
							long attributeDefinitionPosition = nnoi2.GetAttributeDefinitionPosition(attributeIdThatHasChanged
								);
							StoreChangedAttributeAction(new ChangedObjectReferenceAttributeAction
								(attributeDefinitionPosition, (ObjectReference
								)value2, objectRecursionLevel));
						}
						else
						{
							continue;
						}
					}
					hasChanged = true;
					continue;
				}
				if (value1.IsAtomicNativeObject())
				{
					if (!value1.Equals(value2))
					{
						// storeChangedObject(nnoi1, nnoi2, id,
						// objectRecursionLevel);
						StoreChangedAttributeAction(new ChangedNativeAttributeAction
							(nnoi1, nnoi2, nnoi1.GetHeader().GetAttributeIdentificationFromId(id), (NativeObjectInfo
							)value2, objectRecursionLevel, false, nnoi1.GetClassInfo().GetAttributeInfoFromId
							(id).GetName()));
						hasChanged = true;
						continue;
					}
					continue;
				}
				if (value1.IsCollectionObject())
				{
					CollectionObjectInfo coi1 = (CollectionObjectInfo)value1;
					CollectionObjectInfo coi2 = (CollectionObjectInfo)value2;
					bool collectionHasChanged = ManageCollectionChanges(nnoi1, nnoi2, id, coi1, coi2, objectRecursionLevel);
					hasChanged = hasChanged || collectionHasChanged;
					continue;
				}
				if (value1.IsArrayObject())
				{
					ArrayObjectInfo aoi1 = (ArrayObjectInfo)value1;
					ArrayObjectInfo aoi2 = (ArrayObjectInfo)value2;
					bool arrayHasChanged = ManageArrayChanges(nnoi1, nnoi2, id, aoi1, aoi2, objectRecursionLevel
						);
					hasChanged = hasChanged || arrayHasChanged;
					continue;
				}
				if (value1.IsMapObject())
				{
					MapObjectInfo moi1 = (MapObjectInfo)value1;
					MapObjectInfo moi2 = (MapObjectInfo)value2;
					bool mapHasChanged = ManageMapChanges(nnoi1, nnoi2, id, moi1, moi2, objectRecursionLevel
						);
					hasChanged = hasChanged || mapHasChanged;
					continue;
				}
				if (value1.IsEnumObject())
				{
					EnumNativeObjectInfo enoi1 = (EnumNativeObjectInfo)value1;
					EnumNativeObjectInfo enoi2 = (EnumNativeObjectInfo)value2;
					bool enumHasChanged = !enoi1.GetEnumClassInfo().GetId().Equals(enoi2.GetEnumClassInfo
						().GetId()) || !enoi1.GetEnumName().Equals(enoi2.GetEnumName());
					hasChanged = hasChanged || enumHasChanged;
					continue;
				}
				if (value1.IsNonNativeObject())
				{
					NonNativeObjectInfo oi1 = (NonNativeObjectInfo)value1;
					NonNativeObjectInfo oi2 = (NonNativeObjectInfo)value2;
					// If oids are equal, they are the same objects
					if (oi1.GetOid() != null && oi1.GetOid().Equals(oi2.GetOid()))
					{
						hasChanged = HasChanged(value1, value2, objectRecursionLevel + 1) || hasChanged;
					}
					else
					{
						// This means that an object reference has changed.
						hasChanged = true;
						// keep track of the position where the reference must be
						// updated
						long positionToUpdateReference = nnoi1.GetAttributeDefinitionPosition(id);
						StoreNewObjectReference(positionToUpdateReference, oi2, objectRecursionLevel, nnoi1
							.GetClassInfo().GetAttributeInfoFromId(id).GetName());
						objectRecursionLevel++;
						// Value2 may have change too
						AddPendingVerification(value2);
					}
					continue;
				}
			}
			int i1 = (int)alreadyCheckingObjects[nnoi1];
			int i2 = (int)alreadyCheckingObjects[nnoi2];
			if (i1 != null)
			{
				i1 = i1 - 1;
			}
			if (i2 != null)
			{
				i2 = i2 - 1;
			}
			if (i1 == 0)
			{
				alreadyCheckingObjects.Remove(nnoi1);
			}
			else
			{
				alreadyCheckingObjects.Add(nnoi1, i1);
			}
			if (i2 == 0)
			{
				alreadyCheckingObjects.Remove(nnoi2);
			}
			else
			{
				alreadyCheckingObjects.Add(nnoi2, i2);
			}
			return hasChanged;
		}
		private void StoreChangedObject(NonNativeObjectInfo
			 aoi1, NonNativeObjectInfo aoi2, int fieldId
			, AbstractObjectInfo oldValue, AbstractObjectInfo
			 newValue, string message, int objectRecursionLevel)
		{
			if (aoi1 != null && aoi2 != null)
			{
				if (aoi1.GetOid() != null && aoi1.GetOid().Equals(aoi2.GetOid()))
				{
					changedObjectMetaRepresentations.Add(aoi2);
					changes.Add(new ChangedObjectInfo(aoi1
						.GetClassInfo(), aoi2.GetClassInfo(), fieldId, oldValue, newValue, message, objectRecursionLevel
						));
					// also the max recursion level
					if (objectRecursionLevel > maxObjectRecursionLevel)
					{
						maxObjectRecursionLevel = objectRecursionLevel;
					}
					nbChanges++;
				}
				else
				{
					newObjects.Add(aoi2.GetObject());
					string fieldName = aoi1.GetClassInfo().GetAttributeInfoFromId(fieldId).GetName();
					// keep track of the position where the reference must be
					// updated - use aoi1 to get position, because aoi2 do not have position defined yet
					long positionToUpdateReference = aoi1.GetAttributeDefinitionPosition(fieldId);
					StoreNewObjectReference(positionToUpdateReference, aoi2, objectRecursionLevel, fieldName
						);
				}
			}
			else
			{
				//newObjectMetaRepresentations.add(aoi2);
				NeoDatis.Tool.DLogger.Info("Non native object with null object");
			}
		}