3D PLM PPR Hub Open Gateway |
Product Modeler |
Using the Provider MechanismNavigating through applicative containers and feature extensions |
Use Case |
AbstractThis article discusses the CAAPstProviders use case. This use case explains how to use the Product Modeler's "Providers" mechanism to integrate applicative data in the native behaviors of a Product document. |
This use case is intended to help you understand how to work with the Provider mechanism of the Product Modeler. The Provider mechanism allows an application to be integrated in the native behaviors of a product structure. For example, a product structure may not be aware of the specific contents of each applicative container or feature extension found in the Product document and yet still be capable of taking these into account when performing native tasks, such as navigation or visualization. Currently, four provider interfaces have been defined:
The implementation of any one of these interfaces is the actual provider. Each provider supports a specific algorithm. When a provider declaration is currently active, the implementation of the corresponding interface calls the method implemented by the provider. In this use case, you will see how to create and declare a provider in order to integrate one of the native behaviors listed above.
[Top]
CAAPstProviders is a use case of the CAAProductStructure.edu framework that illustrates ProductStructure framework capabilities.
[Top]
The goal of CAAPstProviders is to illustrate working with the Providers mechanism. The example illustrated by this use case deals with CATINavigateProvider implementations. However, the provider mechanism shown here is valid for all the other provider interfaces listed in the previous section.
The CAAPstProviders use case uses two files that must be found in the runtime view when executing the use case: "CAAPstExtCatalog.CATfct" and "CAAPstProvider.CATProduct".
The "CAAPstExtCatalog.CATfct" file contains the following extension definition:
Fig.1 CAAPstExtCatalog.CATfct
![]() |
The "CAAPstProvider.CATProduct" document structure can be viewed in two different ways, depending on whether we want to see it from a data storage view or from an interactive session view. The reason for this is that Provider declarations and extensions activations are not persistent tasks, i.e., they are not saved within the document data stream during the save operation. Therefore, relationships between applicative data may only be established during an interactive session.
Following is the contents of the document as seen from a data storage view:
Fig. 2 CAAPstProvider.CATProduct - Data StorageView
![]() |
Now, here is the image of the same document's contents after it has been loaded in an interactive session:
Fig. 3 CAAPstProvider.CATProduct - Interactive View
![]() |
Note that the applicative containers are not displayed in the navigation tree. Likewise, feature attributes that do not themselves point to other features are not displayed in the navigation tree either. What we do see in the navigation tree is that the "CAAOsmBook1" feature is aggregated by the "CAAPstProviderSubProduct1" and that it has a child called "CAAOsmChapter1" which is itself a feature actually aggregated by an extension of "CAAOsmBook1".
Now, let's try to understand how the mechanism works. As you can see by looking at the structure of the document in storage, the root product aggregates a sub-product. However, the sub-product does not directly aggregate the contents of the "CAAPstBFCont" applicative container as shown in the Interactive View. In order for the sub-product to aggregate the contents of "CAAPstBFCont", the implementation of CATINavigateObject on the product must return the children of "CAAPstBFCont": this is done using a provider.
Likewise, notice that the "CAAOsmChapter1" feature is not a direct child of "CAAOsmBook1", but rather the child of the extension of "CAAOsmBook1" called "CAAPstHistoricalNovel1". Because "CAAOsmBook1" does not necessarily know what, if any, extensions have been activated on it, it cannot retrieve its extensions' children directly from the CATINavigateObject implementation. A provider created especially for this purpose must be declared instead.
To summarize, then, the two objects implementing CATINavigateObject must call specific providers in order to aggregate certain children they have no specific knowledge of.
Providers themselves are called in a generic fashion. In other words, they are first declared to the document. Then, during the CATINavigateObject implementation(s), the list of active providers is retrieved and each provider is called in turn. It is within the provider code itself to determine the identity of the parent caller in order to retrieve the children of the specific object it is a provider for.
You will see in the details of the use case how this is actually done. But first, take a look at the diagram below representing the navigation process illustrated by this use case:
Fig. 4 CAAPstProviders Navigation Process
![]() |
[Top]
To launch CAAPstProviders, you will need to set up the build time environment, then compile the following modules:
Furthermore, verify that the runtime view contains:
Now, you will need to set up the runtime environment, and then launch a CATIA
session. This is fully described in the referenced article [1].
To launch the use case, simply select "File/Open" and
open the
CAAPstProvider.CATProduct document found in the resources + graphic runtime
directory.
[Top]
The main CAAPstProviders code is found in the CAAProductStructure.edu framework. It is composed of four modules as follows:
Windows |
InstallRootDirectory\CAAProductStructure.edu\CAAPstEduContInit.m,
CAAPstEduNavigBook.m, CAAPstEduNavigExtProv.m, CAAPstEduNavigFeatProv.m
and CAAPstEduCtxMenuProv.m |
Unix |
InstallRootDirectory/CAAProductStructure.edu/CAAPstEduContInit.m,
CAAPstEduNavigBook.m, CAAPstEduNavigExtProv.m, CAAPstEduNavigFeatProv.m
and CAAPstEduCtxMenuProv.m |
where InstallRootDirectory
is the root directory of your CAA V5
installation.
[Top]
There are four logical steps in CAAPstProviders:
We will now comment each of these sections by looking at the code.
[Top]
The provider implementation that returns the list of the children of the
"CAAPstBFCont" applicative container is found in the CAAPstAppliContProvider.cpp
file of the CAAPstEduNavigFeatProv.m module. It is an implementation class on
the CATINavigateProvider interface for a CATNull
late type.
In other words, no dictionary entry is necessary. This provider implements the
GetChildren
method defined by the CATINavigateProvider
interface. However, it only returns the list of children if the parent caller is
the sub-product called "CAAPstProviderSubProduct1".
Remember that the CATINavigateProvider implementations are called regardless of the current object. In other words, the CATINavigateObject implementation on "Product1" or on "CAAPstProviderSubProduct1" is the same so that the same provider will be called for each: It is the provider's responsibility to return the list of children depending on the proper parent.
1. Verify that the parent caller is "CAAPstProviderSubProduct1.1"
HRESULT CAAPstAppliContProvider::GetChildren(CATBaseUnknown * pObj, CATLISTP(CATBaseUnknown) ** pListChildren) { cout << "***** CAAPstAppliContProvider::GetChildren" << endl << flush; // This provider returns the list of the children found in the applicative // container of the document only if the caller is the subproduct // named "CAAPstProviderSubProduct1.1" // Retrieve a CATIProduct handle on the caller object CATIProduct *piProductOnObj = NULL; HRESULT rc = pObj -> QueryInterface (IID_CATIProduct, (void**) &piProductOnObj); if (SUCCEEDED(rc)) { // Retrieve the name of the product caller; if it is "CAAPstProviderSubProduct1.1" // then create and return the list of the children of the applicative container. CATUnicodeString productName; rc = piProductOnObj -> GetPrdInstanceName(productName); piProductOnObj -> Release(); piProductOnObj = NULL; if (productName != "CAAPstProviderSubProduct1.1") { cout << "The caller is not the aggregating subproduct." << endl << flush; return S_OK; } ... |
The parent caller is passed as an argument to the GetChildren
method (pObj). If a CATIProduct pointer can be retrieved on it, then use
the GetPrdInstanceName
method to retrieve its name. If the
product's name is "CAAPstProviderSubProduct1.1", then it is the proper parent
under which we want to list the contents of the base feature applicative
container. Otherwise, we return to the calling GetChildren
method.
[Top]
2. Retrieve the base feature applicative container.
... // Retrieve a pointer to the applicative container. CATILinkableObject *piLinkableOnObj = NULL; rc = pObj -> QueryInterface (IID_CATILinkableObject, (void**) &piLinkableOnObj); if (!SUCCEEDED(rc)) return E_FAIL; CATDocument* pDoc = piLinkableOnObj->GetDocument(); piLinkableOnObj -> Release(); piLinkableOnObj = NULL; CATUnicodeString appliContIdentifier("BaseFeatureContainer"); CATBaseUnknown *pApplicativeContainer = NULL; rc = ::CATGetApplicativeContainer (&pApplicativeContainer, pDoc, IID_CATIContainer, appliContIdentifier); if (SUCCEEDED(rc)) cout << "Applicative container retrieved OK" << endl << flush; else { cout << "ERROR in retrieving applicative container" << endl << flush; return E_FAIL; } // Retrieve a pointer to CATIClientContainer in order to list the members // of the applicative container. CATIClientContainer *piClientOnAppliCont = NULL; rc = pApplicativeContainer -> QueryInterface(IID_CATIClientContainer, (void**) &piClientOnAppliCont); pApplicativeContainer -> Release(); pApplicativeContainer = NULL; if (NULL == piClientOnAppliCont) { cout << "ERROR in retrieving container pointer" << endl << flush; return E_FAIL; } else cout << "CATIClientContainer pointer retrieved OK" << endl << flush; ... |
In order to retrieve a pointer to the applicative container, a CATDocument
pointer is necessary. This pointer can be retrieved using the
CATILinkableObject::GetDocument
method on the parent caller.
Then, the CATGetApplicativeContainer
global function can be
executed, using the applicative container identifier as argument. Now, acquire a
CATIClientContainer pointer on the applicative container to be used in
order to list the members of the container.
[Top]
3. Construct the list of the direct children of the sub-product (features of the applicative container).
... // Retrieve the list of features in the applicative container CATUnicodeString clientId("CAAOsmClientId"); CATListPtrCATBaseUnknown *pMemberList = new CATListPtrCATBaseUnknown(); rc = piClientOnAppliCont -> ListMembers(IID_CATISpecObject, clientId, &pMemberList); piClientOnAppliCont -> Release(); piClientOnAppliCont = NULL; if (SUCCEEDED(rc)) cout << "Member list retrieved OK" << endl << flush; else { cout << "ERROR in retrieving member list" << endl << flush; return E_FAIL; } cout << "Number of members in applicative container: " << pMemberList->Size() << endl << flush; for(int i=1;i<=pMemberList->Size();i++) { (*pListChildren)->Append((*pMemberList)[i]); } delete pMemberList; pMemberList = NULL; } return S_OK; } |
Use the ListMembers
method of CATIClientContainer to list
the members of type CATISpecObject found in the applicative container.
Then, loop through this list and append each member to the list of children to
be returned.
[Top]
Implementing CATINavigateProvider for the "CAAOsmBook" Feature's Extension
The provider implementation that returns the list of the children of the
"CAAOsmBook1" feature is found in the CAAPstBookExtProvider.cpp file of the
CAAPstEduNavigExtProv.m module. It is an implementation class on the
CATINavigateProvider interface for a CATNull
late type. In
other words, no dictionary entry is necessary. This provider implements the
GetChildren
method defined by the CATINavigateProvider
interface. However, it only returns the list of children if the parent caller is
the "CAAOsmBook1" feature: it knows that an extension aggregating another
feature instance on the "NovelChapter" attribute may be active.
Remember that the CATINavigateProvider implementations are called regardless of the current object. In other words, the CATINavigateObject implementation on "CAAOsmBook" will call all of the providers that may be currently active, whether or not they are pertinent to the "CAAOsmBook1" feature.
1. Verify that the parent caller is "CAAOsmBook1".
HRESULT CAAPstBookExtProvider::GetChildren(CATBaseUnknown * pObj, CATLISTP(CATBaseUnknown) ** pListChildren) { cout << "***** CAAPstBookExtProvider::GetChildren" << endl << flush; // This provider returns the children of the extension of the feature named // "CAAOsmBook1". If the caller object is not this feature, the provider // simply returns. // Retrieve a CATISpecObject pointer on the caller object. CATISpecObject *piSpecObjectOnExtFeature = NULL; HRESULT rc = pObj -> QueryInterface (IID_CATISpecObject, (void**) &piSpecObjectOnExtFeature); if (!SUCCEEDED(rc)) return S_OK; CATUnicodeString specName = piSpecObjectOnExtFeature -> GetName(); if (specName == "CAAOsmBook1") { ... |
Retrieve a CATISpecObject pointer on the parent caller. If the pointer
is correctly retrieved, use the GetName
method to test whether the
parent caller is actually the "CAAOsmBook1" feature, thus being applicable to
this provider.
[Top]
2. Retrieve and return the value of the "NovelChapter" attribute.
... // Retrieve CATIOsmExtendable on the base feature CATIOsmExtendable *piExtendableOnExtFeature = NULL; rc = piSpecObjectOnExtFeature -> QueryInterface(IID_CATIOsmExtendable, (void**) &piExtendableOnExtFeature); piSpecObjectOnExtFeature -> Release(); piSpecObjectOnExtFeature = NULL; if (FAILED(rc)) return E_FAIL; // Retrieve CATISpecAttrAccess on the extension const char *extensionName = "CAAPstHistoricalNovel"; CATISpecAttrAccess *piAccessOnExtension = NULL; rc = piExtendableOnExtFeature->QueryExtension(extensionName, IID_CATISpecAttrAccess, (void**) &piAccessOnExtension); piExtendableOnExtFeature->Release(); piExtendableOnExtFeature = NULL; if (FAILED(rc)) return E_FAIL; // Retrieve the attribute key for the extensions component attribute CATISpecAttrKey *piKeyNovelChapter = piAccessOnExtension -> GetAttrKey("NovelChapter"); if (NULL == piKeyNovelChapter) return E_FAIL; // Retrieve the feature value of the attribute CATBaseUnknown *pChapter = NULL; pChapter = piAccessOnExtension -> GetSpecObject (piKeyNovelChapter, 1); piAccessOnExtension -> Release(); piAccessOnExtension = NULL; piKeyNovelChapter -> Release(); piKeyNovelChapter = NULL; // Add the feature to the list of children if (NULL != pChapter) { if (NULL == *pListChildren) *pListChildren = new CATLISTP(CATBaseUnknown); (*pListChildren)->Append(pChapter); } else return E_FAIL; ... |
To get an attribute value on the "CAAPstHistoricalNovel" extension, we first need to retrieve a CATISpecAttrAccess pointer on the extension itself. This is done by retrieving a CATIOsmExtendable pointer on the feature and then use it to query for a "CAAPstHistoricalNovel" extension, to be returned as a CATISpecAttrAccess pointer.
With this CATISpecAttrAccess pointer on the extension,
get the attribute key with the GetAttrKey
method for the "NovelChapter" attribute, and
then use the
GetSpecObject
method to retrieve a pointer to the aggregated
feature. It is this feature that is appended to the list of the children of the
"CAAOsmBook1" parent.
[Top]
Implementing CATInit for the "CAAPstBFCont" Applicative Container
The implementation of the CATInit interface on the "CAAPstBFCont"
applicative container is found in the CAAEPstInit.cpp source code of the
CAAPstEduContInit.m module. It is called upon the selection of the
File/Open
command when the CAAPstProvider.CATProduct document is loaded
into the interactive session. Implementing this interface is necessary because,
as you have already seen, provider declarations and extensions activation are
not persistent operations, i.e., they are not saved with the document and must,
therefore, be re-initialized at the start of an interactive session.
1. Retrieve the current document.
void CAAEPstInit::Init (CATBoolean iDestroyExistingData) { cout << "***** CAAPstBFCont: CAAEPstInit::Init" << endl << flush; // Retrieve the current document CATILinkableObject *piLinkableOnCont = NULL; HRESULT rc = this -> QueryInterface(IID_CATILinkableObject, (void**) &piLinkableOnCont); if (!SUCCEEDED(rc)) return; CATDocument *pDoc = piLinkableOnCont -> GetDocument(); piLinkableOnCont -> Release(); piLinkableOnCont = NULL; if (NULL == pDoc) return; |
The current document is retrieved using the GetDocument
method of CATILinkableObject, a pointer to which is acquired on the
current object (the applicative container) using QueryInterface
.
[Top]
2. Declare Providers.
// Retrieve a CATIProviders pointer on the current document CATIProviders *piProvidersOnDocument = NULL; rc = pDoc -> QueryInterface(IID_CATIProviders, (void**) &piProvidersOnDocument); if (!SUCCEEDED(rc)) return; // Declare provider to list the children of the applicative container CAAPstAppliContProvider *pAppliContProvider = new CAAPstAppliContProvider(); rc = piProvidersOnDocument -> AddProvider (IID_CATINavigateProvider, pAppliContProvider); if (!SUCCEEDED(rc)) return; else cout << "Applicative container provider declared OK" << endl; // Declare provider for the contextual menu CAAPstCtxMenuProvider *pCtxMenuProvider = new CAAPstCtxMenuProvider(); rc = piProvidersOnDocument -> AddProvider (IID_CATICtxMenuProvider, pCtxMenuProvider); ... if (!SUCCEEDED(rc)) return; else cout << "Applicative container provider declared OK" << endl; // Declare provider to list the children of an extended feature CAAPstBookExtProvider *pBookExtProvider = new CAAPstBookExtProvider(); rc = piProvidersOnDocument -> AddProvider (IID_CATINavigateProvider, pBookExtProvider); ... if (!SUCCEEDED(rc)) return; else cout << "Extended feature provider declared OK" << endl; |
Providers are declared on a CATDocument object using the
CATIProviders::AddProvider
method of ProductStructure. A
CATIProviders pointer must, therefore, be acquired on the current document.
Then, each provider class is first instantiated and then declared to the
document using the AddProvider
method. This method takes as a first
argument the name of the provider interface, in this case
CATINavigateProvider, and in the second argument the pointer to the provider
class instance. Remember that the provider declaration is not persistent: if the
document is re-opened in another CATIA session, these provider declarations will
no longer be active.
[Top]
3. Activate extensions
// Activate extensions (only required for former extensions) const char *appliContIdentifier = "ExtensionContainer"; rc = CATOsmExtensionServices::CATActivateExtensions(pDoc, appliContIdentifier); if (FAILED(rc)) { cout << "ERROR activating extensions" << endl; return ; } else cout << "Extensions activated OK" << endl; return; } |
The last task to be executed by the Init
method is to activate
the former extensions of the document. Please note that this step is only
required for former extensions as newer extensions are always active. This is
done by calling the CATActivateExtensions method of the
CATOsmExtensionServices class with a CATDocument pointer on the document and the
container name "ExtensionContainer".
Now, all the providers and extensions needed are active. During the display process, the navigation tree will be constructed by calling the existing implementations of CATINavigateObject for each late type encountered.
[Top]
Implementing CATINavigateObject for the "CAAOsmBook" Feature
The implementation of CATINavigateObject for the "CAAOsmBook" late type is found in the CAAEPstNavigateObject.cpp source file of the CAAPstEduNavigBook.m module.In this implementation, you will see that several providers are actually called but only one is valid for the "CAAOsmBook" parent type. As you saw in the previous sections, the provider caller is not responsible for knowing whether the provider being called is relevant for the given parent type.
1. List the active providers.
CATListValCATBaseUnknown_var * CAAEPstNavigateObject::GetChildren() { cout << "***** CAAOsmBook: CAAEPstNavigateObject::GetChildren" << endl; CATListValCATBaseUnknown_var *pList = NULL ; pList = new CATListValCATBaseUnknown_var(); // Retrieve the list of declared providers CATLISTP(CATBaseUnknown) *pListProvider = new CATLISTP(CATBaseUnknown); CATLISTP(CATBaseUnknown) *pListChildren = NULL; // Retrieve a CATILinkableObject pointer on the CAAOsmBook feature in order // to get a CATDocument pointer to the document which contains it. CATILinkableObject * piLinkableOnExtFeature = NULL; HRESULT rc = this -> QueryInterface (IID_CATILinkableObject, (void**) &piLinkableOnExtFeature); if (!SUCCEEDED(rc)) return pList; CATDocument *pDoc = piLinkableOnExtFeature -> GetDocument(); piLinkableOnExtFeature -> Release(); piLinkableOnExtFeature = NULL; if (NULL == pDoc) return pList; // Retrieve a CATIProviders pointer on the document. CATIProviders * piProvidersOnDoc = NULL; rc = pDoc -> QueryInterface (IID_CATIProviders, (void**) &piProvidersOnDoc); if (!SUCCEEDED(rc)) return pList; // Retrieve a list of the providers that have been declared to the document. rc = piProvidersOnDoc -> ListProviders (CATINavigateProvider::ClassId(), &pListProvider); piProvidersOnDoc -> Release(); piProvidersOnDoc = NULL; ... |
First of all, it is necessary to initialize the different array variables for
the different lists of providers and children that will be retrieved. Then,
using the "this" pointer, get a CATDocument pointer to the document in
which "this" is contained using the GetDocument
method of
CATILinkableObject. Now, using the CATDocument pointer, we can get a
pointer to CATIProviders and execute the ListProviders
method to retrieve a list of all of the providers declared to the document for
the CATINavigateProvider interface.
[Top]
2. Construct the list of children retrieved from every active provider.
... // For each declared provider, get the CATINavigateProvider interface pointer to // execute the GetChildren method and append the list of children returned to the // list to be constructed. if(SUCCEEDED(rc)) { int size = pListProvider->Size(); for (int i=size; i>=1; i--) { // Retrieve a CATINavigateProvider pointer on the provider CATINavigateProvider *piNav = NULL; rc = (*pListProvider)[i] -> QueryInterface(IID_CATINavigateProvider, (void**) &piNav); if (SUCCEEDED(rc)) { pListChildren = new CATLISTP(CATBaseUnknown); // Retrieve the list of the children of the current object piNav -> GetChildren (this, &pListChildren); if (NULL != pListChildren) for(int j=1;j<=pListChildren->Size();j++) { if((*pListChildren)[j]) { // Append the child to the list being constructed. CATINavigateObject_var navChild ((*pListChildren)[j]); if(navChild!=NULL_var) pList->Append(navChild); (*pListChildren)[j]->Release(); } } delete pListChildren; pListChildren = NULL; piNav->Release(); } } delete pListProvider; } return pList; } |
If the list of providers has correctly been retrieved, loop through the list
and call the GetChildren
method of each provider. (Remember that
the provider will return a valuated list of children depending on the identity
of the parent caller.) Then, if the list of children is not null, each child is
appended to the actual list of children that will be returned to the navigator.
[Top]
The provider implementation that adds a contextual menu entry labeled
"CAACtxMenuCmd" is in the CAAPstCtxMenuProvider.cpp file of the
CAAPstEduCtxMenuProv.m module. It is an implementation class on the
CATICtxMenuProvider interface for a CATNull
late type. In other
words, no dictionary entry is necessary. This provider implements the
GetContextualMenu
method defined by the CATICtxMenuProvider
interface. However, it only creates a contextual menu entry if the caller is the
sub-product named "CAAPstProviderSubProduct1.1".
... HRESULT CAAPstCtxMenuProvider::GetContextualMenu(CATBaseUnknown *iObj, CATCmdContainer *oCtxMenu) { // Retrieve a CATIProduct handle on the caller object CATIProduct *piProductOnObj = NULL; if (NULL == iObj) return E_FAIL; HRESULT rc = iObj -> QueryInterface (IID_CATIProduct, (void**) &piProductOnObj); if (SUCCEEDED(rc)) { // Retrieve the name of the product caller // If it is "CAAPstProviderSubProduct1.1" // then create a entry in the contextual menu CATUnicodeString productName; rc = piProductOnObj->GetPrdInstanceName(productName); piProductOnObj->Release(); piProductOnObj = NULL; if (productName == "CAAPstProviderSubProduct1.1") { NewAccess(CATCmdStarter, pStarter, CAACtxMenuStarter); CATString cmdName("CAACtxMenuCmd"); SetAccessCommand(pStarter, cmdName); SetAccessChild(oCtxMenu, pStarter); return S_OK; } } return E_FAIL; } ... |
The purpose of this implementation is to illustrate the CATICtxMenuProvider interface and as such does not provide any meaningful user interaction. Please consult this article [2] for more information on contextual menus.
Fig. 5 CATICtxMenuProvider implementation
[Top]
A Provider is an implementation class of one of the supplied provider interfaces. It is declared to the document at runtime. The implementations of native interfaces use active providers to complement their behaviors: in other words, providers will return specific information on applicative data that the parent caller object has no direct knowledge of.
[Top]
[1] | Building and Launching a CAA V5 Use Case |
[2] | Inserting Commands in Contextual Menus |
[Top] |
Version: 3 [Jul 2004] | CATICtxMenuProvider Added |
Version: 2 [Feb 2004] | CATIIconProvider Removed |
Version: 1 [May 2001] | Document created |
[Top] |
Copyright © 2001, Dassault Systèmes. All rights reserved.