From 3bb38c9649bb7728dfc6d422238911eb1dbb6cff Mon Sep 17 00:00:00 2001
From: Hendrik Buschmeier <hbuschme@uni-bielefeld.de>
Date: Fri, 4 Dec 2015 17:42:55 +0100
Subject: [PATCH] ipaaca-python: modified retraction logic

* Exceptions when trying to modify retracted IUs.
* Removal of IU also retracts it.
---
 ipaacalib/python/src/ipaaca/buffer.py |  13 ++--
 ipaacalib/python/src/ipaaca/iu.py     | 102 +++++++++++++++-----------
 2 files changed, 69 insertions(+), 46 deletions(-)

diff --git a/ipaacalib/python/src/ipaaca/buffer.py b/ipaacalib/python/src/ipaaca/buffer.py
index c4b75a4..ab97df6 100644
--- a/ipaacalib/python/src/ipaaca/buffer.py
+++ b/ipaacalib/python/src/ipaaca/buffer.py
@@ -4,7 +4,7 @@
 #  "Incremental Processing Architecture
 #   for Artificial Conversational Agents".
 #
-# Copyright (c) 2009-2014 Social Cognitive Systems Group
+# Copyright (c) 2009-2015 Social Cognitive Systems Group
 #                         CITEC, Bielefeld University
 #
 # http://opensource.cit-ec.de/projects/ipaaca/
@@ -365,7 +365,7 @@ class OutputBuffer(Buffer):
 	"""An OutputBuffer that holds local IUs."""
 
 	def __init__(self, owning_component_name, channel=None, participant_config=None):
-		'''Create an Output Buffer.
+		'''Create an OutputBuffer.
 
 		Keyword arguments:
 		owning_component_name -- name of the entity that own this buffer
@@ -479,6 +479,8 @@ class OutputBuffer(Buffer):
 			raise ipaaca.exception.IUPublishedError(iu)
 		if iu.buffer is not None:
 			raise ipaaca.exception.IUPublishedError(iu)
+		if iu.retracted:
+			raise ipaaca.exception.IURetractedError(iu)
 		if iu.access_mode != ipaaca.iu.IUAccessMode.MESSAGE:
 			# Messages are not really stored in the OutputBuffer
 			self._iu_store[iu.uid] = iu
@@ -486,12 +488,12 @@ class OutputBuffer(Buffer):
 		self._publish_iu(iu)
 
 	def remove(self, iu=None, iu_uid=None):
-		'''Remove the iu or an IU corresponding to iu_uid from the OutputBuffer, retracting it from the system.'''
+		'''Retracts an IU and removes it from the OutputBuffer.'''
 		if iu is None:
 			if iu_uid is None:
 				return None
 			else:
-				if iu_uid not in self. _iu_store:
+				if iu_uid not in self._iu_store:
 					raise ipaaca.exception.IUNotFoundError(iu_uid)
 				iu = self._iu_store[iu_uid]
 		# unpublish the IU
@@ -505,12 +507,13 @@ class OutputBuffer(Buffer):
 		informer.publishData(iu)
 
 	def _retract_iu(self, iu):
-		'''Retract (unpublish) an IU.'''
+		'''Retract an IU.'''
 		iu_retraction = ipaaca_pb2.IURetraction()
 		iu_retraction.uid = iu.uid
 		iu_retraction.revision = iu.revision
 		informer = self._get_informer(iu._category)
 		informer.publishData(iu_retraction)
+		iu._retracted = True
 
 	def _send_iu_commission(self, iu, writer_name):
 		'''Send IU commission.
diff --git a/ipaacalib/python/src/ipaaca/iu.py b/ipaacalib/python/src/ipaaca/iu.py
index fd55bd7..66f139f 100644
--- a/ipaacalib/python/src/ipaaca/iu.py
+++ b/ipaacalib/python/src/ipaaca/iu.py
@@ -4,7 +4,7 @@
 #  "Incremental Processing Architecture
 #   for Artificial Conversational Agents".
 #
-# Copyright (c) 2009-2014 Social Cognitive Systems Group
+# Copyright (c) 2009-2015 Social Cognitive Systems Group
 #                         CITEC, Bielefeld University
 #
 # http://opensource.cit-ec.de/projects/ipaaca/
@@ -114,7 +114,7 @@ class IUInterface(object):
 			s += k+":'"+unicode(v)+"', "
 		s += "} "
 		s += "links={ "
-		for t,ids in self.get_all_links().items():
+		for t, ids in self.get_all_links().items():
 			s += t+":'"+unicode(ids)+"', "
 		s += "} "
 		s += "}"
@@ -173,9 +173,9 @@ class IUInterface(object):
 		if self._buffer is None:
 			self._payload_type = type
 		else:
-			raise IpaacaException('The IU is already in a buffer, cannot change payload type anymore.')
+			raise ipaaca.exception.IpaacaError('The IU is already in a buffer, cannot change payload type anymore.')
 	payload_type = property(
-		fget=_get_payload_type, 
+		fget=_get_payload_type,
 		fset=_set_payload_type,
 		 doc='Type of the IU payload')
 
@@ -209,7 +209,7 @@ class IUInterface(object):
 		return self._buffer
 	def _set_buffer(self, buffer):
 		if self._buffer is not None:
-			raise IpaacaException('The IU is already in a buffer, cannot move it.')
+			raise ipaaca.exception.IpaacaError('The IU is already in a buffer, cannot move it.')
 		self._buffer = buffer
 	buffer = property(
 			fget=_get_buffer,
@@ -220,7 +220,7 @@ class IUInterface(object):
 		return self._owner_name
 	def _set_owner_name(self, owner_name):
 		if self._owner_name is not None:
-			raise Exception('The IU already has an owner name, cannot change it.')
+			raise ipaaca.exception.IpaacaError('The IU already has an owner name, cannot change it.')
 		self._owner_name = owner_name
 	owner_name = property(
 			fget=_get_owner_name,
@@ -241,7 +241,9 @@ class IU(IUInterface):
 		self._payload = ipaaca.payload.Payload(iu=self)
 
 	def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None):
-		if self.committed:
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._committed:
 			raise ipaaca.exception.IUCommittedError(self)
 		with self.revision_lock:
 			# modify links locally
@@ -258,7 +260,9 @@ class IU(IUInterface):
 
 	def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None):
 		"""Modify the payload: add or remove items from this payload locally and send update."""
-		if self.committed:
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._committed:
 			raise ipaaca.exception.IUCommittedError(self)
 		with self.revision_lock:
 			# set item locally
@@ -279,23 +283,33 @@ class IU(IUInterface):
 		self._revision += 1
 
 	def _internal_commit(self, writer_name=None):
-		if self.committed:
-			raise ipaaca.exception.IUCommittedError(self)
+		if self._committed:
+			return
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
 		with self.revision_lock:
-			if not self._committed:
-				self._increase_revision_number()
-				self._committed = True
-				if self.buffer is not None:
-					self.buffer._send_iu_commission(self, writer_name=writer_name)
+			self._increase_revision_number()
+			self._committed = True
+			if self.buffer is not None:
+				self.buffer._send_iu_commission(self, writer_name=writer_name)
 
 	def commit(self):
 		"""Commit to this IU."""
 		return self._internal_commit()
 
+	def retract(self):
+		if self._buffer:
+			self._buffer.remove(self)
+			self._buffer = None
+		else:
+			self._retracted = True
+
 	def _get_payload(self):
 		return self._payload
 	def _set_payload(self, new_pl, writer_name=None):
-		if self.committed:
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._committed:
 			raise ipaaca.exception.IUCommittedError(self)
 		with self.revision_lock:
 			self._increase_revision_number()
@@ -316,7 +330,7 @@ class IU(IUInterface):
 
 	def _set_buffer(self, buffer):
 		if self._buffer is not None:
-			raise Exception('The IU is already in a buffer, cannot move it.')
+			raise ipaaca.exception.IpaacaError('The IU is already in a buffer, cannot move it.')
 		self._buffer = buffer
 		self.owner_name = buffer.unique_name
 		self._payload.owner_name = buffer.unique_name
@@ -364,7 +378,9 @@ class Message(IU):
 		if self.is_published:
 			LOGGER.info('Info: modifying a Message after sending has no global effects')
 		else:
-			if self.committed:
+			if self._retracted:
+				raise ipaaca.exception.IURetractedError(self)
+			if self._committed:
 				raise ipaaca.exception.IUCommittedError(self)
 			with self.revision_lock:
 				self._increase_revision_number()
@@ -385,7 +401,7 @@ class Message(IU):
 
 	def _set_buffer(self, buffer):
 		if self._buffer is not None:
-			raise Exception('The IU is already in a buffer, cannot move it.')
+			raise ipaaca.exception.IpaacaError('The IU is already in a buffer, cannot move it.')
 		self._buffer = buffer
 		self.owner_name = buffer.unique_name
 		self._payload.owner_name = buffer.unique_name
@@ -414,7 +430,6 @@ class RemoteMessage(IUInterface):
 		self._category = category
 		self.owner_name = owner_name
 		self._committed = committed
-		self._retracted = False
 		# NOTE Since the payload is an already-existant Payload which we didn't modify ourselves,
 		#  don't try to invoke any modification checks or network updates ourselves either.
 		#  We are just receiving it here and applying the new data.
@@ -481,7 +496,6 @@ class RemotePushIU(IUInterface):
 		self._category = category
 		self.owner_name = owner_name
 		self._committed = committed
-		self._retracted = False
 		# NOTE Since the payload is an already-existant Payload which we didn't modify ourselves,
 		#  don't try to invoke any modification checks or network updates ourselves either.
 		#  We are just receiving it here and applying the new data.
@@ -490,9 +504,11 @@ class RemotePushIU(IUInterface):
 
 	def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None):
 		"""Modify the links: add or remove item from this payload remotely and send update."""
-		if self.committed:
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._committed:
 			raise ipaaca.exception.IUCommittedError(self)
-		if self.read_only:
+		if self._read_only:
 			raise ipaaca.exception.IUReadOnlyError(self)
 		requested_update = ipaaca.converter.IULinkUpdate(
 				uid=self.uid,
@@ -510,9 +526,11 @@ class RemotePushIU(IUInterface):
 
 	def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None):
 		"""Modify the payload: add or remove item from this payload remotely and send update."""
-		if self.committed:
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._committed:
 			raise ipaaca.exception.IUCommittedError(self)
-		if self.read_only:
+		if self._read_only:
 			raise ipaaca.exception.IUReadOnlyError(self)
 		requested_update = ipaaca.converter.IUPayloadUpdate(
 				uid=self.uid,
@@ -531,30 +549,32 @@ class RemotePushIU(IUInterface):
 
 	def commit(self):
 		"""Commit to this IU."""
-		if self.read_only:
-			raise ipaaca.exception.IUReadOnlyError(self)
 		if self._committed:
-			# ignore commit requests when already committed
 			return
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._read_only:
+			raise ipaaca.exception.IUReadOnlyError(self)
+		commission_request = ipaaca_pb2.IUCommission()
+		commission_request.uid = self.uid
+		commission_request.revision = self.revision
+		commission_request.writer_name = self.buffer.unique_name
+		remote_server = self.buffer._get_remote_server(self)
+		new_revision = remote_server.commit(commission_request)
+		if new_revision == 0:
+			raise ipaaca.exception.IUUpdateFailedError(self)
 		else:
-			commission_request = ipaaca_pb2.IUCommission()
-			commission_request.uid = self.uid
-			commission_request.revision = self.revision
-			commission_request.writer_name = self.buffer.unique_name
-			remote_server = self.buffer._get_remote_server(self)
-			new_revision = remote_server.commit(commission_request)
-			if new_revision == 0:
-				raise ipaaca.exception.IUUpdateFailedError(self)
-			else:
-				self._revision = new_revision
-				self._committed = True
+			self._revision = new_revision
+			self._committed = True
 
 	def _get_payload(self):
 		return self._payload
 	def _set_payload(self, new_pl):
-		if self.committed:
+		if self._retracted:
+			raise ipaaca.exception.IURetractedError(self)
+		if self._committed:
 			raise ipaaca.exception.IUCommittedError(self)
-		if self.read_only:
+		if self._read_only:
 			raise ipaaca.exception.IUReadOnlyError(self)
 		requested_update = ipaaca.converter.IUPayloadUpdate(
 				uid=self.uid,
-- 
GitLab