Add new EC Symbol provider with custom symbol sets using Native API's

An expression is a text string that defines the syntax to be evaluated by the expression evaluator. The expression input can comprise of numbers, strings, access strings, symbols, and operators. Once you attach an item type with an expression defined, the corresponding value of the expression will display in the element's Properties dialog. 

This blog provides some code snips that demonstrate “How to add new EC Symbol Provider and make use of it in EC Expression using Native API's as following:

  • Write new symbol provider to do basic mathematical operation like Addition, Subtraction, Multiplication, Division.
  • Register newly added symbol provider through published EC symbol provider APIs.
  • Create item type, item type property definitions and set EC Expression which consist of newly added symbols.
  • Attach created Item Type to element.
  • Read result of calculated property from attached instance.
  • Unregister newly added symbol provider.

Write Sample Symbol Provider

SymbolsProvider.h file:

using namespace Bentley::ECN;

/*=================================================================================**//**
* MathsOperationSymbolProvider class is symbol provider classs which implements IECSymbolProvider methods.
* This class first creates context of symbols to execute in EC Expression system and then publish all added symbols to it.
* Once it is published, symbol provider will be available to execute symbols methods added in it's calling expression.
* @bsiclass                                                               Bentley Systems
+===============+===============+===============+===============+===============+======*/
struct MathsOperationSymbolProvider : ECN::IECSymbolProvider
{
private:
    mutable ECN::SymbolPtr  m_MathsOperationNamespaceSymbol;
    virtual WCharCP         _GetName() const override { return L"MathsOperationSymbolProvider"; }
    virtual void            _PublishSymbols(ECN::SymbolExpressionContextR context, bvector<WString> const& requestedSymbolSets) const override;

public:
    MathsOperationSymbolProvider();
    static ExpressionStatus Addition(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args);
    static ExpressionStatus Subtraction(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args);
    static ExpressionStatus Multiplication(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args);
    static ExpressionStatus Division(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args);
};

SymbolsProvider.cpp file: 

//SymbolsProvider.cpp file

#include "SymbolsProvider.h"

/*---------------------------------------------------------------------------------**//**
* @description  This creats symbol context and add all required symbol names to it.
* @bsimethod                                                              Bentley Systems
+---------------+---------------+---------------+---------------+---------------+------*/
MathsOperationSymbolProvider::MathsOperationSymbolProvider()
    {
    SymbolExpressionContextPtr methodContext = SymbolExpressionContext::Create(NULL);
    methodContext->AddSymbol(*MethodSymbol::Create(L"Addition", Addition,NULL));
    methodContext->AddSymbol(*MethodSymbol::Create(L"Subtraction", Subtraction,NULL));
    methodContext->AddSymbol(*MethodSymbol::Create(L"Multiplication", Multiplication,NULL));
    methodContext->AddSymbol(*MethodSymbol::Create(L"Division", Division,NULL));

    m_MathsOperationNamespaceSymbol = ContextSymbol::CreateContextSymbol(L"MathsOperation", *methodContext);
    }

/*---------------------------------------------------------------------------------**//**
* @description  This function publish all newly added symbols to EC Expression evaluation system.
* @bsimethod                                                              Bentley Systems
+---------------+---------------+---------------+---------------+---------------+------*/
void MathsOperationSymbolProvider::_PublishSymbols(SymbolExpressionContextR context, bvector<WString> const & requestedSymbolSets) const
    {
    if (m_MathsOperationNamespaceSymbol.IsNull())
        return;
    context.AddSymbol(*m_MathsOperationNamespaceSymbol);
    }

/*---------------------------------------------------------------------------------**//**
* @description  This is Addition symbol implementation which adds two numbers and return result.
* @bsimethod                                                              Bentley Systems
+---------------+---------------+---------------+---------------+---------------+------*/
ECN::ExpressionStatus MathsOperationSymbolProvider::Addition(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args)
    {
    if (2 != args.size())
        return ExprStatus_UnknownError;

    double arg1, arg2;
    DgnECSymbolProvider::ExtractArg(arg1, args[0]);
    DgnECSymbolProvider::ExtractArg(arg2, args[1]);

    evalResult.InitECValue().SetDouble(arg1+arg2);
    return ExprStatus_Success;
    }

/*---------------------------------------------------------------------------------**//**
* @description  This is Substraction symbol implementation which substract two numbers and return result.
* @bsimethod                                                              Bentley Systems
+---------------+---------------+---------------+---------------+---------------+------*/
ECN::ExpressionStatus MathsOperationSymbolProvider::Subtraction(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args)
    {
    if (2 != args.size())
        return ExprStatus_UnknownError;
    double arg1, arg2;
    DgnECSymbolProvider::ExtractArg(arg1, args[0]);
    DgnECSymbolProvider::ExtractArg(arg2, args[1]);

    evalResult.InitECValue().SetDouble(arg1-arg2);
    return ExprStatus_Success;
    }

/*---------------------------------------------------------------------------------**//**
* @description  This is Multiplication symbol implementation which multiplies two numbers and return result.
* @bsimethod                                                              Bentley Systems
+---------------+---------------+---------------+---------------+---------------+------*/
ECN::ExpressionStatus MathsOperationSymbolProvider::Multiplication(ECN::EvaluationResult& evalResult, EvaluationResultVector& args)
    {
    if (2 != args.size())
        return ExprStatus_UnknownError;
    double arg1, arg2;
    DgnECSymbolProvider::ExtractArg(arg1, args[0]);
    DgnECSymbolProvider::ExtractArg(arg2, args[1]);

    evalResult.InitECValue().SetDouble(arg1*arg2);
    return ExprStatus_Success;
    }

/*---------------------------------------------------------------------------------**//**
* @description  This is Division symbol implementation which divides two numbers and return result.
* @bsimethod                                                              Bentley Systems
+---------------+---------------+---------------+---------------+---------------+------*/
ECN::ExpressionStatus MathsOperationSymbolProvider::Division(ECN::EvaluationResult& evalResult, ECN::EvaluationResultVector& args)
    {
    if (2 != args.size())
        return ExprStatus_UnknownError;
    double arg1, arg2;
    DgnECSymbolProvider::ExtractArg(arg1, args[0]);
    DgnECSymbolProvider::ExtractArg(arg2, args[1]);

    evalResult.InitECValue().SetDouble(arg1/arg2);
    return ExprStatus_Success;
    }

Register Sample Symbol Provider

 MathsOperationSymbolProvider* NewSymbolProviderAtNative::m_mathsProvider = NULL;

 m_mathsProvider= new MathsOperationSymbolProvider();
 Bentley::DgnPlatform::DgnECSymbolProvider::GetProvider().RegisterSymbolProvider(*m_mathsProvider);

Create item type, item type property definitions and set EC Expression which consist of newly added symbols

 void CreateItemTypeWithExpression()
    {
     DgnFileP dgnFile = ISessionMgr::GetActiveDgnFile();

     ItemTypeLibraryPtr itemtypeLib = ItemTypeLibrary::Create(L"NativeLibrary", *dgnFile);

     m_itemSet = itemtypeLib->AddItemType(L"ItemType");

     CustomPropertyP prop1 = m_itemSet->AddProperty(L"prop1");
     prop1->SetType(CustomProperty::Type::Double);
     prop1->SetExpression(L"MathsOperation.Addition(4,3)");

     CustomPropertyP prop2 = m_itemSet->AddProperty(L"prop2");
     prop2->SetType(CustomProperty::Type::Double);
     prop2->SetExpression(L"MathsOperation.Subtraction(4,3)");

     CustomPropertyP prop3 = m_itemSet->AddProperty(L"prop3");
     prop3->SetType(CustomProperty::Type::Double);
     prop3->SetExpression(L"MathsOperation.Division(4,3)");

     CustomPropertyP prop4 = m_itemSet->AddProperty(L"prop4");
     prop4->SetType(CustomProperty::Type::Double);
     prop4->SetExpression(L"MathsOperation.Multiplication(4,3)");

     itemtypeLib->Write();
     }

 

Attach created Item Type to Element

 void NewSymbolProviderAtNative::AttachItemTypeToExpression() 
    {
     DgnModelR dgnModel = GetDefaultModel();     
     
     EditElementHandle  eeh;
     DSegment3d  segment;
     segment.Init (0, 0, 0, -100, -100, 0);
     LineHandler::CreateLineElement (eeh, nullptr, segment, dgnModel.Is3d (), dgnModel);
     eeh.AddToModel ();

     CustomItemHost itHostTarget(eeh, false);
     m_instanceSource = itHostTarget.ApplyCustomItem(*m_itemSet);
     m_instanceSource->WriteChanges();
    }

Read result of calculated property from attached instance

 ECValue result;
 ECObjectsStatus status = m_instanceSource->GetValue (result, arg.c_str());
 if (status == ECOBJECTS_STATUS_Success)
    {
    if (result.IsNull ())
     return;
    
    WString::Sprintf (message, L"%ls = %f", arg.c_str(), result.GetDouble ());
    }
 

Unregister Sample Symbol Provider  

 DgnECSymbolProvider::GetProvider().UnregisterSymbolProvider (*m_mathsProvider);  
 if(NULL != m_mathsProvider)
    { 
     delete m_mathsProvider;
     m_mathsProvider = NULL;
    } 
     

Please refer expression related links: