Share My Creation MashSkeletor: Class / CustomView/SQLite Code Helper

Discussion in 'B4J Share Your Creations' started by Mashiane, Apr 3, 2018.

  1. Mashiane

    Mashiane Expert Licensed User

    Ola

    MashSkeletor Download

    I have wanted to do this for like forever. MashSkeletor is a class / custom view/sqlite helper tool that enables one to create class and custom view skeletons including db structures. Yes, it generates useful code, that's what I love about it. It's like a plugin kinda thing. Eventually this coding will be repetitive in nature. The generated code can then be copied to your class / custom view b4j classes.

    This has been inspired by and thanks to @klaus B4X booklet on CustomViews.

    This uses the MashPropertyBag

    Part 1:

    Part 1 is just the basic of how this purports to work. For this part, the Events, Designer Properties and other properties are generated and this can be put on your code. I think I forgot something. Will add it later.



    Events Completion



    Part 2: This will delve more on classes and other things like types and the rest will come along on a need to know basis and what I have learned here. Lol.


    NB: This is supposed to generate excel spreadsheets for reporting, thus the un-avoidable jPOI (15MB) takes everything up.

    PS: Should you have any ideas of what can be added here, any feedback is welcome.
     
    Last edited: Apr 3, 2018
  2. Mashiane

    Mashiane Expert Licensed User

    Now, lets look at the source code. What is behind the scenes?

    1. One needs to create a new / open an existing class project. On new, this creates a database with the respective needed structure. I have opted on a dynamic structure as the development of this will evolve as we go along. As an example,

    Code:
    DBUtils.addFieldOfInteger("Active",True,False)
    creates an integer field that should be indexed and is not unique.

    Code:
    'ensure the database structure is ok
    Sub PrepareDatabase
        
    'create the events database
        DBUtils.InitializeNewTable("Events","id","id")
        DBUtils.AddFieldOfText(
    "EventName",True,True)
        DBUtils.addFieldOfInteger(
    "Active",True,False)
        DBUtils.addFieldOfInteger(
    "Parameters",True,False)
        DBUtils.AddFieldOfText(
    "Description",True,False)
        DBUtils.AddFieldOfText(
    "ParameterDef",False,False)   
        DBUtils.CreateTableFromDefinition(prjDB)
        
    'create the designer properties
        DBUtils.InitializeNewTable("DesignerProperties","id","id")
        DBUtils.AddFieldOfText(
    "KeyName",True,True)
        DBUtils.AddFieldOfText(
    "DisplayName",True,False)
        DBUtils.AddFieldOfText(
    "FieldType",True,False)
        DBUtils.AddFieldOfText(
    "DefaultValue",True,False)
        DBUtils.AddFieldOfText(
    "Description",True,False)
        DBUtils.AddFieldOfText(
    "MinRange",True,False)
        DBUtils.AddFieldOfText(
    "MaxRange",True,False)
        DBUtils.AddFieldOfText(
    "List",True,False)
        DBUtils.addFieldOfInteger(
    "Active",True,False)
        DBUtils.addFieldOfInteger(
    "SetRanges",True,False)
        DBUtils.CreateTableFromDefinition(prjDB)
        
    'create the properties
        DBUtils.InitializeNewTable("Properties","id","id")
        DBUtils.AddFieldOfText(
    "KeyName",True,True)
        DBUtils.AddFieldOfText(
    "DisplayName",True,False)
        DBUtils.AddFieldOfText(
    "FieldType",True,False)
        DBUtils.AddFieldOfText(
    "DefaultValue",True,False)
        DBUtils.AddFieldOfText(
    "Description",True,False)
        DBUtils.addFieldOfInteger(
    "Active",True,False)
        DBUtils.addFieldOfInteger(
    "IsConstant",True,False)
        DBUtils.AddFieldOfText(
    "Scope",True,False)
        DBUtils.AddFieldOfText(
    "HasGet",True,False)
        DBUtils.AddFieldOfText(
    "HasSet",True,False)
        DBUtils.CreateTableFromDefinition(prjDB)
    End Sub
    2. Any existing Events, Designer Properties and Properties are loaded as soon as the end user selects an entry from the tree view. The code below opens up a database, reads a table and load the records in an editable tableview. That's the awesome MashPropertyBag thing. That comes with the source code here in the forum.

    Code:
    Sub treeV_SelectedItemChanged (SelectedItem As TreeItem)
        
    If SelectedItem.IsInitialized = False Then Return
        nodeText = SelectedItem.Text
        
    Select Case nodeText
        
    Case "Events"
            LoadEvents
        
    Case "Designer Properties"
            LoadDesignerProperties
        
    Case "Properties"
            LoadProperties
        
    End Select
    End Sub
    2.1 Events

    Code:
    'load the events in this project
    Sub LoadEvents()
        SetTitle(
    "Events")
        Singular = 
    "event"
        pnlEmpty.RemoveAllNodes
        pnlEmpty.LoadLayout(
    "vTable")
        splitter.SetSizeLimits(
    2,600,600)
        tbl.SetParentForm(MainForm)
        tbl.SetReportViewer
        tbl.AddLabelProperty(
    "ID","-1","Id","id")
        tbl.AddTextBoxProperty(
    "Event Name","","Event Name","eventname")
        tbl.AddTextAreaProperty(
    "Description","","Description","Description")
        tbl.AddLabelProperty(
    "Parameters","0","Parameters","Parameters")
        tbl.AddCheckBoxProperty(
    "Active","1","Active","Active")
        
    Dim structure As Map = CreateMap("id":40,"EventName":200,"Description":200)
        tbl.setreportwidths(structure)
        tbl.SetDataSource(
    File.DirApp,ProjectDB,"Events","id","EventName",True,True)
        tbl.SQLQuery = 
    $"select * from events order by lower(EventName)"$
        tbl.ShowDelete = 
    True
        tbl.ShowClone = 
    True
        tbl.ShowUpdate = 
    True
        tbl.ShowAddChild = 
    True
        tbl.RefreshColumns
        tbl.LoadSQLData(
    Null)
        SetButtons(
    True)
        LastEvent = 
    Null
        LastRow = -
    1
    End Sub
    2.2 Design Properties

    Code:
    load the designer properties
    Sub LoadDesignerProperties()
        LastEvent = 
    Null
        LastRow = -
    1
        SetTitle(
    "Designer Properties")
        Singular = 
    "designer property"
        pnlEmpty.RemoveAllNodes
        pnlEmpty.LoadLayout(
    "vTable")
        splitter.SetSizeLimits(
    2,1,1)
        tbl.SetParentForm(MainForm)
        tbl.SetReportViewer
        tbl.AddLabelProperty(
    "ID","-1","Id","id")
        tbl.AddTextBoxProperty(
    "Key Name","","Key","KeyName")
        tbl.AddTextBoxProperty(
    "Display Name","","DisplayName","DisplayName")
        
    Dim propTypes As List = jMash.CreateList(",","String,Int,Boolean,Color")
        tbl.AddComboBoxProperty(
    "Field Type","String","FieldType","FieldType",propTypes,True)
        tbl.AddTextBoxProperty(
    "Default Value","","DefaultValue","DefaultValue")
        tbl.addtextAreaproperty(
    "Description","","Description","Description")
        tbl.AddTextBoxProperty(
    "Min Range","","MinRange","MinRange")
        tbl.AddTextBoxProperty(
    "Max Range","","MaxRange","MaxRange")
        tbl.addtextAreaproperty(
    "List","","List","List")
        tbl.AddCheckBoxProperty(
    "Active","1","Active","Active")
        
    Dim structure As Map = CreateMap("id":40,"Active":60,"SetRanges":60,"DisplayName":200,"FieldType":100,"MinRange":100,"MaxRange":100,"Description":200,"List":200)
        tbl.setreportwidths(structure)
        tbl.SetDataSource(
    File.DirApp,ProjectDB,"DesignerProperties","id","KeyName",True,True)
        tbl.SQLQuery = 
    $"select * from DesignerProperties order by lower(KeyName)"$
        tbl.ShowDelete = 
    True
        tbl.ShowClone = 
    True
        tbl.ShowUpdate = 
    True
        tbl.RefreshColumns
        tbl.LoadSQLData(
    Null)
        SetButtons(
    True)
    End Sub
    2.3 Properties

    Code:
    'load the properties
    Sub LoadProperties()
        LastEvent = 
    Null
        LastRow = -
    1
        SetTitle(
    "Properties")
        Singular = 
    "property"
        pnlEmpty.RemoveAllNodes
        pnlEmpty.LoadLayout(
    "vTable")
        splitter.SetSizeLimits(
    2,1,1)
        tbl.SetParentForm(MainForm)
        tbl.SetReportViewer
        tbl.AddLabelProperty(
    "ID","-1","Id","id")
        tbl.AddTextBoxProperty(
    "Property","","Key","KeyName")
        tbl.AddTextBoxProperty(
    "Display Name","","DisplayName","DisplayName")
        
    Dim scope As List = jMash.CreateList(",","Private,Public")
        tbl.AddComboBoxProperty(
    "Scope","Private","Scope","Scope",scope,True)
        
    Dim propTypes As List = jMash.CreateList(",","String,Byte,Int,Long,Map,List,Object,Canvas,Boolean,Short,Float,Double,Char")
        tbl.AddComboBoxProperty(
    "Field Type","String","FieldType","FieldType",propTypes,True)
        tbl.AddTextBoxProperty(
    "Default Value","","DefaultValue","DefaultValue")
        tbl.addtextAreaproperty(
    "Description","","Description","Description")
        tbl.AddCheckBoxProperty(
    "Constant","0","IsConstant","IsConstant")
        tbl.AddCheckBoxProperty(
    "Get","1","HasGet","HasGet")
        tbl.AddCheckBoxProperty(
    "Set","1","HasSet","HasSet")
        tbl.AddCheckBoxProperty(
    "Active","1","Active","Active")
        
    Dim structure As Map = CreateMap("id":40,"Active":60,"HasGet":60,"HasSet":60,"IsConstant":80,"DisplayName":200)
        tbl.setreportwidths(structure)
        tbl.SetDataSource(
    File.DirApp,ProjectDB,"Properties","id","KeyName",True,True)
        tbl.SQLQuery = 
    $"select * from Properties order by lower(KeyName)"$
        tbl.ShowDelete = 
    True
        tbl.ShowClone = 
    True
        tbl.ShowUpdate = 
    True
        tbl.RefreshColumns
        tbl.LoadSQLData(
    Null)
        SetButtons(
    True)
    End Sub
    3. A closer look at Events, these can have parameters or not. So the uncoming version will have parameter settings so one can define the parameters for the events and CallSub statements generated for those to raise them.

    4. And then finally, the source code generation. This is nothing fancy actually. The CustomView booklet details how this can be done (manually). After all, we code manually. Anyway, this collects all the information entered in the events, design properties and properties to generate the needed skeleton (thus Skeletor in the name)

    Code:
    'build the source code
    Sub btnBuild_Click
        SetTitle(
    "Skeleton Source Code")
        pnlEmpty.RemoveAllNodes
        pnlEmpty.LoadLayout(
    "vCode")
        splitter.SetSizeLimits(
    2,1,1)
        SetButtons(
    False)
        
    Dim sb As StringBuilder
        sb.Initialize
        sb.Append(BuildEvents)
        sb.Append(BuildDesignerProperties)
        sb.Append(BuildProperties)
        
    'set the code
        jMash.CodeAreaSetText(txtCode,sb.ToString,"1",True)
    End Sub

    'build code for events, only process active ones
    Sub BuildEvents() As String
        
    Dim sb As StringBuilder
        sb.Initialize
        jMash.AddComment(sb,
    "This code has been generated with MashSkeletor")
        jMash.AddComment(sb,
    "The Class / CustomView code generated here can be used as a start to create your classes or custom views")
        jMash.AddComment(sb,
    "The idea behind this is encouraging the planning of one's code FIRST")
        jMash.AddComment(sb,
    "*****")
        jMash.AddComment(sb,
    "Conceptualized, created and developed by Mash on anele@mbangas.com")
        jMash.AddComment(sb,
    "Powered by B4X from Anywhere Software, www.b4x.com")
        jMash.AddComment(sb,jMash.LongDateTimeToday)
        jMash.AddComment(sb,
    "*****")
        jMash.AddComment(sb,
    "***** START OF EVENTS *****")
        
    Dim sb1 As StringBuilder
        sb1.Initialize
        
    Dim rEvents As List = DBUtils.ExecuteMaps(prjDB,"select * from events where Active = 1 order by lower(EventName)",Null)
        
    For Each emap As Map In rEvents
            
    Dim sEventName As String = jMash.GetDefault(emap,"EventName","")
            
    Dim sdescription As String = jMash.GetDefault(emap,"description","")
            
    Dim sparameterdef As String = jMash.GetDefault(emap,"parameterdef","")
            
    'add code to build the parameters
            If sdescription <> "" Then
                jMash.AddComment(sb,sdescription)
            
    End If
            sb.Append(
    $"#Event: ${sEventName}()"$).Append(CRLF)
            sb1.Append(
    $"#RaisesSynchronousEvents: ${sEventName}"$).Append(CRLF)
        
    Next
        jMash.AddComment(sb,
    "*****")
        sb.Append(sb1.ToString)
        jMash.AddComment(sb,
    "***** END OF EVENTS *****")
        sb.append(
    CRLF)
        
    Return sb.tostring
    End Sub

    'build the code for the designer properties
    Sub BuildDesignerProperties() As String
        
    Dim sb As StringBuilder
        sb.Initialize
        jMash.AddComment(sb,
    "***** START OF DESIGNER PROPERTIES *****")
        
    Dim rDP As List = DBUtils.ExecuteMaps(prjDB,"select * from DesignerProperties where active = 1 order by lower(KeyName)",Null)
        
    For Each dpmap As Map In rDP
            
    Dim sKeyName As String = jMash.GetDefault(dpmap,"KeyName","")
            
    Dim sDisplayName As String = jMash.GetDefault(dpmap,"DisplayName","")
            
    Dim sFieldType As String = jMash.GetDefault(dpmap,"FieldType","")
            
    Dim sDefaultValue As String = jMash.GetDefault(dpmap,"DefaultValue","")
            
    Dim sDescription As String = jMash.GetDefault(dpmap,"Description","")
            
    Dim sMinRange As String = jMash.getdefault(dpmap,"MinRange","")
            
    Dim sMaxRange As String = jMash.GetDefault(dpmap,"MaxRange","")
            
    Dim sList As String = jMash.GetDefault(dpmap,"List","")
            
    'remove any blank spaces on the name
            sKeyName = sKeyName.Replace(" ","").trim
            
    'do we have min and max range
            Dim def As String = $"#DesignerProperty: Key: ${sKeyName}, DisplayName: ${sDisplayName}, FieldType: ${sFieldType}, DefaultValue: ${sDefaultValue}"$
            
    If sMinRange <> "" And sMaxRange <> "" Then
                
    Dim minmax As String = $", MinRange: ${sMinRange}, MaxRange: ${sMaxRange}"$
                def = def & minmax
            
    End If
            
    If sList <> "" Then
                def = def & 
    $", List: ${sList}"$
            
    End If
            def = def & 
    $", Description: ${sDescription}"$
            
    'build the def up
            sb.Append(def).Append(CRLF)
        
    Next
        jMash.AddComment(sb,
    "***** END OF DESIGNER PROPERTIES *****")
        sb.append(
    CRLF)
        
    Return sb.tostring
    End Sub

    'build the properties for the class
    Sub BuildProperties() As String
        
    Dim sbCG As StringBuilder
        sbCG.Initialize
        
    Dim sbPrp As StringBuilder
        sbPrp.Initialize
        
    Dim sb As StringBuilder
        sb.initialize
        
    Dim pp As List = DBUtils.ExecuteMaps(prjDB,"select * from Properties where active = 1 order by lower(KeyName)",Null)
        
    For Each pmap As Map In pp
            
    Dim sKeyName As String = jMash.GetDefault(pmap,"KeyName","")
            
    Dim sFieldType As String = jMash.GetDefault(pmap,"FieldType","")
            
    Dim sDefaultValue As String = jMash.getdefault(pmap,"DefaultValue","")
            
    Dim sDescription As String = jMash.GetDefault(pmap,"Description","")
            
    Dim sIsConstant As String = jMash.GetDefault(pmap,"IsConstant","0")
            
    Dim sScope As String = jMash.GetDefault(pmap,"Scope","")
            
    Dim sHasGet As String = jMash.GetDefault(pmap,"HasGet","0")
            
    Dim sHasSet As String = jMash.GetDefault(pmap,"HasSet","0")
            sKeyName = sKeyName.Replace(
    " ","").trim
            
    If sIsConstant = "1" Then sIsConstant = "Const"
            
    'if this has no get or set then sit in class globals
            If sHasGet = "0" And sHasSet = "0" Then
                sbCG.Append(
    $"${sScope} ${sIsConstant} ${sKeyName} As ${sFieldType}"$)
                
    If sDefaultValue <> "" Then
                    sbCG.Append(
    " = ")
                    
    If sFieldType = "String" Then sbCG.Append(QUOTE)
                    sbCG.Append(sDefaultValue)
                    
    If sFieldType = "String" Then sbCG.Append(QUOTE)
                
    End If
                
    If sDescription <> "" Then
                    sbCG.Append(
    $" '${sDescription}"$).Append(CRLF)
                
    End If
            
    Else
                sbCG.Append(
    $"Private m${sKeyName} As ${sFieldType}"$)
                
    If sDefaultValue <> "" Then
                    sbCG.Append(
    " = ")
                    
    If sFieldType = "String" Then sbCG.Append(QUOTE)
                    sbCG.Append(sDefaultValue)
                    
    If sFieldType = "String" Then sbCG.Append(QUOTE)
                
    End If
                
    If sDescription <> "" Then
                    sbCG.Append(
    $" '${sDescription}"$).Append(CRLF)
                
    End If
                
    'this has get or set
                If sHasGet = "1" Then
                    
    If sDescription <> "" Then
                        sbPrp.Append(
    $"'Gets ${sDescription}"$).Append(CRLF)
                    
    End If
                    sbPrp.Append(
    $"${sScope} Sub get${sKeyName} As ${sFieldType}
                    Return m${
    sKeyName}
                    End Sub"$).Append(CRLF)   
                End If
                
    'this has get or set
                If sHasSet = "1" Then
                    
    If sDescription <> "" Then
                        sbPrp.Append(
    $"'Sets ${sDescription}"$).Append(CRLF)
                    
    End If
                    sbPrp.Append(
    $"${sScope} Sub set${sKeyName}(${sKeyName} As ${sFieldType})
                    m${
    sKeyName} = ${sKeyName}
                    End Sub"$).Append(CRLF)
                End If
            
    End If
        
    Next
        jMash.AddComment(sb,
    "***** START OF PROPERTIES *****")
        sb.Append(sbCG.ToString).Append(
    CRLF)
        sb.Append(sbPrp.ToString).Append(
    CRLF)
        jMash.AddComment(sb,
    "***** END OF PROPERTIES *****")
        sb.Append(
    CRLF)
        
    Return sb.tostring
    End Sub
     
    joulongleu likes this.
  3. Mashiane

    Mashiane Expert Licensed User

    Continuing on behind the scenes...

    The BuildEventCalls method, based on captured events builds up the code for the CallSub events for the class. As there are two parameters that should be specified for events, this just does that simply.

    Code:
    'build code for events calls, only process active ones
    Sub BuildEventsCalls() As String
        
    Dim sb As StringBuilder
        sb.Initialize
        jMash.AddComment(sb,
    "***** START EVENT CALLS *****")
        
    Dim rEvents As List = DBUtils.ExecuteMaps(prjDB,"select * from events where Active = 1 order by lower(EventName)",Null)
        
    For Each emap As Map In rEvents
            
    Dim sEventName As String = jMash.GetDefault(emap,"EventName","")
            
    Dim sdescription As String = jMash.GetDefault(emap,"description","")
            
    Dim sParameters As String = jMash.GetDefault(emap,"Parameters","0")
            
    Dim sparameter As String = jMash.GetDefault(emap,"parameter","")
            
    'add code to build the parameters
            If sdescription <> "" Then
                jMash.AddComment(sb,sdescription)
            
    End If
            sb.Append(
    $"Public Sub ${sEventName}(${sparameter})"$).Append(CRLF)
            sb.Append(
    $"If SubExists(CallBack, EventName & "_${sEventName}") Then"$).Append(CRLF)
            
    Select Case sParameters
            
    Case 0
                sb.Append(
    $"CallSub(CallBack, EventName & "_${sEventName}")"$).Append(CRLF)
            
    Case 1
                sb.Append(
    $"CallSub2(CallBack, EventName & "_${sEventName}", ${sparameter})"$).Append(CRLF)
            
    Case 2
                sb.Append(
    $"CallSub3(CallBack, EventName & "_${sEventName}", ${sparameter})"$).Append(CRLF)
            
    End Select
            sb.Append(
    "End If").Append(CRLF)
            sb.Append(
    "End Sub").Append(CRLF)
        
    Next
        jMash.AddComment(sb,
    "***** END OF EVENT CALLS *****")
        sb.append(
    CRLF)
        
    Return sb.tostring
    End Sub
    Also the rest of the custom view code is added, because we need the properties and designer properties to be defined in the right locations and also read in the right places, so I added...

    Code:
    Sub BuildOtherCode() As String
        
    Dim sb As StringBuilder
        sb.Initialize
        jMash.AddComment(sb,
    "***** START OTHER CODE *****")
        sb.Append(
    $"Sub Class_Globals
        Private fx As JFX
        Private mEventName As String 'ignore
        Private mCallBack As Object 'ignore
        Private mBase As Pane
        ${
    sbCG.tostring}
    End Sub"$).append(CRLF)

    sb.append(
    $"Public Sub Initialize (Callback As Object, EventName As String)
        mEventName = EventName
        mCallBack = Callback
    End Sub"$).append(CRLF)

    sb.append(
    $"Public Sub DesignerCreateView (Base As Pane, Lbl As Label, Props As Map)
        mBase = Base
        ${
    sbDE.tostring}
    End Sub"$).append(CRLF)

    sb.append(
    $"Private Sub Base_Resize (Width As Double, Height As Double)
    End Sub"$).append(CRLF)

    sb.append(
    $"Public Sub GetBase As Pane
        Return mBase
        End Sub"$).Append(CRLF)
        jMash.AddComment(sb,"***** END OTHER CODE *****")
        
    Return sb.tostring
    End Sub
    I also decided to add a nice status bar at the bottom of my page to indicate the total number of events, designer properties and properties.

    Code:
    Sub CreateStatusBar
        
    'add the status bar at the bottom
        
        lblEvents.Initialize(
    "lblEvents")
        lblEvents.Text = 
    "Events:"
        lblDesignerProperties.Initialize(
    "lblDesignerProperties")
        lblDesignerProperties.Text = 
    "Designer Properties:"
        lblProperties.Initialize(
    "lblProperties")
        lblProperties.Text = 
    "Properties:"
        
        
    Dim sep1 As Separator
        sep1.Initialize(
    "")
        
    Dim sep2 As Separator
        sep2.Initialize(
    "")
        
        
    Dim blnk1 As Label
        blnk1.Initialize(
    "")
        blnk1.Text = 
    " "
        
        
    Dim blnk2 As Label
        blnk2.Initialize(
    "")
        blnk2.Text = 
    " "
        
        
    Dim blnk3 As Label
        blnk3.Initialize(
    "")
        blnk3.Text = 
    " "
        
        
    Dim blnk4 As Label
        blnk4.Initialize(
    "")
        blnk4.Text = 
    " "
        
        
    Dim lblNote As Label
        lblNote.Initialize(
    "")
        lblNote.Text = 
    "Conceptualized, Designed and Developed by Anele 'Mashy' Mbanga - anele@mbangas.com"
        
        StatusBar1.LeftItems.AddAll(
    Array(lblEvents,blnk1,sep1,blnk2,lblDesignerProperties,blnk3,sep2,blnk4,lblProperties))
        StatusBar1.RightItems.AddAll(
    Array(lblNote))
    End Sub
    And this is updated with...

    Code:
    Sub SetCounters(e As Int, dp As Int, p As Int)
        lblEvents.Text = 
    "Events: " & e
        lblDesignerProperties.Text = 
    "Designer Properties: " & dp
        lblProperties.Text = 
    "Properties: " & p
    End Sub
    As those label definitions are defined in Class_Globals
     
    amaxco likes this.
  4. Mashiane

    Mashiane Expert Licensed User

    Here is the source code for MashSkeletor so far, this is still work in progress though.

    NB: The DButils included here has some things in my flavor. Dont put it on shared folder.

    Enjoy.
     

    Attached Files:

    amaxco likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice