Next / Previous / Contents / Shipman's homepage

74.6. BaseEncounter.scanLine(): Scan a raw encounter record

For the format of encounter lines, see the specification.

This class method parses the raw encounter record and stores the field values it finds into a BaseEncounter record, then returns that record if all goes well.

Why is this a class method, and not a static method? In the MAPS 2006 protocol, it was a static method. However, in the process of writing the MAPS 2004 compiler, it became apparent that it must be a class method, and the class object must be used in place of references to BaseEncounter, so that it will call the derived scanEncounter() instead of the one in the base class.

baseclasses.py
# - - -   B a s e E n c o u n t e r . s c a n L i n e   - - -

#   @classmethod
    def scanLine(myClass, compiler, scan):
        '''Scan a raw encounter record.

          [ (myClass is derived from BaseEncounter) and
            (compiler is a BaseCompiler) and
            (scan is a Scan object) ->
              if the line in scan is a valid encounter line ->
                return a new myClass object representing that
                line
              else ->
                Log()  +:=  error message(s)
                raise SyntaxError ]
        '''

The first task is to look at the first character and see what kind of record it is. In many cases, the first character is a letter, one of the “encounter codes” described in the specification under “Banding sheet layout.”. There are two exceptions:

The class attribute .dispatchMap is a dictionary that gives the correspondence between (uppercased) encounter codes and the methods that parse each line type. This dictionary is defined in Section 74.12, “BaseEncounter.dispatchMap: Encounter record routing dictionary”; this dictionary must be declared after all the methods that it points to. Note that all the parsing methods in this table must have intended functions that match the generic intended function given right before .dispatchMap.

First we pull out the first character, upshift it (to be case-insensitive), and see if it's a key in .dispatchMap. If so, we store the upshifted version in self.captureCode. If not, we use CAPTURE_RECAP.

baseclasses.py
        #-- 1 --
        # [ if the line in scan is empty ->
        #     raise SyntaxError
        #   else ->
        #     first  :=  first character of that line, uppercased ]
        if  scan.pos < len(scan.line):
            first = scan.line[scan.pos].upper()
        else:
            message = ("Programming error: empty line passed to "
                          "BaseEncounter.scan()")
            scan.syntax(message)

        #-- 2 --
        # [ encounter  :=  a new myClass record with
        #                  compiler=(compiler) ]
        encounter = myClass(compiler)

If there is no page header line currently in effect, an encounter line is not valid. Normally, though, at this point we will copy the band size and page number into the encounter object.

baseclasses.py
        #-- 3 --
        # [ if compiler.pageHeader is None ->
        #     Log()  +:=  error message
        #     raise SyntaxError
        #   else ->
        #     encounter.(BAND_SIZE_ATTR)  :=  compiler.pageHeader.size
        #     encounter.(PAGE_NO_ATTR)  := compiler.pageHeader.pageNo ]
        if  compiler.pageHeader is None:
            scan.syntax("There is no page header line in effect.") 
        else:
            setattr(encounter, BAND_SIZE_ATTR, 
                    BandSizeField(encounter,
                                  compiler.pageHeader.size))
            setattr(encounter, PAGE_NO_ATTR, 
                    PageNoField(encounter,
                                compiler.pageHeader.pageNo))

All encounter records must have a location code. For a multi-station set, that comes from the compiler's .location.locCode. For a single-station set, it comes from the compiler's .station.loc.locCode. We wrap the location code in a LocationField object so there will be a .flatten() method for it.

baseclasses.py
        #-- 4 --
        if  compiler.location is None:
            setattr(encounter, LOCATION_ATTR,
                LocationField(encounter, 
                              compiler.station.loc.locCode))
        else:
            setattr(encounter, LOCATION_ATTR,
                LocationField(encounter,
                              compiler.location.locCode))

Similarly, we package the capture code in a CaptureCodeField object; see Section 31, “class CaptureCodeField: Encounter record type code”.

baseclasses.py
        #-- 5 --
        # [ if first is a key in encounter.dispatchMap ->
        #     scan  :=  scan advanced 1
        #     encounter.captureCode  :=  first
        #     scanMethod  :=  corresponding value from
        #                     encounter.dispatchMap
        #   else ->
        #     encounter.captureCode  :=  CAPTURE_RECAP
        #     scanMethod  :=  encounter.dispatchMap[CAPTURE_RECAP] ]
        try:
            scanMethod = encounter.dispatchMap [ first ]
            scan.move(1)
            encounter.captureCode = CaptureCodeField(encounter, 
                                                       first)
        except KeyError:
            first = CAPTURE_RECAP
            encounter.captureCode = CaptureCodeField(encounter,
                                                       CAPTURE_RECAP)
            scanMethod = myClass.dispatchMap [ CAPTURE_RECAP ]

Now we call the appropriate method for the line type. For Cleanroom verification, compare this intended function against the generic intended function for encounter line parsers, which will be found at the start of Section 74.12, “BaseEncounter.dispatchMap: Encounter record routing dictionary”.

baseclasses.py
        #-- 6 --
        # [ if (encounter.captureCode) + (line in scan) is a valid
        #   encounter line in the context of self.compiler ->
        #         scan  :=  scan advanced to the end of the line
        #         encounter  :=  encounter with all fields from
        #             that line added using names in self.OUT_FIELD_LIST
        #       else ->
        #         Log()  +:=  error messages
        #         scan   :=   scan advanced no further than end of line
        #         raise SyntaxError ]
        scanMethod(encounter, scan)

        #-- 7 --
        return encounter

    scanLine = classmethod(scanLine)