def __removeGapNavigations(self): # We do a second pass over the navigations so that we remove any # parts of the navigation that have been previously identified as being # a navigation to a gap (a space between two method definitions). finalNavigations = [] fromNav = None toNav = None foundGap = False for navigation in self._navigations: if not foundGap: if navigation.fromFileNav is None: if navigation.toFileNav.isGap: fromNav = navigation.fromFileNav foundGap = True else: finalNavigations.append(navigation) elif not navigation.fromFileNav.isGap and navigation.toFileNav.isGap: fromNav = navigation.fromFileNav foundGap = True elif navigation.fromFileNav.isGap and navigation.toFileNav.isGap: raise RuntimeError('removeGapNavigations: cannot have a navigation with a fromFileNav that is a gap without a prior navigation with a gap in the toFileNav') else: if not navigation.isToSameMethod(): finalNavigations.append(navigation) elif foundGap: if navigation.fromFileNav.isGap and not navigation.toFileNav.isGap: toNav = navigation.toFileNav foundGap = False newNavigation = Navigation(fromNav, toNav) if not newNavigation.isToSameMethod(): finalNavigations.append(Navigation(fromNav, toNav)) elif navigation.fromFileNav.isGap and navigation.toFileNav.isGap: continue else: raise RuntimeError('removeGapNavigations: cannot have a fromFileNav without a gap if the prior navigation had a gap in the toFileNav') self._navigations = finalNavigations
def __findMethodsForFileNavigations(self, conn): # Here we map the file paths and offsets in the fileNavigations list to # FQNs of methods. This is done by querying for all the Method # declarations within the database and storing that data to the # self.knownMethods object. The insertions into knownMethods will create # entries if they are new or update them if they already exist. Since # code can be changed between navigations, we need to update # self.knownMethods to reflect the most recent state of the code up to # each navigation. # After building the known methods, we test an entry from # fileNavigations against the set of known methods by offset. This is # what maps Text selection offsets to methods. prevNavigation = None postProcessing = False # Iterate over the data gathered from the Text selection offsets for i in range(len(self.__fileNavigations)): toFileNavigation = self.__fileNavigations[i] if self.VERBOSE_PATH: print '\tProcessing text selection offset: ' + str(toFileNavigation) # For every navigation's timestamp, we fill the knownMethods object # with the details of every method declaration up to the timestamp # of the toFileNavigation. The knownMethods object will be queried to # determine in which method (if any) a text selection offset occurs. # Note that the queries here are by a method's FQN. This allows us # to update the method's declaration info if it gets updated at some # point in the future. c = conn.execute(self.METHOD_DECLARATIONS_QUERY, [toFileNavigation.timestamp]) for row in c: action, target, referrer = row['action'], \ row['target'], row['referrer'] if action == 'Method declaration': self.knownPatches.addFilePatch(referrer) elif action == 'Method declaration offset': method = self.knownPatches.findMethodByFqn(target) if method is not None: method.startOffset = int(referrer) elif action == 'Method declaration length': method = self.knownPatches.findMethodByFqn(target) if method is not None: method.length = int(referrer); # Recall that navigations contains the navigation data after its # been translated to methods and headers # If there was at least 1 navigation already, the to destination # from the previous navigation serves as this navigations from. A # clone is necessary since this may be later transformed into a # PFIG header and we don't want to affect the to destination from # the previous navigation. fromFileNavigation = None fromMethodPatch = None if len(self._navigations) > 0: prevNavigation = self._navigations[-1] fromFileNavigation = prevNavigation.toFileNav.clone() self.__addPFIGFileHeadersIfNeeded(conn, prevNavigation, toFileNavigation) fromMethodPatch = self.knownPatches.findMethodByOffset(fromFileNavigation.filePath, fromFileNavigation.offset) # We query known methods here to see if the offset of the current # toFileNavigation is among the known patches. toMethodPatch = self.knownPatches.findMethodByOffset(toFileNavigation.filePath, toFileNavigation.offset) # Create the navigation object representing this navigation navigation = Navigation(fromFileNavigation, toFileNavigation.clone()) # Set method FQN data if navigation.fromFileNav is not None and fromMethodPatch is not None: navigation.fromFileNav.methodFqn = fromMethodPatch.fqn if navigation.toFileNav is not None and toMethodPatch is not None: navigation.toFileNav.methodFqn = toMethodPatch.fqn if not navigation.isToSameMethod(): self._navigations.append(navigation) if navigation.fromFileNav is not None: if navigation.fromFileNav.methodFqn is None: postProcessing = True navigation.fromFileNav.isGap = True prevNavigation.toFileNav.isGap = True c.close() if postProcessing: self.__removeGapNavigations()