This post is machine-translated. The original post in german language can be found here.

Creating Sysoperation classes through code

Anyone who has ever implemented a function using the SysOperation framework knows that creating the necessary (up to) four classes is some work.

ScreenshotFor lazy efficient contemporaries I have therefore developed the following job, with the help of this time-consuming work can be significantly shortened.

In the job you have to set an "base name", which is used for naming the four classes (Controller, Dataprovider, UIBuilder and Service).

In addition there is a map called dataContractParmsMap from - unless you have inserted content in this map - matching parm methods are created when generating the data provider. In example job the map contains two entries for a customer account number and an item number.

How these classes will look like and which methods are generated, the above screenshot is showing.

static void dev_createSysOperationClasses(Args _args)
{
    str 40 baseClassName = "DEV_SysOpTest";  // Your base class name
    str attributes;
    Set interfacesSet;
    str 100 uiBuilderName;
    str 100 dataContractName;
    str 100 serviceName;
    str 100 controllerName;
    ClassNode createdClassNode;
    SetIterator interfaceIterator;
    str runServiceMethodStr;
    str customMethodStr;
    ClassBuild classBuildCompile;
    Map dataContractParmsMap;
    MapEnumerator dataContractParmsME;
    Source parmMethodSourcecode;
    xppSource xppSource;
    Counter c;
    Dialog dialog;
    DialogField df_baseName;
    #AOT

    ClassNode createSysOperationClasses(str _newClassName, str _extendsClassName, Set _implementsInterfaces, str _attributes)
    {
        // Inspired from SysClassWizard::createEmptyClass()
        ClassBuild classBuild;
        str header;
        SetIterator implementsIterator;
        ;

        if(_attributes)
        {
            header += _attributes + '
';
        }

        header += 'public class '+_newClassName;
        if (_extendsClassName)
        {
            header = header + ' extends '+_extendsClassName;
        }
        if (_implementsInterfaces && _implementsInterfaces.elements() > 0)
        {
            implementsIterator = new SetIterator(_implementsInterfaces);
            implementsIterator.begin();
            header = header + ' implements ' + implementsIterator.value() ;
            implementsIterator.next();
            while (implementsIterator.more())
            {
                header = header + ', ' + implementsIterator.value();
                implementsIterator.next();
            }
        }
        classBuild = new ClassBuild(_newClassName);
        classBuild.addMethod(methodStr(SysClassWizard, classDeclaration), header + '
{
}
');
        classBuild.classNode().AOTcompile(1);

        return classBuild.classNode();
    }

    void createAbstractMethods(ClassBuild _newclass)
    {
        // Inspired from SysClassWizard.createAbstractMethods()
        DictClass dictClass;
        DictMethod dictMethod;
        DictMethod dictMethodFound;
        SysDictClass sysDictClassNew;
        int i;
        int methodActualClassId;
        ;
        dictClass = new DictClass(className2Id(_newclass.name()));
        sysDictClassNew = new SysDictClass(dictClass.id());
        while (dictClass.extend() > 0)
        {
            dictClass = new DictClass(dictClass.extend());
            for (i=1; i<dictClass.objectMethodCnt(); i++)
            {
                dictMethod = dictClass.objectMethodObject(i);
                if (dictMethod.isAbstract())
                {
                    // check if method is alreade implemented
                    methodActualClassId = sysDictClassNew.methodsActualClassId(dictMethod.name());
                    if (methodActualClassId != sysDictClassNew.id())
                    {
                        dictMethodFound = new DictMethod(UtilElementType::ClassInstanceMethod, methodActualClassId, dictMethod.name());
                        if ( dictMethodFound && dictMethodFound.isAbstract() )
                        {
                            _newclass.overrideMethod(dictMethod.name());
                        }
                    }
                }
            }
        }
    }

    void createInterfaceMethods(str _interfaceName, ClassBuild _newclass, boolean _overrideParentMethods)
    {
        // Inspired from SysClassWizard.createInterfaceMethods()
        ClassNode           sourceNode;
        TreeNodeIterator    methodIterator;
        MemberFunction      method;
        str                 todoString;
        ;
        sourceNode = TreeNode::findNode(#ClassesPath + '' + _interfaceName);

        if (!sourceNode)
        {
            Box::warning("@SYS65036");
            return;
        }

        methodIterator = sourceNode.AOTiterator();
        if (methodIterator)
        {
            method = methodIterator.next();
            while (method)
            {
                if (method.treeNodeName() != methodStr(SysClassWizard, classDeclaration) &&
                    (_overrideParentMethods) ||
                     (!_newclass.getMethodImplementation(method.treeNodeName(), true)))
                {
                     _newclass.addMethod(method.treeNodeName(), method.AOTgetSource());
                     todoString = strFmt('
 //TODO: %1 
', strFmt("@SYS73931", method.treeNodeName()));
                     _newclass.addSourceToMethod(method.treeNodeName(),todoString);
                }
                method = methodIterator.next();
            }
        }

    }

    uiBuilderName       = strFmt("%1%2", baseClassName, "UIBuilder");
    dataContractName    = strFmt("%1%2", baseClassName, "DataContract");
    serviceName         = strFmt("%1%2", baseClassName, "Service");
    controllerName      = strFmt("%1%2", baseClassName, "Controller");

    // Use this map to create parm-Methods, if needed
    dataContractParmsMap = new Map(Types::String, Types::String);   // 1=EDT, 2=Name of variable
    //dataContractParmsMap.insert("CustAccount", "myCustAccount");
    //dataContractParmsMap.insert("ItemId", "myItemId");
    dataContractParmsME = dataContractParmsMap.getEnumerator();

    // Validation
    if( !baseClassName)
    {
        throw error(Error::wrongUseOfFunction(funcName()));
    }

    if(strLen(uiBuilderName) > 40)
    {
        setPrefix(uiBuilderName);
        throw error(strFmt("@SYS335263"));
    }
    if(strLen(dataContractName) > 40)
    {
        setPrefix(dataContractName);
        throw error(strFmt("@SYS335263"));
    }
    if(strLen(serviceName) > 40)
    {
        setPrefix(serviceName);
        throw error(strFmt("@SYS335263"));
    }
    if(strLen(controllerName) > 40)
    {
        setPrefix(controllerName);
        throw error(strFmt("@SYS335263"));
    }

    runServiceMethodStr = strFmt(@"[SysEntryPointAttribute(true)]
public void runService(%1 _dataContract) {
    if( !_dataContract.validate())
    {
        throw error(error::wrongUseOfFunction(funcName()));
    }
}", dataContractName);

    // ### UIBuilder
    interfacesSet = new Set(Types::String);
    attributes = "";
    createdClassNode = createSysOperationClasses(uiBuilderName, "SysOperationAutomaticUIBuilder", interfacesSet, attributes);

    // ### Datacontract
    interfacesSet = new Set(Types::String);
    interfacesSet.add("SysOperationValidatable");
    interfacesSet.add("SysOperationInitializable");
    attributes = strFmt("[
	DataContractAttribute
, SysOperationContractProcessingAttribute(classStr(%1))
]", uiBuilderName);
    createdClassNode = createSysOperationClasses(dataContractName, "", interfacesSet, attributes);

    // Create interface methods
    interfaceIterator = new SetIterator(interfacesSet);
    interfaceIterator.begin();
    while (interfaceIterator.more())
    {
        createInterfaceMethods(interfaceIterator.value(), new ClassBuild(createdClassNode.AOTname()), false);
        interfaceIterator.next();
    }

    //validate()
    createdClassNode.AOTsave();
    new ClassBuild(dataContractName).addSourceToMethod("validate", "
	return true;");

    // Add parm()-Methodes from Map
    createdClassNode.AOTsave();
    while(dataContractParmsME.moveNext())
    {
        c++;

        new ClassBuild(dataContractName).addSourceToMethod("classDeclaration", strFmt("
	%1 %2;", dataContractParmsME.currentKey(), dataContractParmsME.currentValue()));

        xppSource       = new xppSource();
        parmMethodSourcecode = xppSource.parmMethod(dataContractParmsME.currentKey(), dataContractParmsME.currentValue());

        parmMethodSourcecode = strFmt("[DataMemberAttribute
	 ,SysOperationDisplayOrderAttribute('%1')]
", c) + parmMethodSourcecode;

        new ClassBuild(createdClassNode.AOTname()).addMethod(strFmt("parm%1", dataContractParmsME.currentKey()), parmMethodSourcecode);

    }
    c = 0;

    // ### Service
    interfacesSet = new Set(Types::String);
    attributes = "";
    createdClassNode = createSysOperationClasses(serviceName, "SysOperationServiceBase", interfacesSet, attributes);

    // Add runService()-Method
    new ClassBuild(createdClassNode.AOTname()).addMethod("runService", runServiceMethodStr);

    // ### Controller
    interfacesSet = new Set(Types::String);
    attributes = "";
    createdClassNode = createSysOperationClasses(controllerName, "SysOperationServiceController", interfacesSet, attributes);

    customMethodStr = strFmt(@"public static %1 newFromArgs(Args _args)
{
    %1 controller;

    controller = new %1(classStr(%2), methodStr(%2, runService));
    controller.parmArgs(_args);

    return controller;
}", controllerName, serviceName);


    // Add newFromArgs()-Method
    new ClassBuild(createdClassNode.AOTname()).addMethod("newFromArgs", customMethodStr);

customMethodStr = strFmt(@"public static void main(Args _args)
{
    %1 controller;
    %2 dataContract;
    SysOperationStartResult sysOperationStartResult;

    if (!_args)
    {
        throw error('@SYS25407');
    }
    controller = %1::newFromArgs(_args);

    controller.parmExecutionMode(SysOperationExecutionMode::Synchronous);

    sysOperationStartResult =
    controller.startOperation();
}", controllerName, dataContractName, serviceName);

    // Add main()-Method
    new ClassBuild(createdClassNode.AOTname()).addMethod("main", customMethodStr);

    // ### Compile all created classes
    classBuildCompile = new ClassBuild(uiBuilderName);
    classBuildCompile.classNode().AOTcompile(1);

    classBuildCompile = new ClassBuild(dataContractName);
    classBuildCompile.classNode().AOTcompile(1);

    classBuildCompile = new ClassBuild(serviceName);
    classBuildCompile.classNode().AOTcompile(1);

    classBuildCompile = new ClassBuild(controllerName);
    classBuildCompile.classNode().AOTcompile(1);
}

 

These post applies to following version:
Dynamics AX 2012

Add comment
 
 

 

 
 
 
Posts of the actual month
März 2024
MoTuWeThFrSaSu
 123
45678910
11121314151617
18192021222324
25262728293031
 
© 2006-2024 Heinz Schweda | Imprint | Contact | German version | Mobile version
In order to provide you with better service, this site uses cookies. By continuing to browse the site, you are agreeing to our use of cookies.