text:
import sys
import re
import zipfile
import traceback

from typing import List, Dict, Tuple, Any, Union, Optional  # pylint: disable=unused-import
from wvdTypes import Sector, SectorList

########################################################################
# simple exception class

class ProgramError(Exception):
    def __init__(self, value):
        Exception.__init__(self, value)
        self.value = value
    def __str__(self):
        return repr(self.value)

########################################################################
# this represents top-level information about a wang virtual disk

# pylint: disable=useless-object-inheritance
class WangVirtualDisk(object):

    # creation always produces an invalid disk.
    # currently the only way to get a valid disk is from reading one;
    # one can't be constructed from scratch.
    def __init__(self):
        self._valid: bool = False   # the rest of self.members are meaningless
        self._dirty: bool = False   # contents modified from on-disk version
        self._filename: str = None
        self.sector: SectorList = []
        self.head_dat = bytearray()
        self.catalog = []     # TODO: ttype: List[Catalog] -- enabling this causes tons of warnings
                              # one entry per platter

    # returns True if the in-memory disk image has been modified
    def isDirty(self) -> bool:
        return self._dirty

    # read a virtual disk disk from a file into memory
    def read(self, filename: str) -> None:
        if filename[-4:] == '.zip':
            try:
                zipf = zipfile.ZipFile(filename, 'r')
                firstname = zipf.namelist()[0]
                fh = zipf.open(firstname, 'r')
                fdata = bytearray(fh.read())
            except Exception as e:  # pylint: disable=unused-variable, broad-except
                traceback.print_exc()
                print('Something went wrong opening zip file')
        else:
            fh = open(filename, 'rb')
            fdata = bytearray(fh.read())
            fh.close()
        self._filename = filename
        self.head_dat = fdata[0:256]  # get wvd header
        if self.head_dat[0:5] != b'WANG\x00':
            print('File is not a .wvd type')
            raise IOError
        self.sector = []
        for i in range(self.numPlatters() * self.numSectors()):
            self.sector.append(fdata[256*(i+1) : 256*(i+2)])
            i = i+1
        for p in range(self.numPlatters()):
            self.catalog.append(Catalog(self, p))
        self._valid = True
        self._dirty = False

    # dump the current disk image to the specified file
    def write(self, filename: str) -> None:
        fh = open(filename, 'wb')
        fh.write(self.head_dat)
        for i in range(self.numSectors()):
            fh.write(self.sector[i])
        fh.close()
        self._dirty = False

    # get metadata about the virtual disk
    def getFilename(self) -> str:
        return self._filename
    def getWriteFormat(self) -> int:
        return self.head_dat[5]
    def getReadFormat(self) -> int:
        return self.head_dat[6]
    def getWriteProtect(self) -> int:
        return self.head_dat[7]
    def numSectors(self) -> int:
        return self.head_dat[8] + 256*self.head_dat[9]
    def mediaType(self) -> int:
        return self.head_dat[10]
    def numPlatters(self) -> int:
        return self.head_dat[11] + 1
    def label(self) -> str:
        return (self.head_dat[16:256].decode('latin-1').split('\0'))[0]

    # the first generation disk controller developed by Wang took a 16b
    # sector address, but it ignored the MSB, since at the time no media
    # had > 32K sectors.  for unknown reasons, when a disk was inserted in
    # the R drive of such a controller, the msb of all sector addresses
    # would be set, and those written in the F drive would not be set.
    #
    # later drives had >32K sectors/platter, and could have multiple
    # addressible platters as well.  these "intelligent" disk controllers
    # used all 16 bits of the sector address.
    def getSectorMask(self) -> int:
        if (self.numPlatters() > 1) or (self.numSectors() > 32768):
            return 0xFFFF
        return 0x7FFF

    # set metadata
    # unused
    def setWriteProtect(self, prot: bool) -> None:
        # self.head_dat[7] = (prot != 0)
        if prot:
            protbyte = 0x01
        else:
            protbyte = 0x00
        self.head_dat = self.head_dat[0:7] + bytearray([protbyte]) + self.head_dat[8:256]
        self._dirty = True

    # unused
    def setMediaType(self, kind: int) -> None:
        assert kind in (0, 1, 2)
        self.head_dat[10] = kind
        self._dirty = True

    # accept a string, mapping "|" to line breaks so that multi-line labels can be set
    def setLabel(self, label: str) -> None:
        label = re.sub(r"\|", "\n", label)
        label = label[0:256-16]              # chop off excess
        label += ' ' * (256-16 - len(label)) # pad if required
        self.head_dat[16:256] = bytearray(label.encode('latin-1'))
        self._dirty = True

    def checkSectorAddress(self, p: int, n: int) -> None:
        if p >= self.numPlatters():
            print('Error: attempted to access invalid platter #', p)
            print(self.numPlatters())
            raise ProgramError
        if n >= self.numSectors():
            print('Error: attempted to access invalid sector #', n)
            print(self.numSectors())
            raise ProgramError

    def getRawSector(self, p: int, n: int) -> Sector:
        self.checkSectorAddress(p, n)
        nn = p*self.numSectors() + n
        return self.sector[nn]

    # replace a sector
    def setRawSector(self, p: int, n: int, data: Sector) -> None:
        assert len(data) == 256
        self.checkSectorAddress(p, n)
        nn = p*self.numSectors() + n
        self.sector[nn] = data
        self._dirty = True

########################################################################
# this is a very simple class which is exists mostly to give the mypy type
# checker something to work with.  that is, the wvd filenames are passed
# around as bytearray()s of length 8. other bytearrays are being passed
# around, so this allows us to tag which type we are dealing with.

# pylint: disable=useless-object-inheritance
class WvdFilename(object):
    def __init__(self, filename: bytearray) -> None:
        assert len(filename) == 8
        self._filename = filename
    def get(self) -> bytearray:
        return self._filename
    def asStr(self) -> str:
        '''Return the on-disk filename as a trimmed string.'''
        trimmed = self._filename.decode('latin-1').rstrip()
        return trimmed

########################################################################
# this object represents one slot in the index table of a particular platter.
# the various index attributes can be read out and modified;
# calling the update() method pushes all changes back to the wvd image.

# byte 0 describes the index:
#    bit 0: 1 means valid entry, but file was scratched
#    bit 4: 1 means valid entry
#    bit 5: 1 means invalid; eg, a file was there, but space was recycled,eg
#              SAVE DCF "FOO"         (type = 0x10)
#              SCRATCH F "FOO"        (type = 0x11)
#              SAVE DCF("FOO") "BAR"  ("FOO" type = 0x21, "BAR" type=0x10)
#    the save P and save ! options are not stored in index
#    the number of used sectors is not stored in the header
#
# byte 1 describes the file type
#    0x80 = program
#    0x00 = data
#
# bytes 2:3 point to the first sector of the file
# bytes 4:5 point to the final sector of the file
# bytes 6:7 are unused
#
# I would strongly suspect that the first entry with idxState = 0x00 would
# terminate the search for more files in the sector.
#
# these are private vars of the class:
#    _wvd      = the disk where the image came from
#    _platter  = which platter the catalog is on
#    _indexNum = the disk where the image came from
#    _record   = the 16 byte index entry
#    _dirty    = true if any fields have changed

# pylint: disable=useless-object-inheritance
class WvdIndex(object):
    def __init__(self, wvd: WangVirtualDisk, p: int, n: int, record: bytearray) -> None:
        assert len(record) == 16
        assert p < wvd.numPlatters()
        self._wvd      = wvd     # disk the index belongs to
        self._platter  = p       # platter of disk
        self._indexNum = n       # location in index it is from
        self._record   = record  # the index itself
        self._dirty    = False   # not modified

    # return the record as a 16 byte binary string
    def asBytes(self) -> bytearray:
        return self._record

    def getFilename(self) -> WvdFilename:
        return WvdFilename(self._record[8:16])

    def getIndexState(self) -> str:
        val = self._record[0]
        if val == 0x00:
            return 'empty'
        if val == 0x10:
            return 'valid'
        if val == 0x11:
            return 'scratched'
        # TODO: comletter october 1979 No. 8 has a small article called
        # "2200 Disk Catalog Index Structure". it says 0x21 indicates a
        # formerly scratched file has had its reserved sectors reclaimed.
        # study this more and see how it affects this program.
        if val == 0x21:
            return 'invalid'
        return 'unknown'

    def getIndexStateNumber(self) -> str:
        return '0x%02X' % self._record[0]

    def setIndexState(self, kind: str) -> None:
        val = { 'empty':     0x00,
                'valid':     0x10,
                'invalid':   0x21,
                'scratched': 0x11 }[kind]
        self._record = bytearray([val]) + self._record[1:16]
        self._dirty = True

    def getFileType(self) -> str:
        if self._record[1] == 0x80:
            return "P "
        if self._record[1] == 0x40:
            return "P'"  # 386 program save format
        if self._record[1] == 0x00:
            return "D "
        return '?'

    def getFileTypeNumber(self) -> str:
        return '0x%02X' % self._record[1]

    # unused
#   def setFileType(self, kind: str) -> None:
#       assert kind in ('P ', 'D '), "Bad type in setFileType"
#       val = { 'P ': 0x80, "P'": 0x40, 'D ': 0x00 }[kind]
#       self._record = bytearray([self._record[0], val]) + self._record[2:16]
#       self._dirty = True

    # inspect a program file and determine if it is protected (and what kind)
    # or not returns "normal", "protected", or "scrambled".  if it isn't a
    # program file or the control bytes aren't consistent, "unknown" is
    # returned.
    # pylint: disable=too-many-return-statements
    def programSaveMode(self) -> str:
        if self.getFileType() != 'P ':
            return 'unknown'
        if not self.fileExtentIsPlausible():
            return 'unknown'

        start = self.getFileStart()
        end   = self.getFileEnd()
        used  = self.getFileUsedSectors()
        if used > (end - start + 1):
            return 'unknown'

        mode = 'normal'  # until proven otherwise
        for sec in range(used):
            secData = self._wvd.getRawSector(self._platter, start + sec)

            # FIXME: in the wild there are files where the header block does
            # not indicate protected, yet the sectors are scrambled. also, it
            # is not required that all sectors be scrambled: MVP 3.5 checks
            # each block separately and descrambles as necessary.
            if sec == 0:  # header block
                if (secData[0] & 0xF0) == 0x50:
                    mode = 'protected'
                elif (secData[0] & 0xF0) == 0x40:
                    mode = 'normal'
                else:
                    return 'unknown'
            else:
                # test if scrambled (see MVP OS 3.5 file BPMVP42D)
                if ((secData[0] & 0xD0) == 0x10) and \
                   ((secData[1] & 0x80) == 0x00):
                    return 'scrambled'

        return mode

    # this combines the index state with the file type
    def getFileStatus(self) -> str:
        idxState = self.getIndexState()
        if idxState == 'invalid':
            return  'error' # this index is unused
        if idxState == 'invalid':
            return  'invalid' # this index was used, but is now invalid
        if idxState == 'valid':
            return ' ' + self.getFileType()  # append P or D or P'
        if idxState == 'scratched':
            return 'S' + self.getFileType()  # append P or D or P'
        return idxState  # error message

    def getFileStart(self) -> int:
        if self._wvd.catalog[self._platter].getIndexType() < 2:
            return bytes2toInt(self._record[2:4]) & self._wvd.getSectorMask()
        return bytes3toInt(self._record[2:5])

    def getFileEnd(self) -> int:
        if self._wvd.catalog[self._platter].getIndexType() < 2:
            return bytes2toInt(self._record[4:6]) & self._wvd.getSectorMask()
        return bytes3toInt(self._record[5:8])

    def getFileExtent(self) -> Tuple[int, int]:
        return (self.getFileStart(), self.getFileEnd())

    # returns True if the file extent passes basic sanity checks
    def fileExtentIsPlausible(self) -> bool:
        start = self.getFileStart()
        end   = self.getFileEnd()
        # pylint: disable=chained-comparison
        return (start < end < self._wvd.numSectors()) and \
               (start >= self._wvd.catalog[self._platter].numIndexSectors())

    # unused
#   def setFileExtent(self, start: int, end: int) -> None:
#       assert start >= 0
#       assert start < end
#       if self._wvd.catalog[self._platter].getIndexType() < 2:
#           assert end < (1 << 16)
#           self._record = self._record[0:2] + intToBytes2(start) \
#                        + intToBytes2(end) + self._record[6:16]
#       else:
#           assert end < (1 << 24)
#           self._record = self._record[0:2] + intToBytes3(start) \
#                        + intToBytes3(end) + self._record[8:16]
#       self._dirty = True

    # the last record of a file indicates how many sectors are in use
    def getFileUsedSectors(self) -> int:
        assert self.fileExtentIsPlausible()
        end = self.getFileEnd()
        secData = self._wvd.getRawSector(self._platter, end)
        if self._wvd.catalog[self._platter].getIndexType() < 2:
            used = bytes2toInt(secData[1:3])
        else:
            used = bytes3toInt(secData[1:4])
        return used

    # returns True if the file control record passes basic sanity checks
    def fileControlRecordIsPlausible(self) -> bool:
        if not self.fileExtentIsPlausible():
            return False
        start, end = self.getFileExtent()
        used = self.getFileUsedSectors()
        filetype = self.getFileType()
        valid_prefix: Tuple[int,...]
        if filetype == 'P ':
            valid_prefix = (0x20,)
        elif filetype == 'D ':
            valid_prefix = (0xa0,)
        else:
            valid_prefix = (0x20, 0xa0)  # be generous and allow either
        secData = self._wvd.getRawSector(self._platter, end)
        return (used <= (end-start+1)) and \
               ((secData[0] & 0xF0) in valid_prefix)

    # return how many sectors the file uses, including header, trailer,
    # and control records
    def getFileFreeSectors(self) -> int:
        assert self.fileExtentIsPlausible()
        start, end = self.getFileExtent()
        used = self.getFileUsedSectors()
        free = ((end - start+1) - used) % 65536
        return free

    def isDirty(self) -> bool:
        return self._dirty

    # write the index back to the disk image it came from
    # unused
    def update(self) -> None:
        if self.isDirty():
            self._wvd.catalog[self._platter].setIndexEntry(self._platter, self._indexNum, self._record)
            self._dirty = False

############################## disk catalog ##############################
# this returns information about the information in the catalog of a
# particular platter of a disk.  it is best for a client to access it
# through the wvd.catalog member, so that the wvd can make sure 'catalog'
# is always up to date, even when the disk data is being modified.

# pylint: disable=useless-object-inheritance
class Catalog(object):
    # associate the disk and catalog
    def __init__(self, wvd: WangVirtualDisk, p: int) -> None:
        assert p < wvd.numPlatters()
        self._wvd = wvd
        self._platter = p

    # return True if the given platter of the disk appears to have a catalog
    def hasCatalog(self) -> bool:
        idxType = self.getIndexType()
        # TODO: should we perform some kind of catalog sanity check?
        #       that would be heavyweight and this function might be
        #       called a lot, so we'd want to cache that state.
        return idxType <= 2

    # return a list of all the catalog indices
    def catalogIndices(self) -> List[int]:
        # -1 because the first catalog sector contains only 15 entries
        # as the first slot contains the disk paramter block
        return list(range(16*self.numIndexSectors()-1))

    # given a list of (str) filenames or wildcards and a disk, return
    # a list of all filenames on disk matching the wildcards
    def expandWildcards(self, wcList: List[str]) -> List[WvdFilename]:
        assert self.hasCatalog()

        # get a list of all files on the disk
        #
        # note that if an entry is somehow hashed into the wrong bucket,
        # this routine will pick up the file, but the file may not be
        # "visible" when accessed normally.  That is, if a file entry
        # is in the wrong catalog sector, when this program, or a real 2200,
        # tries to access this misplaced file, it won't find it.
        allFiles = []
        for n in self.catalogIndices():
            indexEntry = self.getIndexEntryByNumber(n)
            if indexEntry:
                if indexEntry.getIndexState() in ['valid', 'scratched']:
                    allFiles.append(indexEntry.getFilename().get())

        # check each entry in the wildcard list for an exact match,
        # then if that fails, see if it is a wildcard and then
        # perform a pattern match
        nameList: List[bytearray] = []
        for pat in wcList:
            if len(pat) > 8:
                print('Filename "%s" is more than 8 characters; ignoring' % pat)
                continue
            fullname = padName(bytearray(pat, 'ascii'))
            # try exact match first
            if (fullname in allFiles) and (fullname not in nameList):
                nameList.append(fullname)
                continue
            # if it is a wildcard, expand it to re form
            if ('*' in pat) or ('?' in pat) or ('.' in pat):
                rePat = re.sub(r'\.', r'\.', pat)    # '.' -> literal char
                rePat = re.sub(r'\*', r'.*', rePat)  # '*' -> any number of chars
                rePat = re.sub(r'\?', r'.',  rePat)  # '?' -> any one char
                rePat += r' *$' # allow trailing spaces
                rePat_bytes = bytes(rePat, 'ascii')
                # loop over all filenames and find any that match
                #matched = False
                for f in allFiles:
                    if re.match(rePat_bytes, f):
                        #matched = True
                        if f not in nameList:
                            nameList.append(f)
                #if not matched:
                #    print('''Pattern "%s" doesn't match any filenames on disk; ignoring''' % pat)
            #else:
            #    print('''Filename "%s" doesn't match any filenames on disk; ignoring''' % pat)

        return [WvdFilename(f) for f in nameList]

    # return a list containing all filenames on disk
    def allFilenames(self) -> List[WvdFilename]:
        assert self.hasCatalog()
        return self.expandWildcards(['*'])

    # there are three types of indices
    def getIndexType(self) -> int:
        secData = self._wvd.getRawSector(self._platter, 0)
        idxType = secData[0] & 0x7F  # sometimes MSB is set -- what is this about?
        #if idxType > 2:
        #    print("Unknown disk index type type 0x%02X; perhaps it is raw sectors?" % idxType)
        return idxType

    def setIndexType(self, newType: int) -> None:
        if newType < 0 or newType > 2:
            print("Attempting to set unknown disk index type", newType)
            raise ProgramError
        secData = self._wvd.getRawSector(self._platter, 0)
        secData = bytearray([newType]) + secData[1:256]
        self._wvd.setRawSector(self._platter, 0, secData)

    # the first N sectors hold index information.  return N.
    def numIndexSectors(self) -> int:
        secData = self._wvd.getRawSector(self._platter, 0)
        if self.getIndexType() == 2:
            return bytes2toInt(secData[1:3])
        return secData[1]

    # return which sector is the last in use by the cataloged files
    def currentEnd(self) -> int:
        secData = self._wvd.getRawSector(self._platter, 0)
        if self.getIndexType() == 2:
            return bytes3toInt(secData[3:6]) - 1
        # msb is set sometimes -- what does it mean?
        return (bytes2toInt(secData[2:4]) & self._wvd.getSectorMask()) - 1

    # return which sector is the last for holding cataloged files
    def endCatalogArea(self) -> int:
        secData = self._wvd.getRawSector(self._platter, 0)
        if self.getIndexType() == 2:
            return bytes3toInt(secData[6:9]) - 1
        # msb is set sometimes -- what does it mean?
        return (bytes2toInt(secData[4:6]) & self._wvd.getSectorMask()) - 1

    # return an index object for the n-th catalog slot,
    # or the index object of the names file.
    # return None if the index is too large.
    def getIndexEntry(self, n: Union[int, WvdFilename]) -> Optional[WvdIndex]:
        assert self.hasCatalog()
        if isinstance(n, WvdFilename):
            return self.getIndexEntryByName(n)
        if isinstance(n, int):
            return self.getIndexEntryByNumber(n)
        raise ProgramError

    # return an index object for the n-th catalog slot.
    # numbering starts at 0
    # return None if the index is too large.
    # the first slot of sector 0 is disk metadata, which is why (n+1) is used.
    def getIndexEntryByNumber(self, n: int) -> Optional[WvdIndex]:
        sec  = (n+1) // 16  # which index sector
        slot = (n+1) %  16  # which slot of that sector
        if sec > self.numIndexSectors():
            return None
        secData = self._wvd.getRawSector(self._platter, sec)
        record = secData[16*slot : 16*(slot+1)]
        return WvdIndex(self._wvd, self._platter, n, record)

    # given a filename, return the corresponding slot struct,
    # or return None, if not found
    def getIndexEntryByName(self, name: WvdFilename) -> Optional[WvdIndex]:
        if False:  # pylint: disable=using-constant-test
            # brute force: don't bother with hash approach
            for n in self.catalogIndices():
                idx = self.getIndexEntryByNumber(n)
                idxState = idx.getIndexState()
                if (idx.getFilename().get() == name.get()) and \
                    (idxState in ['valid', 'scratched']):
                    return idx
            return None # not found
        # use hash to speed up search; important for huge catalogs
        secs = self.numIndexSectors()            # sectors for catalog
        hsh = self.getFilenameHash(name)         # where to start search
        sdir = [-1, 1, 1][self.getIndexType()]   # search direction search
        for delta in range(secs):
            secNum = (hsh + delta*sdir) % secs
            # scan all entries of this sector; sector 0 has only 15 entries
            for slot in range(16 - (secNum == 0)):
                absSlot = 16*secNum + slot - (secNum != 0)
                thisSlot = self.getIndexEntry(absSlot)
                if thisSlot:
                    idxState = thisSlot.getIndexState()
                    if idxState == 'empty':
                        return None  # not found
                    if (thisSlot.getFilename().get() == name.get()) and \
                       (idxState in ['valid', 'scratched']):
                        return thisSlot
        return None  # searched all entries, and not found

    # raise an error if the index is too large
    def setIndexEntry(self, n: int, record: bytearray) -> None:
        assert len(record) == 16
        sec  = (n+1) // 16  # which index sector
        slot = (n+1) % 16   # which slot of that sector
        if sec > self.numIndexSectors():
            raise ProgramError
        secData = self._wvd.getRawSector(self._platter, sec)
        secData = secData[0:16*slot] + record + secData[16*(slot+1):256]
        self._wvd.setRawSector(self._platter, sec, secData)

    def getFile(self, name: WvdFilename) -> Optional['CatalogFile']:
        assert self.hasCatalog()
        if self.getIndexEntry(name) is None:
            return None
        return CatalogFile(self, name)

    # this converts the catalog index between new and old formats.
    # the data files aren't touched; the entries are simply moved around
    # to their new hash locations.
    def convertIndex(self, newStyle: bool) -> None:
        assert self.hasCatalog()

        oldIndexType = self.getIndexType()
        if oldIndexType == 2:
            print("Can't deal with tri-byte catalogs yet")
            return
        if newStyle and (oldIndexType == 1):
            return  # not changing anything
        if not newStyle and (oldIndexType == 0):
            return  # not changing anything

        # build a list of all files on the disk, along with their attributes
        fileList = []
        for name in self.allFilenames():
            indexEntry = self.getIndexEntry(name)
            assert indexEntry is not None
            fileList.append(indexEntry)

        # wipe out the index
        for s in range(self.numIndexSectors()):
            if s == 0:
                self.setIndexType(newStyle)
                secData = self._wvd.getRawSector(self._platter, s)
                secData = secData[0:16] + bytearray(240)
            else:
                secData = bytearray(256)
            self._wvd.setRawSector(self._platter, s, secData)

        # step through all files and add them back in the right place
        for entry in fileList:
            self.addIndexEntry(entry)

    # given an indexInfo struct (name, start, end, etc), add it to the catalog.
    # only the index is added, not the file it is pointing to.
    def addIndexEntry(self, entry: WvdIndex) -> None:
        assert self.hasCatalog()

        if self.getIndexType() == 0:
            sdir = -1  # old index overflows to previous bucket
        else:
            sdir =  1  # new index overflows to next bucket

        # get which sector to try first
        hsh = self.getFilenameHash(entry.getFilename())

        # try to insert it into all sectors in the index until it fits in one
        for delta in range(self.numIndexSectors()):
            secNum = (hsh + delta*sdir) % self.numIndexSectors()
            # print("Trying to put %s into sector %d" % (entry.getFilename(), secNum))
            # scan all entries of this sector; sector has only 15 entries
            for slot in range(16 - (secNum == 0)):
                absSlot = 16*secNum + slot - (secNum != 0)
                thisSlot = self.getIndexEntry(absSlot)
                if thisSlot:
                    if thisSlot.getIndexState() == 'empty':
                        # print("    fit into slot %d" % slot)
                        self.setIndexEntry(absSlot, entry.asBytes())
                        return

        print("Error: couldn't find a slot to fit file %s" % entry.getFilename())
        sys.exit()

    # return which sector the filename hashes to for the given disk
    # this is where to start looking; if the bucket is full, the
    # adjacent sectors may need to be searched too
    def getFilenameHash(self, filename: WvdFilename) -> int:
        if self.getIndexType() == 0:
            return self.oldIndexHash(filename)
        return self.newIndexHash(filename)

    # this code is based on code from ISS release 5.1, program "ISS.229S"
    @staticmethod
    def oldIndexHash(filename: WvdFilename) -> int:
        hash_val = 0x00
        for c in filename.get():
            hash_val ^= c
        hash_val *= 3
        hash_val = (hash_val//256) + (hash_val % 256)   # fold over carries
        return hash_val

    # this code is based on information from the OS 2.5 release notes
    @staticmethod
    def newIndexHash(filename: WvdFilename) -> int:
        rawbytes = filename.get()
        hash_val = 0x00
        for i in range(8):
            c = rawbytes[i]
            if i % 2 == 0:
                hash_val += 16*(c & 0xf) + (c >> 4)  # swap nibbles
            else:
                hash_val += c
        return hash_val % 256

    # return True if the given catalog index bucket is full for sector 'secnum'
    # it is a bit fiddly because sector 0 has indexes 0..14, sec 1 has 15..30
    def isIndexBucketFull(self, secnum: int) -> bool:
        if secnum == 0:
            first, last = 0, 14
        else:
            first, last = (16*secnum-1), (16*secnum + 14)
        for n in range(first, last+1):
            indexEntry = self.getIndexEntryByNumber(n)
            if (indexEntry is not None) and \
               (indexEntry.getIndexState() == 'empty'):
                return False
        return True

############################ CatalogFile Object ############################
# these objects return status about a file in a catalog.
# if the disk state is changed, the object may become stale.

# pylint: disable=useless-object-inheritance
class CatalogFile(object):
    # given a filename, return a CatalogFile object
    def __init__(self, cat: Catalog, filename: WvdFilename) -> None:
        self._name    = filename
        self._cat     = cat
        self._wvd     = cat._wvd      # pylint: disable=protected-access; # this grubbing is pretty ugly
        self._platter = cat._platter  # pylint: disable=protected-access; # this grubbing is pretty ugly
        self._index   = cat.getIndexEntry(filename)
        self._valid   = (self._index is not None)

    def getFilename(self) -> WvdFilename:
        return self._name
    def getType(self) -> str:
        if self._valid:
            assert self._index is not None
            return self._index.getFileType()
        return '?'
    def programSaveMode(self) -> str:
        if self._valid:
            assert self._index is not None
            return self._index.programSaveMode()
        return 'unknown'
    def getStatus(self) -> str:
        if self._valid:
            assert self._index is not None
            return self._index.getFileStatus()
        return 'invalid'
    def getStart(self) -> int:
        if self._valid:
            assert self._index is not None
            return self._index.getFileStart()
        return 0
    def getEnd(self) -> int:
        if self._valid:
            assert self._index is not None
            return self._index.getFileEnd()
        return 0
    def getExtent(self) -> Tuple[int, int]:
        if self._valid:
            assert self._index is not None
            return self._index.getFileExtent()
        return (0,0)
    def getUsed(self) -> int:
        if self._valid:
            assert self._index is not None
            return self._index.getFileUsedSectors()
        return 0
    def getFree(self) -> int:
        if self._valid:
            assert self._index is not None
            return self._index.getFileFreeSectors()
        return 0
    def fileExtentIsPlausible(self) -> bool:
        if self._valid:
            assert self._index is not None
            return self._index.fileExtentIsPlausible()
        return False
    def fileControlRecordIsPlausible(self) -> bool:
        if self._valid:
            assert self._index is not None
            return self._index.fileControlRecordIsPlausible()
        return False

    # set or clear the protection mode on the file.
    # the file must be a program file.
    def setProtection(self, protect: bool) -> None:
        start, end = self.getExtent()
        # we don't touch the final allocated block
        # we only have to touch the used blocks, not all the allocated blocks
        for s in range(start, end):
            secData = self._wvd.getRawSector(self._platter, s)
            if protect:
                # set protection bit
                secData = bytearray([secData[0] | 0x10]) + secData[1:256]
            else:
                # clear protection bit
                secData = bytearray([secData[0] & 0xEF]) + secData[1:256]
            self._wvd.setRawSector(self._platter, s, secData)

    # extract sectors of named file
    # given a file name (program or data), return a list containing the sector
    # data, but only of the used sectors, not all the allocated sectors.
    # return None if the filename isn't found, or if the file extent
    # information is bogus (in which case we report it)
    # pylint: disable=too-many-return-statements
    def getSectors(self) -> Optional[SectorList]:
        if self._index is None:
            return None
        if self._index.getIndexState() != 'valid':
            return None
        typ = self.getType()
        if typ == '?':
            return None
        start = self.getStart()
        end   = self.getEnd()
        if end < start:
            print("'%s': bad file extent information" % self._name.asStr())
            return None
        if end >= self._wvd.numSectors():
            print("'%s': bad file extent information" % self._name.asStr())
            return None
        used = self.getUsed()
        sectors: SectorList = []
        if not self.fileControlRecordIsPlausible():
            print("'%s': implausible file control record" % self._name.asStr())
            return None
        # used-1 because one sector is used for holding the used sector count
        for sec in range(start, start+used-1):
            secData = self._wvd.getRawSector(self._platter, sec)
            sectors.append(secData)
        return sectors

    # rewrite sectors of a file
    def setSectors(self, blocks: SectorList) -> None:
        assert self._index is not None
        assert self._index.getIndexState() == 'valid'
        assert self.getType() == 'P '
        start = self.getStart()
        end   = self.getEnd()
        if end < start:
            print("'%s': bad file extent information" % self._name.asStr())
            return
        if end >= self._wvd.numSectors():
            print("'%s': bad file extent information" % self._name.asStr())
            return
        if not self.fileControlRecordIsPlausible():
            print("'%s': implausible file control record" % self._name.asStr())
            return
        used = self.getUsed()
        # used-1 because one sector is used for holding the used sector count
        for sec in range(start, start+used-1):
            if sec-start > len(blocks): break
            self._wvd.setRawSector(self._platter, sec, blocks[sec-start])
        return

############################## helpers ##############################

def bytes3toInt(data: bytearray) -> int:
    assert len(data) == 3
    return 65536*data[0] + 256*data[1] + data[2]

def bytes2toInt(data: bytearray) -> int:
    assert len(data) == 2
    return 256*data[0] + data[1]

def intToBytes2(val: int) -> bytearray:
    return bytearray([val // 256, val % 256])

def intToBytes3(val: int) -> bytearray:
    return bytearray([val // 65536, (val // 256) % 256, val % 256])

# return True if the two nibbles are bcd
def valid_bcd_byte(ch: int) -> bool:
    low  = ch & 0x0F
    high = ch & 0xF0
    return (low <= 0x09) and (high <= 0x90)

def padName(name: bytearray) -> bytearray:
    '''Takes a bytearray filename string and pads it to 8 spaces.'''
    assert len(name) < 9
    name += bytearray([ord(' ')]) * (8 - len(name))
    return name

########################################################################
# given a header record, return the filename it contains.
# NB: disk headers pad names shorter than 8 characters with 0x20
#     tape headers pad names shorter than 8 characters with 0xFF
def headerName(sector: bytearray) -> str:
    rawname = sector[1:9]
    return sanitize_filename(rawname)

# NB: disk headers pad names shorter than 8 characters with 0x20
#     tape headers pad names shorter than 8 characters with 0xFF
def sanitize_filename(rawname: Any) -> str:
    if isinstance(rawname, str):
        byts = bytearray(rawname.encode('latin-1'))
    else:
        byts = bytearray(rawname)
    while byts and (byts[-1] in (0xff, ord(' '))):
        byts = byts[:-1]
    name = [chr(byt) if (32 <= byt < 128) else ("\\%02X" % byt) for byt in byts]
    return ''.join(name)

########################################################################
# unscramble a single sector of data. if the incoming data isn't scrambled,
# this routine will mess it up. the incoming data is unaffected and the
# function returns a new block.
#
# the algorithm is extracted from the DCRYPT routine of Wang MVP OS 3.5.
# this function doesn't implement the tweak for "wrap mode" files.

def unscramble_one_sector(block: Sector) -> Sector:
    assert len(block) == 256
    sector = bytearray(block)  # make a copy

    byte0 = sector[0]
    byte1 = sector[1]

    # The pass 1 key is in byte 5, 25, 45, or 65
    # depending on the two middle coded bits of the 2nd byte
    offset = (byte1 & 0x60) + 5
    key = sector[offset]

    sector[0] = (byte0 & 0xF0) | ((byte1 & 0xF0) >> 4)       # new control byte
    sector[1] = key                                          # save key
    sector[offset] = ((byte0 & 0x0F) << 4) | (byte1 & 0x0F)  # replace key byte

    ### DECRYPT PASS 2

    offset = 0x100-0x22     # point to pass 2 seed
    endidx = offset         # terminate the loop at the seed location
    pair = (sector[offset] << 8) | sector[offset + 1]  # pass 2 seed

    # the starting location for loop
    offset = (offset + 26)   # 248 = 0xF8

    while offset != endidx:
        # rotate previous pair left one bit, then swap the middle nibbles
        seed = ((pair << 1) | (pair >> 15)) & 0xFFFF
        nibs = (seed & 0xF00F) | ((seed & 0x00F0) << 4) | ((seed & 0x0F00) >> 4)

        # fetch next pair of bytes
        pair = (sector[offset] << 8) | sector[offset + 1]

        # transform bytes and write them out
        tmp = (pair + (0xffff ^ nibs))   # or tmp = (pair - nibs - 1)
        sector[offset]     = (tmp >> 8) & 0xff
        sector[offset + 1] = (tmp     ) & 0xff

        # increment and wrap, skipping bytes 0 & 1
        offset += 26
        if offset >= 256:
            offset = (offset + 2) & 0xff

    ### DECRYPT PASS 1

    offset = 0x22         # starting offset for loop
    endidx = 0            # stop when we get here
    pair   = sector[1]    # pass 1 seed

    while offset != endidx:
        # rotate previous pair left one bit, then swap the middle nibbles
        seed = ((pair << 1) | (pair >> 15)) & 0xFFFF
        nibs = (seed & 0xF00F) | ((seed & 0x00F0) << 4) | ((seed & 0x0F00) >> 4)

        # fetch next pair of bytes
        pair = (sector[offset] << 8) | sector[offset + 1]

        # transform bytes and write them out
        tmp = (pair + (0xffff ^ nibs))   # or tmp = (pair - nibs - 1)
        sector[offset]     = (tmp >> 8) & 0xff
        sector[offset + 1] = (tmp     ) & 0xff

        # increment and wrap
        offset = (offset + 34) & 0xFF

    # reconstruct first two bytes
    byte0 = sector[0]
    sector[0] = byte0 & 0xF0            # high half only
    sector[1] = 0xFE | (byte0 & 0x01)   # FE or FF

    return sector


text:
.. _usage_installation:

Grab Installaion
================

.. _installation_linux:

Installation on Linux
--------------------------

Run the command:

.. code:: shell

    pip install -U Grab

This command will install Grab and all dependencies. Be aware that you need
to have some libraries installed in your system to successfully build lxml and
pycurl dependencies.

To build pycurl successfully you need to install:

.. code:: shell

    apt-get install libcurl4-openssl-dev

To build lxml successfully you need to install:

.. code:: shell

    apt-get install libxml2-dev libxslt-dev

.. _installation_windows:

Installation on Windows
-----------------------

1) Install lxml. You can get lxml here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml

2) Install pycurl. You can get pycurl here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl

3) Install Grab

Download the Grab package from https://pypi.python.org/pypi/grab, unpack it and run::

    python setup.py install

If you use Python 2.x, then you might get an error while using `python setup.py install`. There is a bug in python 2.7.6. Delete it, download python 2.7.5 and try to install Grab again.

You can also install Grab via pip. If you don't have pip installed, install pip first. Download the file get-pip.py from https://bootstrap.pypa.io/get-pip.py and then run this command::

    python get-pip.py

Now you can install Grab via pip with this command::

    python -m pip install grab

.. _installation_freebsd:

Installation on FreeBSD
-----------------------

Run the command:

.. code:: shell

    pip install -U Grab

You can also install Grab from FreeBSD ports (thanks to Ruslan Makhmatkhanov):

* To install the port: cd /usr/ports/devel/py-grab/ && make install clean
* To add the package: pkg_add -r py27-grab

.. _installation_macos:

Installation on MacOS
---------------------

Run the command:

.. code:: shell

    pip install -U Grab

.. _installation_deps:

Dependencies
------------

All required dependencies should be installed automatically if you
install Grab with pip. Here is actual list of Grab dependencies::

    lxml
    pycurl
    selection
    weblib
    six

.. _installation_upgrade:

Upgrade Grab from 0.5.x version to 0.6.x
------------------------------------

In Grab 0.6.x some features were moved out into separate packages. If
you install/upgrade Grab with pip command all dependencies should
be installed automatically. Anyway, if you have some ImportError issues
then try to install dependencies manually with the command:

.. code:: shell

    pip install -U lxml pycurl selection weblib six

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<!--

-->
<plugin>
	<extension point="org.eclipse.wst.server.core.runtimeTypes">
		<runtimeType id="org.cloudfoundry.appcloudserver.runtime.10"
			version="1.0"
			name="Cloud Foundry"
			description="Cloud Foundry supports J2EE Web modules."
			vendor="Cloud Foundry"
			class="org.eclipse.cft.server.core.internal.CloudFoundryServerRuntime">
			<moduleType types="wst.web" versions="1.0" />
			<moduleType types="jst.web" versions="2.2,2.3,2.4,2.5,3.0,3.1" />
			<moduleType types="jst.utility" versions="1.0"/>
            <moduleType types="cloudfoundry.standalone.app" versions="1.0"/>
		</runtimeType>
	</extension>

	<extension point="org.eclipse.wst.server.core.serverTypes">
		<serverType
	        behaviourClass="org.eclipse.cft.server.core.internal.client.CloudFoundryServerBehaviour"
	        class="org.eclipse.cft.server.core.internal.CloudFoundryServer"
	        description="Publishes and runs J2EE Web projects to Cloud Foundry."
	        hasConfiguration="false"
	        id="org.cloudfoundry.appcloudserver.10"
	        initialState="stopped"
	        name="Cloud Foundry"
	        runtime="true"
	        supportsRemoteHosts="true"
	        runtimeTypeId="org.cloudfoundry.appcloudserver.runtime.10"
	        startTimeout="600000"
	        stopTimeout="60000">
		</serverType>
	</extension>
	<extension point="org.eclipse.wst.common.project.facet.core.defaultFacets">
    	<default-facets>
      		<runtime-component id="org.cloudfoundry.appcloudserver"/>
        	<facet id="wst.jsdt.web" version="1.0"/>
    	</default-facets>
  	</extension>
  	<extension point="org.eclipse.wst.common.project.facet.core.runtimes">
		<runtime-component-type id="org.cloudfoundry.appcloudserver" />
		<runtime-component-version type="org.cloudfoundry.appcloudserver" version="1.0" />

		<adapter>
			<runtime-component id="org.cloudfoundry.appcloudserver" />
			<factory class="org.eclipse.jst.server.core.internal.RuntimeClasspathProvider$Factory" />
			<type class="org.eclipse.jst.common.project.facet.core.IClasspathProvider" />
		</adapter>
		<adapter>
	      <runtime-component id="org.cloudfoundry.appcloudserver"/>
	      <factory class="org.eclipse.wst.server.ui.FacetRuntimeComponentLabelProvider"/>
	      <type class="org.eclipse.wst.common.project.facet.ui.IRuntimeComponentLabelProvider"/>
	    </adapter>
		<supported>
			<runtime-component id="org.cloudfoundry.appcloudserver" version="1.0" />
			<facet id="jst.web" version="2.2,2.3,2.4,2.5,3.0,3.1" />
			<facet id="jst.utility" version="1.0"/>
			<facet id="cloudfoundry.standalone.app" version="1.0"/>
    	</supported>
	</extension>
	<extension point="org.eclipse.jst.server.core.runtimeFacetMappings">
		<runtimeFacetMapping
			runtimeTypeId="org.cloudfoundry.appcloudserver.runtime.10"
			runtime-component="org.cloudfoundry.appcloudserver" version="1.0" />
	</extension>
	<extension point="org.eclipse.jst.server.core.runtimeClasspathProviders">
	     <runtimeClasspathProvider
	        id="org.cloudfoundry.appcloudserver.classPathProvider"
	        runtimeTypeIds="org.cloudfoundry.appcloudserver.runtime.10"
	        class="org.eclipse.cft.server.core.internal.CloudFoundryRuntimeClasspathProvider"/>
     </extension>
	 <extension
       point="org.eclipse.cft.server.core.branding">
	    <service
           name="Cloud Foundry"
           provideServices="true"
           remoteSystemTypeId="org.cloudfoundry.cloudfoundryserver"
           serverDisplayName="Cloud Foundry"
           serverTypeId="org.cloudfoundry.appcloudserver.10"
           supportUpperCaseURL="false">
    </service>
     </extension>

</plugin>


text:
package com.alibaba.dubbo.monitor.support;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.monitor.Monitor;
import com.alibaba.dubbo.monitor.MonitorFactory;

/**
 * AbstractMonitorFactroy. (SPI, Singleton, ThreadSafe)
 *
 * @author william.liangf
 */
public abstract class AbstractMonitorFactroy implements MonitorFactory {

    // 注册中心获取过程锁
    private static final ReentrantLock LOCK = new ReentrantLock();

    // 注册中心集合 Map<RegistryAddress, Registry>
    private static final Map<String, Monitor> MONITORS = new ConcurrentHashMap<String, Monitor>();

    public static Collection<Monitor> getMonitors() {
        return Collections.unmodifiableCollection(MONITORS.values());
    }

    public Monitor getMonitor(URL url) {
        LOCK.lock();
        try {
            String uri = url.toIdentityString();
            Monitor monitor = MONITORS.get(uri);
            if (monitor != null) {
                return monitor;
            }
            monitor = createMonitor(url);
            if (monitor == null) {
                throw new IllegalStateException("Can not create monitor " + url);
            }
            MONITORS.put(uri, monitor);
            return monitor;
        } finally {
            // 释放锁
            LOCK.unlock();
        }
    }

    protected abstract Monitor createMonitor(URL url);

}


text:
import fs from 'fs'
import path from 'path'
import {generateHelpUrl} from '@sanity/generate-help-url'
import flattenTree from './flattenTree'
import readManifest from './readManifest'
import {resolvePlugins, resolvePlugin} from './resolvePlugins'
import resolveSanityRoot from './resolveProjectRoot'
import removeDuplicatePlugins from './removeDuplicatePlugins'

export default resolveTree
export const resolveProjectRoot = resolveSanityRoot
export {resolvePlugin}

export function resolveParts(opts = {}) {
  const options = Object.assign({basePath: process.cwd()}, opts)

  if (options.sync) {
    return mergeResult(resolveTree(options), options)
  }

  return resolveTree(options).then((plugins) => mergeResult(plugins, options))
}

function resolveTreeSync(options) {
  const basePath = options.basePath || process.cwd()
  const manifest = readManifest(options)
  const plugins = resolvePlugins(manifest.plugins || [], options).concat([
    getProjectRootPlugin(basePath, manifest),
  ])

  return plugins.reduce(flattenTree, plugins.slice())
}

async function resolveTreeAsync(options) {
  const projectManifest = await readManifest(options)
  const plugins = await resolvePlugins(projectManifest.plugins || [], options)
  const withRoot = plugins.concat([getProjectRootPlugin(options.basePath, projectManifest)])
  const flattened = withRoot.reduce(flattenTree, withRoot)
  const deduped = removeDuplicatePlugins(flattened)
  return deduped
}

function resolveTree(opts = {}) {
  const options = Object.assign({basePath: process.cwd()}, opts)

  if (options.resolveProjectRoot) {
    // @todo Sync vs async
    const resolveOpts = Object.assign({}, options, {sync: true})
    options.basePath = resolveSanityRoot(resolveOpts)
  }

  return options.sync ? resolveTreeSync(options) : resolveTreeAsync(options)
}

function getProjectRootPlugin(basePath, manifest) {
  return {
    name: '(project root)',
    path: basePath,
    manifest: manifest,
    plugins: [],
  }
}

function mergeResult(plugins, options = {}) {
  const definitions = {}
  const implementations = {}
  const result = {definitions, implementations, plugins}

  // Find plugins that define parts, and do a basic validation on the syntax
  const partPlugins = plugins
    .map((plugin) => {
      if (!plugin.manifest.parts) {
        return false
      }

      if (!Array.isArray(plugin.manifest.parts)) {
        const help = `See ${generateHelpUrl('plugin-parts-syntax')}`
        throw new Error(
          `Plugin "${plugin.name}" has a "parts" property which is not an array\n${help}`
        )
      }

      return {
        parts: plugin.manifest.parts,
        plugin: plugin,
      }
    })
    .filter(Boolean)
    .reverse()

  partPlugins.forEach(({parts, plugin}) => {
    parts.forEach((part) => {
      if (part.name && part.path) {
        assignNonOverridablePart(plugin, part, implementations, definitions, options)
      } else if (part.name) {
        assignDefinitionForAbstractPart(plugin, part, definitions)
      }

      if (part.implements) {
        assignPartImplementation(plugin, part, implementations, definitions, options)
      }
    })
  })

  return result
}

function assignNonOverridablePart(plugin, part, implementations, definitions, options) {
  // Actual, non-overridable part
  const prevDefinition = definitions[part.name]
  if (prevDefinition) {
    // Part already exists, non-overridable parts can't be redefined
    const existing = `"${prevDefinition.plugin}" (${prevDefinition.path})`
    const current = `"${plugin.name}" (${plugin.path})`
    throw new Error(
      `${
        `Plugins ${existing} and ${current} both define part "${part.name}"` +
        ' - did you mean to use "implements"?\n' +
        'See '
      }${generateHelpUrl('part-declare-vs-implement')}`
    )
  }

  definitions[part.name] = getDefinitionDeclaration(plugin, part)
  implementations[part.name] = [getImplementationDeclaration(plugin, part, options)]
}

function assignDefinitionForAbstractPart(plugin, part, definitions) {
  const prevDefinition = definitions[part.name]
  if (prevDefinition && !prevDefinition.loose) {
    // Part already exists, non-overridable parts can't be redefined
    const existing = `"${prevDefinition.plugin}" (${prevDefinition.path})`
    const current = `"${plugin.name}" (${plugin.path})`
    throw new Error(
      `${
        `Plugins ${existing} and ${current} both define part "${part.name}"` +
        ' - did you mean to use "implements"?\n' +
        'See '
      }${generateHelpUrl('part-declare-vs-implement')}`
    )
  }

  definitions[part.name] = getDefinitionDeclaration(plugin, part)
}

function assignPartImplementation(plugin, part, implementations, definitions, options) {
  const partName = part.implements
  if (!part.path) {
    const current = `"${plugin.name}" (${plugin.path})`
    throw new Error(
      `${
        `Plugin ${current} tries to implement a part "${partName}",` +
        ' but did not define a path. Did you mean to use "name"?\n' +
        'See '
      }${generateHelpUrl('part-declare-vs-implement')}`
    )
  }

  const prevDefinition = definitions[partName]
  if (prevDefinition && !prevDefinition.isAbstract) {
    const existing = `"${prevDefinition.plugin}" (${prevDefinition.path})`
    const current = `"${plugin.name}" (${plugin.path})`
    throw new Error(
      `${
        `Plugin ${current} tried to implement part "${partName}", which is already declared` +
        ` as a non-overridable part by ${existing} - ` +
        'See '
      }${generateHelpUrl('implement-non-overridable-part')}`
    )
  } else if (!prevDefinition) {
    // In some cases, a user might want to declare a new part name and
    // assign it a non-overridable implementation, while simulatenously
    // fulfilling an existing part using `implements`. In this case,
    // `name`, `implements` and `path` are all set, and we want the part
    // referenced in `implements` to be treated as a non-abstract part.
    // This is why we're explicitly setting `isAbstract` to true below
    // `loose` means that this declaration is "implicit" - the part isn't
    // defined as a `name` + `description` combination, so if we come across
    // a plugin that declares the part outright, we want to use that over this
    definitions[partName] = getDefinitionDeclaration(plugin, part, {
      isAbstract: true,
      loose: true,
    })
  }

  if (!implementations[partName]) {
    implementations[partName] = []
  }

  implementations[partName].push(getImplementationDeclaration(plugin, part, options))
}

function getDefinitionDeclaration(plugin, part, options = {}) {
  const isAbstract =
    typeof options.isAbstract === 'undefined'
      ? typeof part.path === 'undefined'
      : options.isAbstract

  return {
    plugin: plugin.name,
    path: plugin.path,
    description: part.description,
    isAbstract: isAbstract,
    loose: options.loose,
  }
}

function getImplementationDeclaration(plugin, part, options) {
  const paths = plugin.manifest.paths || {}

  let pluginPath = plugin.path

  if (options.isSanityMonorepo) {
    pluginPath = tryResolvePath(pluginPath)
  }

  const isLib = pluginPath.split(path.sep).indexOf('node_modules') !== -1
  const isDotPath = /^\.{1,2}[\\/]/.test(part.path)
  const useCompiled = options.useCompiledPaths || isLib

  const basePath = isDotPath
    ? pluginPath
    : path.join(pluginPath, (useCompiled ? paths.compiled : paths.source) || '')

  const filePath = path.isAbsolute(part.path)
    ? part.path
    : path.resolve(path.join(basePath, part.path))

  return {
    plugin: plugin.name,
    path: filePath,
  }
}

function tryResolvePath(dstPath) {
  try {
    return fs.realpathSync(dstPath)
  } catch (err) {
    return dstPath
  }
}


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>Contaminated Sites Map - Planned Outage</title>

Contaminated Sites Database Map Viewer

Planned Outage

                The Contaminated Sites Database Map Viewer is currently offline for planned maintenance.

The system will be down from 10 am on Sunday 10 April 2016 until 7am on Monday 11 April 2016.

During this time the system will be unavailable.

We apologise for any inconvenience.


text:
DELETE FROM reindex_aoco_gist  WHERE id < 128;
1: BEGIN;
1: REINDEX index idx_gist_reindex_aoco;
2&: TRUNCATE TABLE reindex_aoco_gist;
1: COMMIT;
2<:
2: COMMIT;
3: SELECT COUNT(*) FROM reindex_aoco_gist WHERE id = 1500;
3: SELECT 1 AS relfilenode_same_on_all_segs from gp_dist_random('pg_class')   WHERE relname = 'idx_gist_reindex_aoco' GROUP BY relfilenode having count(*) = (SELECT count(*) FROM gp_segment_configuration WHERE role='p' AND content > -1);


text:
<?php

namespace {

from('Hoa')

/**
 * \Hoa\Test\Praspel\Variable
 */
-> import('Test.Praspel.Variable');

}

namespace Hoa\Test\Praspel\Constructor {

/**
 * Class \Hoa\Test\Praspel\Constructor\Old.
 *
 * Represents the \old(e) constructor, the value of e in pre-state.
 *
 * @author     Ivan Enderlin <ivan.enderlin@hoa-project.net>
 * @copyright  Copyright © 2007-2012 Ivan Enderlin.
 * @license    New BSD License
 */

class Old extends \Hoa\Test\Praspel\Variable { }

}


text:
package innovimax.mixthem.rest;

import innovimax.mixthem.rest.request.CheckFactory;
import innovimax.mixthem.rest.request.FileParamsCheck;
import innovimax.mixthem.rest.request.TextParamsCheck;

import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition.FormDataContentDispositionBuilder;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class RequestTest {

    private int test;
    private TextParamsCheck textCheck;
    private FileParamsCheck fileCheck;
    private FormDataContentDisposition emptyDetail;
    private FormDataContentDisposition filledDetail;

    @Before
    public void setUp() {
        this.test = 1;
        this.textCheck = CheckFactory.getTextParamsCheck();
        this.fileCheck = CheckFactory.getFileParamsCheck();
        FormDataContentDispositionBuilder builder = FormDataContentDisposition.name("test");
        builder.fileName("");
        this.emptyDetail = builder.build();
        builder.fileName("test.txt");
        this.filledDetail = builder.build();
    }

    private final boolean incTest(boolean result) {
        this.test++;
        return result;
    }

    @Test
    public final void testTextParamsPass() {
        boolean result = true;
        result &= incTest(this.textCheck.addFromText("text1", "text2"));
        result &= incTest(this.textCheck.altFromText("char", "text1", "text2"));
        result &= incTest(this.textCheck.altFromText("line", "text1", "text2"));
        result &= incTest(this.textCheck.joinFromText("text1", "text2", null, null));
        result &= incTest(this.textCheck.zipFromText("char", "text1", "text2", null));
        result &= incTest(this.textCheck.zipFromText("line", "text1", "text2", null));
        result &= incTest(this.textCheck.zipFromText("cell", "text1", "text2", null));
        Assert.assertTrue("Test " + this.test, result);
    }

    @Test
    public final void testTextParamsFail() {
        boolean result = true;
        result &= incTest(this.textCheck.addFromText("", "text2"));
        result &= incTest(this.textCheck.addFromText("text1", ""));
        result &= incTest(this.textCheck.altFromText("char", "", "text2"));
        result &= incTest(this.textCheck.altFromText("char", "text1", ""));
        result &= incTest(this.textCheck.altFromText("none", "text1", "text2"));
        result &= incTest(this.textCheck.randomAltFromText("", "text2", null));
        result &= incTest(this.textCheck.randomAltFromText("text1", "", null));
        result &= incTest(this.textCheck.joinFromText("", "text2", null, null));
        result &= incTest(this.textCheck.joinFromText("text1", "", null, null));
        result &= incTest(this.textCheck.zipFromText("char", "", "text2", null));
        result &= incTest(this.textCheck.zipFromText("char", "text1", "", null));
        result &= incTest(this.textCheck.zipFromText("none", "text1", "text2", null));
        Assert.assertFalse("Test " + this.test, result);
    }

    @Test
    public final void testFileParamsPass() {
        boolean result = true;
        result &= incTest(this.fileCheck.addFromFile(null, this.filledDetail, null, this.filledDetail));
        result &= incTest(this.fileCheck.altFromFile("char", null, this.filledDetail, null, this.filledDetail));
        result &= incTest(this.fileCheck.altFromFile("line", null, this.filledDetail, null, this.filledDetail));
        result &= incTest(this.fileCheck.joinFromFile(null, this.filledDetail, null, this.filledDetail, null, null));
        result &= incTest(this.fileCheck.zipFromFile("char", null, this.filledDetail, null, this.filledDetail, null));
        result &= incTest(this.fileCheck.zipFromFile("line", null, this.filledDetail, null, this.filledDetail, null));
        result &= incTest(this.fileCheck.zipFromFile("cell", null, this.filledDetail, null, this.filledDetail, null));
        Assert.assertTrue("Test " + this.test, result);
    }

    @Test
    public final void testFileParamsFail() {
        boolean result = true;
        result &= incTest(this.fileCheck.addFromFile(null, this.emptyDetail, null, this.filledDetail));
        result &= incTest(this.fileCheck.addFromFile(null, this.filledDetail, null, this.emptyDetail));
        result &= incTest(this.fileCheck.altFromFile("char", null, this.emptyDetail, null, this.filledDetail));
        result &= incTest(this.fileCheck.altFromFile("char", null, this.filledDetail, null, this.emptyDetail));
        result &= incTest(this.fileCheck.altFromFile("none", null, this.filledDetail, null, this.filledDetail));
        result &= incTest(this.fileCheck.randomAltFromFile(null, this.emptyDetail, null, this.filledDetail, null));
        result &= incTest(this.fileCheck.randomAltFromFile(null, this.filledDetail, null, this.emptyDetail, null));
        result &= incTest(this.fileCheck.joinFromFile(null, this.emptyDetail, null, this.filledDetail, null, null));
        result &= incTest(this.fileCheck.joinFromFile(null, this.filledDetail, null, this.emptyDetail, null, null));
        result &= incTest(this.fileCheck.zipFromFile("char", null, this.emptyDetail, null, this.filledDetail, null));
        result &= incTest(this.fileCheck.zipFromFile("char", null, this.filledDetail, null, this.emptyDetail, null));
        result &= incTest(this.fileCheck.zipFromFile("none", null, this.filledDetail, null, this.filledDetail, null));
        Assert.assertFalse("Test " + this.test, result);
    }

}


text:
'use strict';

const colors = require('ansi-colors');
const hSelect = require('./hselect');

class hMultiSelect extends hSelect {
  constructor(options) {
    super({ ...options, multiple: true });
  }

  async choiceMessage(choice, i) {
    let msg = await super.choiceMessage(choice, i);
    if (choice.enabled) {
      msg = this.styles.success(colors.unstyle(msg));
      if (this.index === i) msg = colors.underline(msg);
    }
    return msg;
  }
}

module.exports = hMultiSelect;


text:
module Azure::CognitiveServices::Autosuggest::V1_0
  module Models
    #
    # Defines a response. All schemas that could be returned at the root of a
    # response should inherit from this
    #
    class Response < Identifiable

      include MsRestAzure

      def initialize
        @_type = "Response"
      end

      attr_accessor :_type

      # @return [String] The URL that returns this resource.
      attr_accessor :read_link

      # @return [String] The URL To Bing's search result for this item.
      attr_accessor :web_search_url

      # @return [Array<Action>]
      attr_accessor :potential_action

      # @return [Array<Action>]
      attr_accessor :immediate_action

      # @return [String]
      attr_accessor :preferred_clickthrough_url

      # @return [String]
      attr_accessor :adaptive_card

      #
      # Mapper for Response class as Ruby Hash.
      # This will be used for serialization/deserialization.
      #
      def self.mapper()
        {
          client_side_validation: true,
          required: false,
          serialized_name: 'Response',
          type: {
            name: 'Composite',
            class_name: 'Response',
            model_properties: {
              _type: {
                client_side_validation: true,
                required: true,
                serialized_name: '_type',
                type: {
                  name: 'String'
                }
              },
              id: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'id',
                type: {
                  name: 'String'
                }
              },
              read_link: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'readLink',
                type: {
                  name: 'String'
                }
              },
              web_search_url: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'webSearchUrl',
                type: {
                  name: 'String'
                }
              },
              potential_action: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'potentialAction',
                type: {
                  name: 'Sequence',
                  element: {
                      client_side_validation: true,
                      required: false,
                      serialized_name: 'ActionElementType',
                      type: {
                        name: 'Composite',
                        class_name: 'Action'
                      }
                  }
                }
              },
              immediate_action: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'immediateAction',
                type: {
                  name: 'Sequence',
                  element: {
                      client_side_validation: true,
                      required: false,
                      serialized_name: 'ActionElementType',
                      type: {
                        name: 'Composite',
                        class_name: 'Action'
                      }
                  }
                }
              },
              preferred_clickthrough_url: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'preferredClickthroughUrl',
                type: {
                  name: 'String'
                }
              },
              adaptive_card: {
                client_side_validation: true,
                required: false,
                read_only: true,
                serialized_name: 'adaptiveCard',
                type: {
                  name: 'String'
                }
              }
            }
          }
        }
      end
    end
  end
end


text:
require 'spec_helper'
require 'puppet/resource/type'

require 'matchers/json'

describe Puppet::Resource::Type do
  include JSONMatchers

  it "should have a 'name' attribute" do
    Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo"
  end

  [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr|
    it "should have a '#{attr}' attribute" do
      type = Puppet::Resource::Type.new(:hostclass, "foo")
      type.send(attr.to_s + "=", "yay")
      type.send(attr).should == "yay"
    end
  end

  [:hostclass, :node, :definition].each do |type|
    it "should know when it is a #{type}" do
      Puppet::Resource::Type.new(type, "foo").send("#{type}?").should be_true
    end
  end

  it "should indirect 'resource_type'" do
    Puppet::Resource::Type.indirection.name.should == :resource_type
  end

  it "should default to 'parser' for its terminus class" do
    Puppet::Resource::Type.indirection.terminus_class.should == :parser
  end

  describe "when converting to json" do
    before do
      @type = Puppet::Resource::Type.new(:hostclass, "foo")
    end

    def from_json(json)
      Puppet::Resource::Type.from_data_hash(json)
    end

    def double_convert
      Puppet::Resource::Type.from_data_hash(PSON.parse(@type.to_pson))
    end

    it "should include the name and type" do
      double_convert.name.should == @type.name
      double_convert.type.should == @type.type
    end

    it "should validate with only name and kind" do
      expect(@type.to_pson).to validate_against('api/schemas/resource_type.json')
    end

    it "should validate with all fields set" do
      @type.set_arguments("one" => nil, "two" => "foo")
      @type.line = 100
      @type.doc = "A weird type"
      @type.file = "/etc/manifests/thing.pp"
      @type.parent = "one::two"

      expect(@type.to_pson).to validate_against('api/schemas/resource_type.json')
    end

    it "should include any arguments" do
      @type.set_arguments("one" => nil, "two" => "foo")

      double_convert.arguments.should == {"one" => nil, "two" => "foo"}
    end

    it "should not include arguments if none are present" do
      @type.to_pson["arguments"].should be_nil
    end

    [:line, :doc, :file, :parent].each do |attr|
      it "should include #{attr} when set" do
        @type.send(attr.to_s + "=", "value")
        double_convert.send(attr).should == "value"
      end

      it "should not include #{attr} when not set" do
        @type.to_pson[attr.to_s].should be_nil
      end
    end

    it "should not include docs if they are empty" do
      @type.doc = ""
      @type.to_pson["doc"].should be_nil
    end
  end

  describe "when a node"  do
    it "should allow a regex as its name" do
      lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error
    end

    it "should allow an AST::HostName instance as its name" do
      regex = Puppet::Parser::AST::Regex.new(:value => /foo/)
      name = Puppet::Parser::AST::HostName.new(:value => regex)
      lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error
    end

    it "should match against the regexp in the AST::HostName when a HostName instance is provided" do
      regex = Puppet::Parser::AST::Regex.new(:value => /\w/)
      name = Puppet::Parser::AST::HostName.new(:value => regex)
      node = Puppet::Resource::Type.new(:node, name)

      node.match("foo").should be_true
    end

    it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do
      name = Puppet::Parser::AST::HostName.new(:value => "foo")
      node = Puppet::Resource::Type.new(:node, name)

      node.name.should == "foo"
    end

    describe "and the name is a regex" do
      it "should have a method that indicates that this is the case" do
        Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex
      end

      it "should set its namespace to ''" do
        Puppet::Resource::Type.new(:node, /w/).namespace.should == ""
      end

      it "should return the regex converted to a string when asked for its name" do
        Puppet::Resource::Type.new(:node, /ww/).name.should == "ww"
      end

      it "should downcase the regex when returning the name as a string" do
        Puppet::Resource::Type.new(:node, /W/).name.should == "w"
      end

      it "should remove non-alpha characters when returning the name as a string" do
        Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*")
      end

      it "should remove leading dots when returning the name as a string" do
        Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./
      end

      it "should have a method for matching its regex name against a provided name" do
        Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match)
      end

      it "should return true when its regex matches the provided name" do
        Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true
      end

      it "should return true when its regex matches the provided name" do
        Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true
      end

      it "should return false when its regex does not match the provided name" do
        (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false
      end

      it "should return true when its name, as a string, is matched against an equal string" do
        Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true
      end

      it "should return false when its name is matched against an unequal string" do
        Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false
      end

      it "should match names insensitive to case" do
        Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true
      end
    end
  end

  describe "when initializing" do
    it "should require a resource super type" do
      Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass
    end

    it "should fail if provided an invalid resource super type" do
      lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError)
    end

    it "should set its name to the downcased, stringified provided name" do
      Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar"
    end

    it "should set its namespace to the downcased, stringified qualified name for classes" do
      Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar::baz"
    end

    [:definition, :node].each do |type|
      it "should set its namespace to the downcased, stringified qualified portion of the name for #{type}s" do
        Puppet::Resource::Type.new(type, "Foo::Bar::Baz".intern).namespace.should == "foo::bar"
      end
    end

    %w{code line file doc}.each do |arg|
      it "should set #{arg} if provided" do
        type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something")
        type.send(arg).should == "something"
      end
    end

    it "should set any provided arguments with the keys as symbols" do
      type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"})
      type.should be_valid_parameter("foo")
      type.should be_valid_parameter("baz")
    end

    it "should set any provided arguments with they keys as strings" do
      type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"})
      type.should be_valid_parameter(:foo)
      type.should be_valid_parameter(:baz)
    end

    it "should function if provided no arguments" do
      type = Puppet::Resource::Type.new(:hostclass, "foo")
      type.should_not be_valid_parameter(:foo)
    end
  end

  describe "when testing the validity of an attribute" do
    it "should return true if the parameter was typed at initialization" do
      Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_valid_parameter("foo")
    end

    it "should return true if it is a metaparam" do
      Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("require")
    end

    it "should return true if the parameter is named 'name'" do
      Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("name")
    end

    it "should return false if it is not a metaparam and was not provided at initialization" do
      Puppet::Resource::Type.new(:hostclass, "foo").should_not be_valid_parameter("yayness")
    end
  end

  describe "when setting its parameters in the scope" do
    before do
      @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new("foo")), :source => stub("source"))
      @resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope)
      @type = Puppet::Resource::Type.new(:definition, "foo")
      @resource.environment.known_resource_types.add @type
    end

    ['module_name', 'name', 'title'].each do |variable|
      it "should allow #{variable} to be evaluated as param default" do
        @type.instance_eval { @module_name = "bar" }
        var = Puppet::Parser::AST::Variable.new({'value' => variable})
        @type.set_arguments :foo => var
        @type.set_resource_parameters(@resource, @scope)
        @scope['foo'].should == 'bar'
      end
    end

    # this test is to clarify a crazy edge case
    # if you specify these special names as params, the resource
    # will override the special variables
    it "should allow the resource to override defaults" do
      @type.set_arguments :name => nil
      @resource[:name] = 'foobar'
      var = Puppet::Parser::AST::Variable.new({'value' => 'name'})
      @type.set_arguments :foo => var
      @type.set_resource_parameters(@resource, @scope)
      @scope['foo'].should == 'foobar'
    end

    it "should set each of the resource's parameters as variables in the scope" do
      @type.set_arguments :foo => nil, :boo => nil
      @resource[:foo] = "bar"
      @resource[:boo] = "baz"

      @type.set_resource_parameters(@resource, @scope)

      @scope['foo'].should == "bar"
      @scope['boo'].should == "baz"
    end

    it "should set the variables as strings" do
      @type.set_arguments :foo => nil
      @resource[:foo] = "bar"

      @type.set_resource_parameters(@resource, @scope)

      @scope['foo'].should == "bar"
    end

    it "should fail if any of the resource's parameters are not valid attributes" do
      @type.set_arguments :foo => nil
      @resource[:boo] = "baz"

      lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError)
    end

    it "should evaluate and set its default values as variables for parameters not provided by the resource" do
      @type.set_arguments :foo => Puppet::Parser::AST::String.new(:value => "something")
      @type.set_resource_parameters(@resource, @scope)
      @scope['foo'].should == "something"
    end

    it "should set all default values as parameters in the resource" do
      @type.set_arguments :foo => Puppet::Parser::AST::String.new(:value => "something")

      @type.set_resource_parameters(@resource, @scope)

      @resource[:foo].should == "something"
    end

    it "should fail if the resource does not provide a value for a required argument" do
      @type.set_arguments :foo => nil

      lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError)
    end

    it "should set the resource's title as a variable if not otherwise provided" do
      @type.set_resource_parameters(@resource, @scope)

      @scope['title'].should == "bar"
    end

    it "should set the resource's name as a variable if not otherwise provided" do
      @type.set_resource_parameters(@resource, @scope)

      @scope['name'].should == "bar"
    end

    it "should set its module name in the scope if available" do
      @type.instance_eval { @module_name = "mymod" }

      @type.set_resource_parameters(@resource, @scope)

      @scope["module_name"].should == "mymod"
    end

    it "should set its caller module name in the scope if available" do
      @scope.expects(:parent_module_name).returns "mycaller"

      @type.set_resource_parameters(@resource, @scope)

      @scope["caller_module_name"].should == "mycaller"
    end
  end

  describe "when describing and managing parent classes" do
    before do
      environment = Puppet::Node::Environment.create(:testing, [])
      @krt = environment.known_resource_types
      @parent = Puppet::Resource::Type.new(:hostclass, "bar")
      @krt.add @parent

      @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar")
      @krt.add @child

      @scope = Puppet::Parser::Scope.new(Puppet::Parser::Compiler.new(Puppet::Node.new("foo", :environment => environment)))
    end

    it "should be able to define a parent" do
      Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar")
    end

    it "should use the code collection to find the parent resource type" do
      @child.parent_type(@scope).should equal(@parent)
    end

    it "should be able to find parent nodes" do
      parent = Puppet::Resource::Type.new(:node, "bar")
      @krt.add parent
      child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar")
      @krt.add child

      child.parent_type(@scope).should equal(parent)
    end

    it "should cache a reference to the parent type" do
      @krt.stubs(:hostclass).with("foo::bar").returns nil
      @krt.expects(:hostclass).with("bar").once.returns @parent
      @child.parent_type(@scope)
      @child.parent_type
    end

    it "should correctly state when it is another type's child" do
      @child.parent_type(@scope)
      @child.should be_child_of(@parent)
    end

    it "should be considered the child of a parent's parent" do
      @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo")
      @krt.add @grandchild

      @child.parent_type(@scope)
      @grandchild.parent_type(@scope)

      @grandchild.should be_child_of(@parent)
    end

    it "should correctly state when it is not another type's child" do
      @notchild = Puppet::Resource::Type.new(:hostclass, "baz")
      @krt.add @notchild

      @notchild.should_not be_child_of(@parent)
    end
  end

  describe "when evaluating its code" do
    before do
      @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))
      @scope = Puppet::Parser::Scope.new @compiler
      @resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope)

      # This is so the internal resource lookup works, yo.
      @compiler.catalog.add_resource @resource

      @type = Puppet::Resource::Type.new(:hostclass, "foo")
      @resource.environment.known_resource_types.add @type
    end

    it "should add node regex captures to its scope" do
      @type = Puppet::Resource::Type.new(:node, /f(\w)o(.*)$/)
      match = @type.match('foo')

      code = stub 'code'
      @type.stubs(:code).returns code

      subscope = stub 'subscope', :compiler => @compiler
      @scope.expects(:newscope).with(:source => @type, :namespace => '', :resource => @resource).returns subscope

      elevel = 876
      subscope.expects(:ephemeral_level).returns elevel
      subscope.expects(:ephemeral_from).with(match, nil, nil).returns subscope
      code.expects(:safeevaluate).with(subscope)
      subscope.expects(:unset_ephemeral_var).with(elevel)

      # Just to keep the stub quiet about intermediate calls
      @type.expects(:set_resource_parameters).with(@resource, subscope)

      @type.evaluate_code(@resource)
    end

    it "should add hostclass names to the classes list" do
      @type.evaluate_code(@resource)
      @compiler.catalog.classes.should be_include("foo")
    end

    it "should not add defined resource names to the classes list" do
      @type = Puppet::Resource::Type.new(:definition, "foo")
      @type.evaluate_code(@resource)
      @compiler.catalog.classes.should_not be_include("foo")
    end

    it "should set all of its parameters in a subscope" do
      subscope = stub 'subscope', :compiler => @compiler
      @scope.expects(:newscope).with(:source => @type, :namespace => 'foo', :resource => @resource).returns subscope
      @type.expects(:set_resource_parameters).with(@resource, subscope)

      @type.evaluate_code(@resource)
    end

    it "should not create a subscope for the :main class" do
      @resource.stubs(:title).returns(:main)
      @type.expects(:subscope).never
      @type.expects(:set_resource_parameters).with(@resource, @scope)

      @type.evaluate_code(@resource)
    end

    it "should store the class scope" do
      @type.evaluate_code(@resource)
      @scope.class_scope(@type).should be_instance_of(@scope.class)
    end

    it "should still create a scope but not store it if the type is a definition" do
      @type = Puppet::Resource::Type.new(:definition, "foo")
      @type.evaluate_code(@resource)
      @scope.class_scope(@type).should be_nil
    end

    it "should evaluate the AST code if any is provided" do
      code = stub 'code'
      @type.stubs(:code).returns code
      subscope = stub_everything("subscope", :compiler => @compiler)
      @scope.stubs(:newscope).returns subscope
      code.expects(:safeevaluate).with subscope

      @type.evaluate_code(@resource)
    end

    describe "and ruby code is provided" do
      it "should create a DSL Resource API and evaluate it" do
        @type.stubs(:ruby_code).returns(proc { "foo" })
        @api = stub 'api'
        Puppet::DSL::ResourceAPI.expects(:new).with { |res, scope, code| code == @type.ruby_code }.returns @api
        @api.expects(:evaluate)

        @type.evaluate_code(@resource)
      end
    end

    it "should noop if there is no code" do
      @type.expects(:code).returns nil

      @type.evaluate_code(@resource)
    end

    describe "and it has a parent class" do
      before do
        @parent_type = Puppet::Resource::Type.new(:hostclass, "parent")
        @type.parent = "parent"
        @parent_resource = Puppet::Parser::Resource.new(:class, "parent", :scope => @scope)

        @compiler.add_resource @scope, @parent_resource

        @type.resource_type_collection = @scope.known_resource_types
        @type.resource_type_collection.add @parent_type
      end

      it "should evaluate the parent's resource" do
        @type.parent_type(@scope)

        @type.evaluate_code(@resource)

        @scope.class_scope(@parent_type).should_not be_nil
      end

      it "should not evaluate the parent's resource if it has already been evaluated" do
        @parent_resource.evaluate

        @type.parent_type(@scope)

        @parent_resource.expects(:evaluate).never

        @type.evaluate_code(@resource)
      end

      it "should use the parent's scope as its base scope" do
        @type.parent_type(@scope)

        @type.evaluate_code(@resource)

        @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id
      end
    end

    describe "and it has a parent node" do
      before do
        @type = Puppet::Resource::Type.new(:node, "foo")
        @parent_type = Puppet::Resource::Type.new(:node, "parent")
        @type.parent = "parent"
        @parent_resource = Puppet::Parser::Resource.new(:node, "parent", :scope => @scope)

        @compiler.add_resource @scope, @parent_resource

        @type.resource_type_collection = @scope.known_resource_types
        @type.resource_type_collection.add(@parent_type)
      end

      it "should evaluate the parent's resource" do
        @type.parent_type(@scope)

        @type.evaluate_code(@resource)

        @scope.class_scope(@parent_type).should_not be_nil
      end

      it "should not evaluate the parent's resource if it has already been evaluated" do
        @parent_resource.evaluate

        @type.parent_type(@scope)

        @parent_resource.expects(:evaluate).never

        @type.evaluate_code(@resource)
      end

      it "should use the parent's scope as its base scope" do
        @type.parent_type(@scope)

        @type.evaluate_code(@resource)

        @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id
      end
    end
  end

  describe "when creating a resource" do
    before do
      @node = Puppet::Node.new("foo", :environment => 'env')
      @compiler = Puppet::Parser::Compiler.new(@node)
      @scope = Puppet::Parser::Scope.new(@compiler)

      @top = Puppet::Resource::Type.new :hostclass, "top"
      @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top"

      @code = Puppet::Resource::TypeCollection.new("env")
      @code.add @top
      @code.add @middle

      @node.environment.stubs(:known_resource_types).returns(@code)
    end

    it "should create a resource instance" do
      @top.ensure_in_catalog(@scope).should be_instance_of(Puppet::Parser::Resource)
    end

    it "should set its resource type to 'class' when it is a hostclass" do
      Puppet::Resource::Type.new(:hostclass, "top").ensure_in_catalog(@scope).type.should == "Class"
    end

    it "should set its resource type to 'node' when it is a node" do
      Puppet::Resource::Type.new(:node, "top").ensure_in_catalog(@scope).type.should == "Node"
    end

    it "should fail when it is a definition" do
      lambda { Puppet::Resource::Type.new(:definition, "top").ensure_in_catalog(@scope) }.should raise_error(ArgumentError)
    end

    it "should add the created resource to the scope's catalog" do
      @top.ensure_in_catalog(@scope)

      @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
    end

    it "should add specified parameters to the resource" do
      @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'})
      @compiler.catalog.resource(:class, "top")['one'].should == '1'
      @compiler.catalog.resource(:class, "top")['two'].should == '2'
    end

    it "should not require params for a param class" do
      @top.ensure_in_catalog(@scope, {})
      @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
    end

    it "should evaluate the parent class if one exists" do
      @middle.ensure_in_catalog(@scope)

      @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
    end

    it "should evaluate the parent class if one exists" do
      @middle.ensure_in_catalog(@scope, {})

      @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
    end

    it "should fail if you try to create duplicate class resources" do
      othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope )
      # add the same class resource to the catalog
      @compiler.catalog.add_resource(othertop)
      lambda { @top.ensure_in_catalog(@scope, {}) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError)
    end

    it "should fail to evaluate if a parent class is defined but cannot be found" do
      othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay"
      @code.add othertop
      lambda { othertop.ensure_in_catalog(@scope) }.should raise_error(Puppet::ParseError)
    end

    it "should not create a new resource if one already exists" do
      @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
      @compiler.catalog.expects(:add_resource).never
      @top.ensure_in_catalog(@scope)
    end

    it "should return the existing resource when not creating a new one" do
      @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
      @compiler.catalog.expects(:add_resource).never
      @top.ensure_in_catalog(@scope).should == "something"
    end

    it "should not create a new parent resource if one already exists and it has a parent class" do
      @top.ensure_in_catalog(@scope)

      top_resource = @compiler.catalog.resource(:class, "top")

      @middle.ensure_in_catalog(@scope)

      @compiler.catalog.resource(:class, "top").should equal(top_resource)
    end

    # #795 - tag before evaluation.
    it "should tag the catalog with the resource tags when it is evaluated" do
      @middle.ensure_in_catalog(@scope)

      @compiler.catalog.should be_tagged("middle")
    end

    it "should tag the catalog with the parent class tags when it is evaluated" do
      @middle.ensure_in_catalog(@scope)

      @compiler.catalog.should be_tagged("top")
    end
  end

  describe "when merging code from another instance" do
    def code(str)
      Puppet::Parser::AST::Leaf.new :value => str
    end

    it "should fail unless it is a class" do
      lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(Puppet::Error)
    end

    it "should fail unless the source instance is a class" do
      dest = Puppet::Resource::Type.new(:hostclass, "bar")
      source = Puppet::Resource::Type.new(:node, "foo")
      lambda { dest.merge(source) }.should raise_error(Puppet::Error)
    end

    it "should fail if both classes have different parent classes" do
      code = Puppet::Resource::TypeCollection.new("env")
      {"a" => "b", "c" => "d"}.each do |parent, child|
        code.add Puppet::Resource::Type.new(:hostclass, parent)
        code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent)
      end
      lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(Puppet::Error)
    end

    it "should fail if it's named 'main' and 'freeze_main' is enabled" do
      Puppet.settings[:freeze_main] = true
      code = Puppet::Resource::TypeCollection.new("env")
      code.add Puppet::Resource::Type.new(:hostclass, "")
      other = Puppet::Resource::Type.new(:hostclass, "")
      lambda { code.hostclass("").merge(other) }.should raise_error(Puppet::Error)
    end

    it "should copy the other class's parent if it has not parent" do
      dest = Puppet::Resource::Type.new(:hostclass, "bar")

      parent = Puppet::Resource::Type.new(:hostclass, "parent")
      source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent")
      dest.merge(source)

      dest.parent.should == "parent"
    end

    it "should copy the other class's documentation as its docs if it has no docs" do
      dest = Puppet::Resource::Type.new(:hostclass, "bar")
      source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness")
      dest.merge(source)

      dest.doc.should == "yayness"
    end

    it "should append the other class's docs to its docs if it has any" do
      dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness")
      source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness")
      dest.merge(source)

      dest.doc.should == "foonessyayness"
    end

    it "should set the other class's code as its code if it has none" do
      dest = Puppet::Resource::Type.new(:hostclass, "bar")
      source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar"))

      dest.merge(source)

      dest.code.value.should == "bar"
    end

    it "should append the other class's code to its code if it has any" do
      dcode = Puppet::Parser::AST::BlockExpression.new(:children => [code("dest")])
      dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode)

      scode = Puppet::Parser::AST::BlockExpression.new(:children => [code("source")])
      source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode)

      dest.merge(source)

      dest.code.children.collect { |l| l.value }.should == %w{dest source}
    end
  end
end


text:
var DashboardController = function(cdns, serverCount, $scope, $interval, $filter, locationUtils, cacheGroupService, cdnService, serverService, propertiesModel) {

	var cacheGroupHealthInterval,
		currentStatsInterval,
		serverCountInterval,
		autoRefresh = propertiesModel.properties.dashboard.autoRefresh;

	var serverCount = serverCount;

	var getCacheGroupHealth = function() {
		cacheGroupService.getCacheGroupHealth()
			.then(
				function(result) {
					$scope.cacheGroupHealth = result; // this is required by a dashboard widget
					$scope.totalOnline = $filter('number')(result.totalOnline, 0);
					$scope.totalOffline = $filter('number')(result.totalOffline, 0);
				},
				function() {
					$scope.totalOnline = 'Error';
					$scope.totalOffline = 'Error';
				}
			);
	};

	var getCurrentStats = function() {
		cdnService.getCurrentStats()
			.then(
				function(result) {
					var totalStats = _.find(result.currentStats, function(item) {
						// total stats are buried in a hash where cdn = total
						return item.cdn == 'total';
					});
					$scope.totalBandwidth = $filter('number')(totalStats.bandwidth, 2) + ' Gbps';
					$scope.totalConnections = $filter('number')(totalStats.connections, 0);
				},
				function() {
					$scope.totalBandwidth = 'Error';
					$scope.totalConnections = 'Error';
				}
			);
	};

	var getServerCount = function() {
		serverService.getEdgeStatusCount()
			.then(function(result) {
				serverCount = result;
			});
	};

	var createIntervals = function() {
		killIntervals();
		cacheGroupHealthInterval = $interval(function() { getCacheGroupHealth() }, propertiesModel.properties.dashboard.healthyCacheCount.refreshRateInMS );
		currentStatsInterval = $interval(function() { getCurrentStats() }, propertiesModel.properties.dashboard.currentStats.refreshRateInMS );
		serverCountInterval = $interval(function() { getServerCount() }, propertiesModel.properties.dashboard.cacheStatusCount.refreshRateInMS );
	};

	var killIntervals = function() {
		if (angular.isDefined(cacheGroupHealthInterval)) {
			$interval.cancel(cacheGroupHealthInterval);
			cacheGroupHealthInterval = undefined;
		}
		if (angular.isDefined(currentStatsInterval)) {
			$interval.cancel(currentStatsInterval);
			currentStatsInterval = undefined;
		}
		if (angular.isDefined(serverCountInterval)) {
			$interval.cancel(serverCountInterval);
			serverCountInterval = undefined;
		}
	};

	$scope.cdns = cdns;

	$scope.totalBandwidth = 'Loading...';

	$scope.totalConnections = 'Loading...';

	$scope.totalOnline = 'Loading...';

	$scope.totalOffline = 'Loading...';

	$scope.online = function() {
		if (!serverCount.ONLINE) return 0;
		return $filter('number')(serverCount.ONLINE, 0);
	};

	$scope.offline = function() {
		if (!serverCount.OFFLINE) return 0;
		return $filter('number')(serverCount.OFFLINE, 0);
	};

	$scope.reported = function() {
		if (!serverCount.REPORTED) return 0;
		return $filter('number')(serverCount.REPORTED, 0);
	};

	$scope.adminDown = function() {
		if (!serverCount.ADMIN_DOWN) return 0;
		return $filter('number')(serverCount.ADMIN_DOWN, 0);
	};

	$scope.navigateToPath = locationUtils.navigateToPath;

	$scope.$on("$destroy", function() {
		killIntervals();
	});

	var init = function () {
		getCurrentStats();
		getCacheGroupHealth();
		if (autoRefresh) {
			createIntervals();
		}
	};
	init();

};

DashboardController.$inject = ['cdns', 'serverCount', '$scope', '$interval', '$filter', 'locationUtils', 'cacheGroupService', 'cdnService', 'serverService', 'propertiesModel'];
module.exports = DashboardController;