class CSVFileEngine_text(unittest.TestCase):
    def setUp(self):
        self._file = AutoResizeableFile()
        self._file.writeLine("T1;T02;T003")
        self._engine = CSVFileEngine(self._file)


    def tearDown(self):
        del self._file
        del self._engine


    def testEngineOperations(self):
        self.assertEqual(self._engine.columnCount(), 3, ".columnCount() return error")
        self.assertEqual(self._engine.rowCount(), 0, ".rowCount() return error")
        self.assertEqual(tuple(self._engine.titles()), ("T1", "T02", "T003"), ".titles() return error")
        self._engine.insertRow(0, ("a", "aaa", "aa"))
        self._engine.insertRow(1, ("bb", "aaa", "bbb"))
        self._engine.insertRow(0, ("ccc", "c", "bbb"))
        self._engine.insertRow(3, ("dddd", "eeeee", "aa"))
        self.assertEqual(self._engine.rowData(0), (0, "ccc", "c", "bbb"), ".rowData() return error")
        self.assertEqual(self._engine.rowData(1), (1, "a", "aaa", "aa"), ".rowData() return error")
        self.assertEqual(self._engine.rowData(2), (2, "bb", "aaa", "bbb"), ".rowData() return error")
        self.assertEqual(self._engine.rowData(3), (3, "dddd", "eeeee", "aa"), ".rowData() return error")
        self.assertEqual(self._engine.rowCount(), 4, ".rowCount() return error")
        self._engine.removeRowAction(1)
        self._engine.removeRowAction(2)
        self.assertEqual(self._engine.rowCount(), 2, ".rowCount() return error")
        self.assertEqual(self._engine.rowData(0), (0, "ccc", "c", "bbb"), ".rowData() return error")
        self.assertEqual(self._engine.rowData(1), (1, "bb", "aaa", "bbb"), ".rowData() return error")
        searchResult = self._engine.searchRows(2, "bbb")
        self.assertEqual(searchResult.rowCount(), 2, "Search result length error")
        self._engine.insertRow(0, ("a", "aaa", ""))
        self._engine.insertRow(1, ("bb", "aaa", "bbb"))
        self._engine.insertRow(2, ("ccc", "c", "bbb"))
        self._engine.insertRow(3, ("dddd", "eeeee", "aa"))
        self.assertEqual(self._engine.rowCount(), 6, ".rowCount() return error")
        self._engine.changeFieldData(0, 2, "CHANGED")
        self._engine.changeFieldData(2, 2, "0")
        self._engine.changeFieldData(5, 2, "GO")
        self._engine.changeFieldData(4, 0, "")
        self.assertEqual(self._engine.fieldData(0, 2), "CHANGED", ".fieldData() return error")
        self.assertEqual(self._engine.fieldData(2, 2), "0", ".fieldData() return error")
        self.assertEqual(self._engine.fieldData(5, 2), "GO", ".fieldData() return error")
        self.assertEqual(self._engine.fieldData(4, 0), "", ".fieldData() return error")
        searchResult = self._engine.searchRows(1, "aaa")
        self.assertEqual(searchResult.rowCount(), 3, "Search result length error")
        self._engine.insertRow(0, ("", "", ""))
        self._engine.changeFieldData(0, 0, "HI")
        self._engine.changeFieldData(0, 1, "HELLO")
        self._engine.changeFieldData(0, 2, "BYE")
        self.assertEqual(tuple(self._engine.rowData(0)), (0, "HI", "HELLO", "BYE"), ".rowData() return error")
class CSVTableModel(QtCore.QAbstractTableModel):
    """
        A class for representing *.CSV files as QAbstractTableModel objects
    """
    def __init__ (self, filePath = None, engine = None):
        """
            Default constructor.
            'filePath' - path to the *.CSV file.
            'engine' - reference to CSVFileEngine object.
        Used if the 'filePath' parameter has None value, ignored otherwise
        """
        QtCore.QAbstractTableModel.__init__(self)
        self._engine = engine
        if self._engine == None:
            engineFile = AutoResizeableFile(filePath)
            self._engine = CSVFileEngine(engineFile)
            
    
    def rowCount(self, modelIndex = QtCore.QModelIndex()):
        """
            Reimplemented from QAbstractTableModel class
        """
        return self._engine.rowCount()
    
    
    def columnCount(self, modelIndex = QtCore.QModelIndex()):
        """
            Reimplemented from QAbstractTableModel class
        """
        return self._engine.columnCount()
    
    
    def data(self, index, role = QtCore.Qt.DisplayRole):
        """
            Reimplemented from QAbstractTableModel class
        """
        index = self._validateQModelIndex(index) 
        if not index.isValid() or role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()
        return QtCore.QVariant(self._engine.fieldData(index.row(), index.column()))
    
    
    def headerData(self, section, orientation, role = QtCore.Qt.DisplayRole):
        """
            Reimplemented from QAbstractTableModel class
        """
        if role == QtCore.Qt.DisplayRole:
            if orientation == QtCore.Qt.Horizontal:
                return QtCore.QVariant(self._engine.titles()[section])
            else:
                return  QtCore.QVariant(section + 1)
        return QtCore.QVariant()
    
    
    def insertRows(self, row, count, parent = QtCore.QModelIndex()):
        """
            Reimplemented from QAbstractTableModel class
        """
        for i in xrange(row, row + count):
            self.insertRow(i)
        return True
    
    
    def insertRow(self, row, parent = QtCore.QModelIndex()):
        """
            Reimplemented from QAbstractTableModel class
        """
        emptyData = []
        for i in xrange(0, self._engine.columnCount()):
            emptyData.append("")
        QtCore.QAbstractTableModel.beginInsertRows(self, parent, row, row)
        self._engine.insertRow(row, emptyData)
        QtCore.QAbstractTableModel.endInsertRows(self)
        return True
    
    
    def removeRow(self, row, parent = QtCore.QModelIndex()):
        """
            Reimplemented from QAbstractTableModel class
        """
        QtCore.QAbstractTableModel.beginRemoveRows(self, parent, row, row)
        self._engine.removeRow(row)
        QtCore.QAbstractTableModel.endRemoveRows(self)
        return True
    
    
    def removeRows(self, row, count, parent = QtCore.QModelIndex()):
        """
            Reimplemented from QAbstractTableModel class
        """
        
        for i in xrange(row, row + count):
            self.removeRow(row)
        return True
        
        
    def setData(self, index, value, role = QtCore.Qt.EditRole):
        """
            Reimplemented from QAbstractTableModel class
        """
        self._engine.changeFieldData(index.row(), index.column(), value)
        return True
    
    
    def search(self, keyColumn, key):
        """
            Searches rows that have the same string value in 'keyColumn' as 'key'.
        Returns a CSVTableModel object, representing search results.
            'keyColumn' - zero-based column index
            'key' - string-convertable object to search
        """
        return CSVTableModel(None, self._engine.searchRows(keyColumn, key))
        
        
    def flags(self, index):
        """
            Reimplemented from QAbstractTableModel class
        """
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
    
    
    def save(self):
        """
            Saves all changes made on the disk.
        """
        self._engine.flush()
            
    
    def _validateQModelIndex(self, index):
        if not isinstance(index, QtCore.QModelIndex):
            raise TypeError("Index must be have 'QModelIndex' type", 
                            {"raisingObject": self, "index": index})
        return index