From c5a4e544e1abf11ffa8378a4a03df92e40137cff Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 03 Oct 2009 14:43:29 +0000 Subject: LP 439980 : Redoing addon list comparison, adding comments --- (limited to 'tutorius') diff --git a/tutorius/core.py b/tutorius/core.py index 41089f1..ff592ad 100644 --- a/tutorius/core.py +++ b/tutorius/core.py @@ -261,8 +261,15 @@ class State(object): def is_identical(self, otherState): """ - Compares two states and tells whether they contain the same states and - + Compares two states and tells whether they contain the same states with the + same actions and event filters. + + @param otherState The other State that we wish to match + @returns True if every action in this state has a matching action in the + other state with the same properties and values AND if every + event filters in this state has a matching filter in the + other state having the same properties and values AND if both + states have the same name. ` """ if not isinstance(otherState, State): return False @@ -272,27 +279,38 @@ class State(object): # Do they have the same actions? if len(self._actions) != len(otherState._actions): return False + + if len(self._event_filters) != len(otherState._event_filters): + return False + for act in self._actions: found = False + # For each action in the other state, try to match it with this one. for otherAct in otherState._actions: if act.is_identical(otherAct): found = True break if found == False: + # If we arrive here, then we could not find an action with the + # same values in the other state. We know they're not identical return False # Do they have the same event filters? - if len(self._actions) != len(otherState._actions): - return False for event in self._event_filters: found = False + # For every event filter in the other state, try to match it with + # the current filter. We just need to find one with the right + # properties and values. for otherEvent in otherState._event_filters: if event.is_identical(otherEvent): found = True break - if found == False: + if found == False: + # We could not find the given event filter in the other state. return False + # If nothing failed up to now, then every actions and every filters can + # be found in the other state return True class FiniteStateMachine(State): @@ -568,20 +586,25 @@ class FiniteStateMachine(State): Compares the elements of two FSM to ensure and returns true if they have the same set of states, containing the same actions and the same event filters. - @returns True if the two FSMs have the same content false otherwise + @returns True if the two FSMs have the same content, False otherwise """ if not isinstance(otherFSM, FiniteStateMachine): return False + # Make sure they share the same name if not (self.name == otherFSM.name) or \ not (self.start_state_name == otherFSM.start_state_name): return False - + + # Ensure they have the same number of FSM-level actions if len(self._actions) != len(otherFSM._actions): return False + # Test that we have all the same FSM level actions for act in self._actions: found = False + # For every action in the other FSM, try to match it with the + # current one. for otherAct in otherFSM._actions: if act.is_identical(otherAct): found = True @@ -589,16 +612,26 @@ class FiniteStateMachine(State): if found == False: return False + # Make sure we have the same number of states in both FSMs if len(self._states) != len(otherFSM._states): return False - for state in self._states.itervalues(): - found = False - for otherState in otherFSM._states.itervalues(): - if state.is_identical(otherState): - found = True - break - if found == False: + # For each state, try to find a corresponding state in the other FSM + for state_name in self._states.keys(): + state = self._states[state_name] + other_state = None + try: + # Attempt to use this key in the other FSM. If it's not present + # the dictionary will throw an exception and we'll know we have + # at least one different state in the other FSM + other_state = otherFSM._states[state_name] + except: + return False + # If two states with the same name exist, then we want to make sure + # they are also identical + if not state.is_identical(other_state): return False + # If we made it here, then all the states in this FSM could be matched to an + # identical state in the other FSM. return True diff --git a/tutorius/properties.py b/tutorius/properties.py index 6d30a8d..7bfbad0 100644 --- a/tutorius/properties.py +++ b/tutorius/properties.py @@ -96,25 +96,40 @@ class TPropContainer(object): return object.__getattribute__(self, "_props").keys() def is_identical(self, otherContainer): + """ + Compare this property container to the other one and returns True only if + the every property of the first one can be found in the other container, with + the same name and the same value. + + This is an approximation of identity because we are really looking to see + if this container is at least a subset of the other. + + @param otherContainer The other container that we wish to test for equality. + @returns True if every property in the first container can be found with the same + value and the same name in the second container. + """ + # For every property in this container for prop in self._props.keys(): found = False + # Try to match it with another property present in the other container for otherProp in otherContainer._props.keys(): + # If we were able to match the name, then we look up the value if prop == otherProp: this_type = getattr(type(self), prop).type other_type = getattr(type(otherContainer), prop).type if this_type != other_type: return False + + # If this is an addon list, then we need to make sure that + # every element of the list is also present in the other list if this_type == "addonlist": - for inner_cont in self._props[prop]: - inner_found = False - for other_inner in otherContainer._props[prop]: - if inner_cont.is_identical(other_inner): - inner_found = True - break - if inner_found == False: - return False + if not self._are_lists_identical(self._props[prop], otherContainer._props[prop]): + return False found = True break + + # If this is just an embedded / decorated container, then we want to + # make sure the sub component are identical. elif this_type == "addon": if not self._props[prop].is_identical(otherContainer._props[prop]): return False @@ -124,10 +139,41 @@ class TPropContainer(object): if self._props[prop]== otherContainer._props[prop]: found = True break + # If we arrive here, then we couldn't find any property in the second + # container that matched the current one. We know that the two containers are + # not equal. if found == False: return False return True + def _are_lists_identical(self, myList, otherList): + """ + Compares two lists of property containers to see if they are identical ( + they have the same properties + + @param myList The first list of properties containers + @param otherList The second list of properties containers + @return True if all of the properties inside the list are identical. False otherwise. + """ + # For each property in the first list, + for container in myList: + found = False + # Attempt to match it with every property in the other list + for other_container in otherList: + # If the containers are identical, + if container.is_identical(other_container): + # We found a matching container. We don't need to search in the + # second list anymore, so we break + found = True + break + # In the case the property was not found inside the second list + if found == False: + # We know right away that the two lists are not identical + return False + # If we were able to match each property in the first list, then we + # can say the lists are equal. + return True + class TutoriusProperty(object): """ The base class for all actions' properties. The interface is the following : -- cgit v0.9.1