Wednesday, September 4, 2019

utility method to get all table fields their Extended data type or Enum type and add them as parm methods in a class programmatically

I had to get the list of fields from a table with quite many fields to be used as properties in a class. quite and error prone task.

for example for CustTable you should have to do:

classDelaration
{
   CustAccount accountNum;
    CustInvoiceAccount invoiceAccount;
    CustGroupId custGroup;
   ….
}

I made this job that using a bit reflection does the work for you:

static void JobEF_TableGetFieldsListForProperties(Args _args)
{
    TableID tableID;
    DictTable dictTable;
    TableName tableName;
    FieldId fieldId;
    DictField dictField, dictFieldFrom;
    Common buffer;
    TableScope scope = TableScope::CurrentTableOnly;
    ExtendedDataTypeName fieldType;
    EnumName fieldEnum;
    FieldName fieldName;
    LabelString fieldTypeLabel, fieldEnumLabel;
    IdentifierName fieldDataType;
    TextBuffer txtb = new TextBuffer();
    int i;

    tableName = tableStr(CustTable);

    tableID = tablename2id(tableName);
    dictTable = new DictTable(tableID);

    if (!dictTable)
        throw error('invalid table');

    buffer = dictTable.makeRecord();

    fieldId   = dictTable.fieldNext(0, scope);

    while (fieldId && ! isSysId(fieldId))
    {
        dictField   = new DictField(tableID, fieldId);

        fieldType = Global::extendedTypeId2name(dictField.typeId());
        fieldEnum = Global::enumId2Name(dictField.enumId());
        fieldName = dictField.name();
        fieldTypeLabel = Global::extendedTypeId2pname(dictField.typeId());
        fieldEnumLabel = Global::enumId2pname(dictField.enumId());

        fieldDataType = fieldType ? fieldType : fieldEnum;

        if (i)
            txtb.appendText('\n');

        txtb.appendText(strFmt('%1 %2;', fieldDataType, MYD_Functions::stringLowerCaseFirst(fieldName)));

        i++;

        fieldId         = dictTable.fieldNext(fieldId, scope);
    }

    info(txtb.getText());
}

I have a helper class where I have all my utility methods, one is the one used in this code:

public static str stringLowerCaseFirst(str string)
{
    System.String myString, firstLetter;
    str firstLetterLwr, stringEnd, stringNew;

    if (strLen(string) < 2)
        return strLwr(string);

    myString = string;

    firstLetter = myString.Substring(0, 1);
    stringEnd = myString.Substring(1);
    firstLetterLwr = firstLetter.ToLowerInvariant();
    stringNew = strFmt('%1%2', firstLetterLwr, stringEnd);

    return stringNew;
    
    /*
    NOTE:
    you can do the same like \Classes\xppSource\parmMethod
    stringNew = strLwr(subStr(string,1,1))+subStr(string,2,strLen(string));
    I don't know which one is best. I come form C#
    */
}

I took it a step further and this other Job also auto generate the parm methods in a class for each table field.

To complete this task I used two Ax Foundation classes:

  • ClassBuild
  • XppSource

ClassBuild contains methods to programmatically create a class and methods

XppSource is the same class which you use behind the scene when you use shorcuts in the code editor or when you select a template under the script button in the editor toolbar.

static void JobEF_ClassMakeDataContractPerTblFields(Args _args)
{
    TableID tableID;
    DictTable dictTable;
    TableName tableName;
    FieldId fieldId;
    DictField dictField, dictFieldFrom;
    Common buffer;
    TableScope scope = TableScope::CurrentTableOnly;
    ExtendedDataTypeName fieldType;
    EnumName fieldEnum;
    FieldName fieldName;
    LabelString fieldTypeLabel, fieldEnumLabel;
    IdentifierName fieldDataType;
    TextBuffer txtb = new TextBuffer();
    int i;
    ClassName className;
    Source source;
    ClassBuild classBuild;
    XppSource xppSource;
    MethodName paramMethodName;
    str parmPrefixToFieldName;
    boolean addDataContractAttribute;

    //SET the table you want to get all fields from
    tableName = tableStr(CustTable);
    //SET the class you want the parm methods to be created
    className = classStr(MYDataContractClass);
    //SET optionally a prefix to all Table field names for the parm
    parmPrefixToFieldName = '';
    //SET if you want to add the [DataContractAttribute] attribute
    addDataContractAttribute = true;
    
    /*
    the hob will run and create a parm for each field of the table in the class.
    From the info log copy the parmeters and paste in the class declaration.
    */

    tableID = tablename2id(tableName);
    dictTable = new DictTable(tableID);


    if (!dictTable)
        throw error('invalid table');

    buffer = dictTable.makeRecord();

    fieldId   = dictTable.fieldNext(0, scope);

    while (fieldId && !isSysId(fieldId))
    {
        dictField   = new DictField(tableID, fieldId);

        fieldType = Global::extendedTypeId2name(dictField.typeId());
        fieldEnum = Global::enumId2Name(dictField.enumId());
        fieldName = strFmt('%1%2', parmPrefixToFieldName, dictField.name());
        fieldTypeLabel = Global::extendedTypeId2pname(dictField.typeId());
        fieldEnumLabel = Global::enumId2pname(dictField.enumId());

        fieldDataType = fieldType ? fieldType : fieldEnum;

        if (i)
            txtb.appendText('\n');

        txtb.appendText(strFmt('%1 %2;', fieldDataType, MYD_Functions::stringLowerCaseFirst(fieldName)));

        i++;

        classBuild = new ClassBuild(className);
        xppSource = new XppSource();
        source = xppSource.parmMethod(fieldDataType, fieldName);

        paramMethodName = strFmt('parm%1%2', parmPrefixToFieldName, strUpr(subStr(fieldName,1,1))+subStr(fieldName,2,strLen(fieldName)));
        
        if (addDataContractAttribute)
            source = strFmt('[DataContractAttribute]\n%1', source);
        
        classBuild.addMethod(paramMethodName, source);

        fieldId = dictTable.fieldNext(fieldId, scope);
    }

    info(txtb.getText());
}

I hope it helps!

No comments:

Post a Comment