Page 1 of 1

OOP : DataStore class

Posted: Thu Aug 06, 2009 7:28 pm
by LuckyLuke
I'm trying to create a lightweight datastore class (with filter and sort support).
Tips and hints are welcome.

(all this will be part of a small framework (visual and non visual objects))

11/08/2009
- Added the Datastore Class (Sort, Update, ... will be added in the near future)
- Added sample to show usage of the datastore.

The first part is this array class

Code: Select all

;# Array Class 
;# CopyRight 2009 by Smollies 
;# Initial release 

Procedure sf_FreePBString(*Address) 
  Protected string.String    
  PokeI(@string, *Address) 
EndProcedure 

;Private 
#SNARRAY_STRING = 1
#SNARRAY_VALUE  = 2

Structure CSNARRAY 
  *vTable 
  Size.i  
  Flags.b    
  *Data 
EndStructure 

Structure SNARRAYELEMENT 
  StructureUnion 
    Value.i 
    String.s 
  EndStructureUnion  
EndStructure 

Structure SNARRAYDATA 
  Data.SNARRAYELEMENT[0] 
EndStructure 

Interface ISNARRAY 
  Release() 
  GetString.b(iIndex.i, sValue.String) 
  GetValue.b(iIndex.i, iValue.Integer) 
  ReDim.b(iDim.i) 
  SetString.b(iIndex.i, sValue.s) 
  SetValue.b(iIndex.i, iValue.i)      
  Size.i() 
EndInterface 

Procedure.i SnArray_Size(*this.CSNARRAY) 
  ProcedureReturn *this\Size 
EndProcedure 

Procedure.b SnArray_ReDim(*this.CSNARRAY, iDim.i) 
  Protected lData.i, lRes.b = #False, i.i, *p.SNARRAYDATA 
    
  *p = *this\Data 

  If iDim >= 0 
    If *this\Flags & #SNARRAY_STRING And iDim < *this\Size 
      ;Release string memory 
      For i=iDim To *this\Size 
        sf_FreePBString(@*p\Data[i]\String)      
      Next 
    EndIf 
  
    lData = ReAllocateMemory(*this\Data, (iDim+1)*SizeOf(SNARRAYELEMENT)) 
    If lData 
      *this\Size = iDim 
      *this\Data = lData 
      lRes = #True 
    EndIf    
  EndIf      
  ProcedureReturn lRes 
EndProcedure 

Procedure SnArray_Release(*this.CSNARRAY) 
  Protected i.i, *p.SNARRAYDATA 
  
  *p = *this\Data 

  If *this\Flags & #SNARRAY_STRING 
    For i = 0 To *this\Size     
     *p\Data[i]\String = ""
      sf_FreePBString(@*p\Data[i]\String)      
    Next  
  EndIf 
  FreeMemory(*this\Data) 

  FreeMemory(*this)  
EndProcedure 

Procedure.b SnArray_SetValue(*this.CSNARRAY, iIndex.i, iValue.i) 
  Protected *p.SNARRAYELEMENT, lRes.b = #False 
  
  If iIndex >= 0 And iIndex <= *this\Size 
    *p = *this\Data + iIndex*SizeOf(SNARRAYELEMENT)    
    *p\Value = iValue 
    lRes = #True      
  EndIf 
  ProcedureReturn lRes 
EndProcedure 

Procedure.b SnArray_SetString(*this.CSNARRAY, iIndex.i, sValue.s) 
  Protected *p.SNARRAYELEMENT, lRes.b = #False 
  
  If iIndex >= 0 And iIndex <= *this\Size 
    *p = *this\Data + iIndex*SizeOf(SNARRAYELEMENT) 
    *p\String = sValue 
    lRes = #True      
  EndIf 
  ProcedureReturn lRes 
EndProcedure 

Procedure.b SnArray_GetValue(*this.CSNARRAY, iIndex.i, *iValue.Integer) 
  Protected *p.SNARRAYELEMENT, lRes.b = #False 
  
  If iIndex >= 0 And iIndex <= *this\Size 
    *p = *this\Data + iIndex*SizeOf(SNARRAYELEMENT) 
    If *p      
      *iValue\i = *p\Value 
    EndIf 
    lRes = #True 
  EndIf 
  ProcedureReturn lRes 
EndProcedure 

Procedure.b SnArray_GetString(*this.CSNARRAY, iIndex.i, *sValue.String) 
  Protected *p.SNARRAYELEMENT, lRes.b = #False 
  
  If iIndex >= 0 And iIndex <= *this\Size 
    *p = *this\Data + iIndex*SizeOf(SNARRAYELEMENT) 
    *sValue\s = *p\String 
    lRes = #True 
  EndIf 
  ProcedureReturn lRes 
EndProcedure 

Procedure.i SnArray(iDim.i, bFlags.b = #SNARRAY_VALUE) 
  Protected *object.CSNARRAY 

  If iDim >= 0 
    *object = AllocateMemory(SizeOf(CSNARRAY)) 
    If *object 
      *object\vTable = ?VTABLE_CSNARRAY 
      *object\Size = iDim 
      *object\Flags = bFlags 
      *object\Data = AllocateMemory((iDim + 1)*SizeOf(SNARRAYELEMENT)) 
        
      If Not *object\Data 
        FreeMemory(*object)    
        *object = #Null 
      EndIf 
    EndIf 
  EndIf 

  ProcedureReturn *object 
EndProcedure 

DataSection 
  VTABLE_CSNARRAY: 
    Data.i @SnArray_Release() 
    Data.i @SnArray_GetString() 
    Data.i @SnArray_GetValue() 
    Data.i @SnArray_ReDim() 
    Data.i @SnArray_SetString() 
    Data.i @SnArray_SetValue()      
    Data.i @SnArray_Size() 
EndDataSection
My first attempt of the datastore class. (above code needs to be included)

Code: Select all

;# Datastore Class 
;# CopyRight 2009 by Smollies 
;# Initial release 

#SN_ERROR = -1

;Row state identifiers
#SNDS_INSERT = "I"
#SNDS_DELETE = "D"
#SNDS_UPDATE = "U"
#SNDS_NONE = "N"

;Data buffers
Enumeration
 #SNDS_DATA   
 #SNDS_DELETED
 #SNDS_VISIBLE
EndEnumeration

Interface ISNDS 
  Release()
  AddRow.i()
  DeleteRow.b(iRow.i)
  GetColCount.i()
  GetItem(iRow.i, iCol.i, *sValue.String, bType.b = #SNDS_VISIBLE)
  GetRowCount.i(bType.b) 
  Reset()
  SetItem(iRow.i, iCol.i, sValue.s)
EndInterface 

Structure CSNDS
  *vTable
  Buffer.i
  Cols.i
  Rows.i
  DRows.i
  SRows.i
  *Data.ISNARRAY
  *DData.ISNARRAY
  *SData.ISNARRAY
EndStructure  

Procedure.i SNDS(iCol.i, iBuffer.i = 100)
  Protected *object.CSNDS
  If iCol > 0 
    *object = AllocateMemory(SizeOf(CSNDS))
    If *object
      *object\vTable = ?VTABLE_CSNDS
      *object\Cols = iCol + 1   ;extra col to store rowstate
      *object\Buffer = iBuffer
      *object\Rows = 0
      *object\SRows = 0
      *object\DRows = 0

      *object\Data = SnArray(0)
      *object\SData = SnArray(0)
      *object\DData = SnArray(0)
    EndIf
  EndIf

  ProcedureReturn *object
EndProcedure

Procedure SnDS_Release(*this.CSNDS)
  Protected i.i, lData.Integer, lArr.ISNARRAY

  ;Free data
  For i = 0 To *this\Data\Size()
    If *this\Data\GetValue(i, lData)
      lArr = lData\i
      If lArr
        lArr\Release()
      EndIf      
    EndIf
  Next

  *this\Data\Release()
  *this\SData\Release()
  *this\DData\Release()

  FreeMemory(*this)
EndProcedure

;Returns address of array or error
Procedure.i SnDS_GetData(*this.CSNDS, iRow.i) 
  Protected lVal.Integer, lIndex.i, lRes.i = #SN_ERROR

  If iRow >= 0 And iRow <= *this\Rows
    lIndex = iRow / *this\Buffer + 1    
    If *this\Data\GetValue(lIndex, @lVal)     
      lRes = lVal\i
    EndIf
  EndIf

  ProcedureReturn lRes
EndProcedure

Procedure.i SnDS_GetColCount(*this.CSNDS)
  ProcedureReturn *this\Cols - 1 ;Rowstate col => -1
EndProcedure

Procedure.s SnDS_GetRowState(*this.CSNDS, iRow.i)  
  Protected lRes.s = "", lArr.ISNARRAY, lVal.String

  If iRow > 0 And iRow <= *this\Rows      
    lArr = SnDS_GetData(*this, iRow)             
    If lArr
      If lArr\GetString((*this\Rows % *this\Buffer) * *this\Cols, lVal)
        lRes = lVal\s          
      EndIf
    EndIf  
  EndIf

  ProcedureReturn lRes
EndProcedure

Procedure.b SnDS_SetRowState(*this.CSNDS, iRow.i, sValue.s)  
  Protected lRes.b = #False, lArr.ISNARRAY

  If iRow > 0 And iRow <= *this\Rows      
    lArr = SnDS_GetData(*this, iRow)             
    If lArr
      If lArr\SetString((*this\Rows % *this\Buffer) * *this\Cols, sValue)
        lRes = #True
      EndIf
    EndIf  
  EndIf

  ProcedureReturn lRes
EndProcedure


;Add row
Procedure.i SnDS_AddRow(*this.CSNDS)
  Protected lContinue.b = #True, lSize.i, lArr.ISNARRAY, lRes.i = #SN_ERROR
 
  *this\Rows + 1	;Data count
  *this\SRows + 1	;Visible row count

  ;Data
  If (*this\Rows % *this\Buffer = 0 Or *this\Rows = 1) And lContinue
    ;We need extra array containing *this\Buffer rows
    lContinue = #False
    lSize = *this\Data\Size() + 1    
    
    If *this\Data\Redim(lSize)
      ;Create array holding data
      lArr = SnArray(*this\Cols * *this\Buffer, #SNARRAY_STRING)
      If lArr 
        lContinue = *this\Data\SetValue(lSize, lArr)         
      EndIf 
    EndIf
  EndIf

  If lContinue
    ;Visible rows array
    If (*this\SRows % *this\Buffer = 0 Or *this\SRows = 1)
      lContinue = *this\SData\ReDim(*this\SData\Size() + *this\Buffer)    
    EndIf
  
    If lContinue
      *this\SData\SetValue(*this\SRows, *this\Rows)     
      ;Set rowstate
      If SnDS_SetRowState(*this, *this\Rows, #SNDS_INSERT)
        lRes = *this\SRows
      EndIf
    EndIf
  EndIf

  If lRes = #SN_ERROR
    *this\Rows - 1
    *this\SRows - 1
  EndIf

  ProcedureReturn lRes
EndProcedure

;Visible rows
Procedure.b SnDS_DeleteRow(*this.CSNDS, iRow.i)
  Protected lRes.b = #False, lRow.Integer, lArr.ISNARRAY, lState.s, i.i

  If iRow > 0 And iRow <= *this\SRows
    ; Get data row
    If *this\SData\GetValue(iRow, lRow)    
      If lRow\i > 0 And lRow\i <= *this\Rows
        ;Get rowstate 
        lState = SnDS_GetRowState(*this, lRow\i)       
	
        If lState = #SNDS_INSERT        
          SnDS_SetRowState(*this, lRow\i, #SNDS_NONE)       
        Else
          SnDS_SetRowState(*this, lRow\i, #SNDS_DELETE)
        EndIf

        *this\DRows + 1        
        If (*this\DRows % *this\Buffer = 0 Or *this\DRows = 1) 
          *this\DData\ReDim(*this\DData\Size() + *this\Buffer)     
        EndIf
        *this\DData\SetValue(*this\DRows, lRow\i)         

        ;Remove from visible array
        If iRow < *this\SRows
          For i = iRow To *this\SRows - 1
            *this\SData\GetValue(i + 1, lRow)
            *this\SData\SetValue(i, lRow\i)
          Next
        EndIf
        *this\SRows - 1 
        lRes = #True
      EndIf 
    EndIf
  EndIf

  ProcedureReturn lRes  
EndProcedure

Procedure SnDS_Reset(*this.CSNDS)  
  Protected i.i

  For i = 1 To *this\Rows    
    SnDS_SetRowState(*this, i, #SNDS_NONE) 
    *this\SData\SetValue(i,i) 
  Next
  
  ;Remove delete array
  *this\SRows + *this\DRows
  *this\DData\Redim(0)
  *this\DRows = 0
EndProcedure

Procedure.i SnDS_GetRowCount(*this.CSNDS, bType.b)
  Protected lRes.i = 0

  Select bType
    Case #SNDS_DATA
      lRes = *this\Rows
    Case #SNDS_VISIBLE
      lRes = *this\SRows
    Case #SNDS_DELETED
      lRes = *this\DRows
  EndSelect
     
  ProcedureReturn lRes
EndProcedure

;VISIBLE ROWS
Procedure.b SnDS_SetItem(*this.CSNDS, iRow.i, iCol.i, sValue.s)
  Protected lRes.b = #False, lRow.Integer, lArr.ISNARRAY, lState.s

  If (iCol > 0 And iCol <= *this\Cols - 1) And (iRow > 0 And iRow <= *this\SRows)
    ; Get data row
    If *this\SData\GetValue(iRow, lRow)    
      lArr = SnDS_GetData(*this, lRow\i)                     
      If lArr  
        If lArr\SetString(((iRow % *this\Buffer) * *this\Cols) + iCol, sValue)          
          lState = SnDS_GetRowState(*this, lRow\i)
          If lState <> #SNDS_INSERT
            SnDS_SetRowState(*this, lRow\i, #SNDS_UPDATE)
          EndIf              
          lRes = #True    
        EndIf
      EndIf
    EndIf                  
  EndIf
 
  ProcedureReturn lRes
EndProcedure

;VISIBLE ROWS
Procedure.b SnDS_GetItem(*this.CSNDS, iRow.i, iCol.i, *sValue.String, bType.b = #SNDS_VISIBLE)
  Protected lTotal.i, lRes.b = #False, lRow.Integer, lArr.ISNARRAY

  Select bType 
    Case #SNDS_VISIBLE
      lTotal = *this\SRows
      *this\SData\GetValue(iRow, lRow)

    Case #SNDS_DATA
      lTotal = *this\Rows
      lRow\i = iRow

    Case #SNDS_DELETED
      lTotal = *this\DRows
      *this\DData\GetValue(iRow, lRow)
  EndSelect

  If (iCol > 0 And iCol <= *this\Cols - 1) And (iRow > 0 And iRow <= lTotal)   
    lArr = SnDS_GetData(*this, lRow\i)                     
    If lArr  
      If lArr\GetString(((lRow\i % *this\Buffer) * *this\Cols) + iCol, *sValue)          
        lRes = #True    
      EndIf
    EndIf
  EndIf
 
  ProcedureReturn lRes
EndProcedure

DataSection 
  VTABLE_CSNDS: 
    Data.i @SnDS_Release()
    Data.i @SnDS_AddRow()
    Data.i @SnDS_DeleteRow()
    Data.i @SnDS_GetColCount()
    Data.i @SnDS_GetItem()
    Data.i @SnDS_GetRowCount() 
    Data.i @SnDS_Reset()
    Data.i @SnDS_SetItem()
EndDataSection
How to use the datastore...


Code: Select all

UseSQLiteDatabase()

Procedure CheckDatabaseUpdate(Database, Query$)
   Result = DatabaseUpdate(Database, Query$)
   If Result = 0
      Debug DatabaseError()
   EndIf
   
   ProcedureReturn Result
EndProcedure
   
If OpenDatabase(0, ":memory:", "", "")
   
  CheckDatabaseUpdate(0, "CREATE TABLE food (name CHAR(50), weight INT)")
  CheckDatabaseUpdate(0, "INSERT INTO food (name, weight) VALUES ('apple', '10')")
  CheckDatabaseUpdate(0, "INSERT INTO food (name, weight) VALUES ('pear', '5')")
  CheckDatabaseUpdate(0, "INSERT INTO food (name, weight) VALUES ('banana', '20')")
      
  For i = 1 To 3000
    CheckDatabaseUpdate(0, "INSERT INTO food (name, weight) VALUES ('test" + Str(i) + "', '20')")
  Next

  If DatabaseQuery(0, "SELECT * FROM food WHERE weight > 7")
    DS.ISNDS = SnDS(DatabaseColumns(0))

    While NextDatabaseRow(0)
      llRow = DS\AddRow()
      DS\SetItem(llRow, 1, GetDatabaseString(0, 0) ) ; Col 1
      DS\SetItem(llRow, 2, GetDatabaseString(0, 1) ) ; Col 2
    Wend

    ;Call this to reset the update flags after filling the datastore
    DS\Reset()      
    FinishDatabaseQuery(0)
    CloseDatabase(0)
  EndIf
      
  Debug "Columns = " + Str(DS\GetColCount())
  Debug "Total rows = " + Str(DS\GetRowCount(#SNDS_DATA))
  Debug "Visible rows = " + Str(DS\GetRowCount(#SNDS_VISIBLE))      
  Debug "Deleted rows = " + Str(DS\GetRowCount(#SNDS_DELETED))                  

  Debug "-----"
  Debug "Delete Visible row 3"
  DS\DeleteRow(3)
  Debug "Delete Visible row 10"
  DS\DeleteRow(10)
  Debug "-----"

  Debug "Total rows = " + Str(DS\GetRowCount(#SNDS_DATA))
  Debug "Visible rows = " + Str(DS\GetRowCount(#SNDS_VISIBLE))      
  Debug "Deleted rows = " + Str(DS\GetRowCount(#SNDS_DELETED))                  
  Debug "-----"
  Debug "Deleted rows :"      
  For i = 1 To DS\GetRowCount(#SNDS_DELETED)
    If DS\GetItem(i, 1, tmp.String, #SNDS_DELETED)      
      Debug tmp\s      
    EndIf
  Next      
  Debug "-----"
  Debug "Visible rows : (TOP 10)"      
  For i = 1 To 10
    If DS\GetItem(i, 1, tmp.String, #SNDS_VISIBLE)      
      Debug tmp\s      
    EndIf
  Next      

  DS\Release()
EndIf

Posted: Thu Aug 06, 2009 10:43 pm
by Rook Zimbabwe
I thought I agreed... OOP was dead! :wink:

Interesting work so far... how do you clear the subclasses?

Posted: Tue Aug 11, 2009 7:26 pm
by LuckyLuke
Datastore class and sample added. (see first post)

@Rook Zimbabwe:
how do you clear the subclasses?

I'm not using subclasses (yet) ... :wink:

Kind regards