"""pathmap.py: Objects to represent the PathMap file $Revision: 1.9 $ $Date: 2000/03/27 08:09:18 $ """ import string # Standard string functions import re # Standard regular expression package from scan import * # Author's stream scanning package from log import * # Author's error logging package class PathMap: """Represents the PathMap file. Exports: PathMap ( fileName ): [ if fileName is a string -> if fileName names a readable, valid PathMap file -> return a new PathMap object representing that file else -> Log() +:= error message(s) raise IOError ] .rootURL: [ the root URL from self as a string (read-only) ] .expandShortName(name): [ if name is a string -> if name is a valid short name in self -> return the corresponding path name relative to the starting directory else -> return None ] State/Invariants: .__shortPathMap: [ a dictionary that maps each short name |-> the corresponding full path name ] """ # - - - P a t h M a p . _ _ i n i t _ _ - - - def __init__ ( self, fileName ): """Constructor for the PathMap object """ #-- 1 -- # [ errCount := count of errors from Log() ] errCount = Log().count() #-- 2 -- # [ if fileName is a readable, valid PathMap file -> # self := self with entries added from that file # else -> # self := self with valid entries from that file added # Log() +:= error message(s) ] self.__readFile ( fileName ) #-- 3 -- # [ if Log() has more than errCount errors -> # Log() +:= error message # stop execution # else -> I ] if errCount < Log().count(): Log().error ( "Execution terminated due to errors in the " "`%s' file." % fileName ) raise IOError, "There were errors in the PathMap file." # - - - P a t h M a p . _ _ r e a d F i l e - - - def __readFile ( self, fileName ): """Read the source file for self. [ if fileName is a string -> if fileName names a readable, valid PathMap file -> self.rootURL := the root URL name from that file self.__shortPathMap := a dictionary that maps each short name from that file |-> corresponding full path name else -> self := self with valid entries from that file added Log() +:= error message(s) ] """ #-- 1 -- # [ errCount := count of errors from Log() # self.__shortPathMap := a new, empty dictionary ] errCount = Log().count() self.__shortPathMap = {} #-- 2 -- # [ if fileName can be opened for reading -> # scan := a Scan object pointing to the start of that file # else -> # Log() +:= error message # return ] try: scan = Scan ( fileName ) except IOError: Log().error ( "Can't open file `%s' for reading." % fileName ) return #-- 3 -- # [ if scan contains one line starting with "http://" -> # scan := scan advanced to the next line # self.rootURL := that line, without trailing whitespace # else -> # Log() +:= error message # return ] if scan.atEndFile: Log().error ( "The path map file must contain at least one " "line specifying the root URL." ) return self.rootURL = string.rstrip(scan.tab(-1)) scan.nextLine() prefix = "http://" if ( ( len(self.rootURL) <= len(prefix) ) or ( self.rootURL[0:len(prefix)] != prefix ) ): Log().error ( "The first line of the path map file must " "start with `%s'." % prefix ) return #-- 4 -- # [ if the remainder of scan consists of valid path map lines -> # scan := scan advanced to EOF # self.__shortPathMap +:= entries mapping short name |-> # corresponding full path name # else -> # scan := scan advanced to EOF # self.__shortPathMap +:= entries mapping valid short names # |-> corresponding full path names # Log() +:= error messages about invalid lines ] while not scan.atEndFile: self.__readPathLine ( scan ) scan.nextLine() #-- 5 -- scan.close() # - - - P a t h M a p . _ _ r e a d P a t h L i n e - - - linePat = re.compile ( r'^' # Anchor to start of line r'(?P\S+)' # Short name r'\s+' # Any whitespace r'(?P\S+)' # Full path r'\s*' # Ignore trailing whitespace r'$' ) # Anchor to end of line def __readPathLine ( self, scan ): """Read and store one path-map line from the given scan stream. [ if scan is a Scan object -> if the line in scan is a valid path map line -> self.__shortPathMap +:= entry mapping short name |-> corresponding full path name else -> Log() +:= error message(s) ] """ #-- 1 -- # [ if line in scan matches linePat -> # scan := scan advanced to end of line # m := a Match object describing the matched fields # else -> # m := None ] m = scan.tabReMatch ( self.linePat ) #-- 2 -- # [ if (m is None) # or (self.__shortPathMap already contains a key like # the group from m) -> # Log() +:= error message # else -> # self.__shortPathMap +:= a new entry mapping the # group from m |-> the group ] if m is None: scan.error ( "Valid path map lines must start with the " "short name, then whitespace, then the " "full path." ) else: short = m.group ( "short" ) full = m.group ( "full" ) if self.__shortPathMap.has_key ( short ): scan.error ( "Duplicate short name: `%s'" % short ) else: self.__shortPathMap[short] = full # - - - P a t h M a p . e x p a n d S h o r t N a m e - - - def expandShortName ( self, s ): """Expand a short name into a relative pathname. [ if (s has the form "spath/sfile[#anchor]") and (spath is a key in self.__shortPathMap) -> return self.__shortPathMap[spath] + "/" + sfile[#anchor] else if (s has the form "spath/sfile[#anchor]") and (spath is not a key in self.__shortPathMap) -> return None else -> return s ] """ #-- 1 -- # [ if there is a slash in s -> # shortPath := characters from s up to the first slash # filePart := characters from first slash through end # else -> # return s ] pieces = string.split ( s, "/", 1 ) if len(pieces) < 2: return s else: shortPath, filePart = pieces #-- 2 -- # [ if shortPath is not a key in self.__shortPathMap -> # return None # else -> # return self.__shortPathMap[shortPath] + "/" + filePart ] try: return "%s/%s" % ( self.__shortPathMap[shortPath], filePart ) except KeyError: return None