Showing posts with label Strings. Show all posts
Showing posts with label Strings. Show all posts

Wednesday, February 26, 2020

get class Name of a static method

in different occasions especially while using the SysExtension or SysOperation frameworks I found myself writing again the same code and I wanted something more generic the get the current class name. I didn’t find any built in method so I decided to make this simple helper method:

public static ClassName getClassNameFromFuncName(str _funcName)
{
    ClassName className;
    #define.ObjectMethodSeparator('.')
    #define.StaticMethodSeparator('::')
    
    if (strContains(_funcName, #ObjectMethodSeparator))
        className = any2str(conPeek(str2con(_funcName, #ObjectMethodSeparator), 1));
    
    if (strContains(_funcName, #StaticMethodSeparator))
        className = any2str(conPeek(str2con(_funcName, #StaticMethodSeparator), 1));
    
    if (!className2Id(className))
        throw(error(Error::wrongUseOfFunction(funcName())));
    
    return className;    
}

you can try it:

public static void main(Args _args)
{
    str fullName = funcName();
    
    str className = MYD_Functions::getClassNameFromFuncName(fullName);

    info(className);
}

here is how I used it to make a constructor more generic and reusable in other SysExtension instances:

class EF_ExtFrameworkSample3bisFactory extends EF_ExtFrameworkSample3bisBaseClass
{
}

public static Object construct(str _className, str _mySetting = '')
{
    EF_StringAtttibute attr;
    Object baseCl;
    ClassName factoryClassName;
    ClassId factoryClassId;
    ClassName baseClassName;
    ClassId baseClassId;
        
    factoryClassName = MYD_Functions::getClassNameFromFuncName(funcName());
    factoryClassId = className2Id(factoryClassName);
    baseClassId = SysDictClass::superClass(factoryClassId);
    baseClassName = classId2Name(baseClassId);

    attr = new EF_StringAtttibute(_className);
    baseCl = SysExtensionAppClassFactory::getClassFromSysAttribute(baseClassName, attr);

    if (!baseCl)
    {
    throw error(Error::wrongUseOfFunction(funcName()));
    }

    baseCl.initSetting(_mySetting);

    return baseCl;

}

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!