XCO Library : lire et traiter les metadonnees par programmation

Catégorie
ABAP Cloud
Publié
Auteur
Johannes

La bibliotheque XCO (Extension Components) est une collection d’APIs moderne et orientee objet dans ABAP Cloud qui permet l’acces programmatique aux objets du repository et aux metadonnees. Avec XCO, vous pouvez lire les objets DDIC, analyser les vues CDS, gerer les packages et meme generer des objets de developpement.

Qu’est-ce que XCO ?

XCO signifie Extension Components et fournit une API unifiee et compatible cloud pour :

Domaine XCODescriptionClasse principale
DDICObjets Data DictionaryXCO_CP_ABAP_DICTIONARY
CDSVues Core Data ServicesXCO_CP_CDS
RepositoryPackages, TransportXCO_CP_ABAP_REPOSITORY
GenerationGeneration d’objetsXCO_CP_GENERATION
SystemInfos systeme, UserXCO_CP

Avantages de XCO

  • Cloud-ready : Toutes les APIs sont released pour ABAP Cloud (C1)
  • Interface fluide : Appels de methodes lisibles et chainables
  • API coherente : Patterns uniformes dans tous les domaines
  • Type-safe : Typage fort avec enumerations

XCO pour les objets DDIC

Lire les tables de base de donnees

Avec XCO, vous pouvez lire les metadonnees des tables de base de donnees au moment de l’execution :

CLASS zcl_xco_ddic_example DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_xco_ddic_example IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Lire la table
DATA(lo_table) = xco_cp_abap_dictionary=>database_table( 'SFLIGHT' ).
" Verifier si la table existe
IF lo_table->exists( ).
" Lire le contenu de la table (champs)
DATA(lt_fields) = lo_table->fields->all->get( ).
LOOP AT lt_fields INTO DATA(lo_field).
DATA(lv_field_name) = lo_field->name.
DATA(lv_data_element) = lo_field->content(
)->get_data_element( )->name.
out->write( |Champ: { lv_field_name } -> { lv_data_element }| ).
ENDLOOP.
" Determiner le type de table
DATA(lv_delivery_class) = lo_table->content(
)->get_delivery_class( )->value.
out->write( |Delivery Class: { lv_delivery_class }| ).
ENDIF.
ENDMETHOD.
ENDCLASS.

Analyser les elements de donnees

" Lire l'element de donnees
DATA(lo_data_element) = xco_cp_abap_dictionary=>data_element( 'CARRID' ).
IF lo_data_element->exists( ).
" Obtenir le contenu
DATA(lo_content) = lo_data_element->content( ).
" Determiner le domaine
DATA(lv_domain) = lo_content->get_domain( )->name.
" Lire les libelles de champ
DATA(ls_short_text) = lo_content->get_short_text( ).
DATA(ls_medium_text) = lo_content->get_medium_text( ).
DATA(ls_long_text) = lo_content->get_long_text( ).
out->write( |Domaine: { lv_domain }| ).
out->write( |Texte court: { ls_short_text-text }| ).
ENDIF.

Domaines avec valeurs fixes

" Lire le domaine avec valeurs fixes
DATA(lo_domain) = xco_cp_abap_dictionary=>domain( 'S_CONN_TYPE' ).
IF lo_domain->exists( ).
" Lire les Fixed Values
DATA(lt_fixed_values) = lo_domain->fixed_values->all->get( ).
LOOP AT lt_fixed_values INTO DATA(lo_fixed_value).
DATA(lv_low) = lo_fixed_value->value.
DATA(lv_description) = lo_fixed_value->content(
)->get_description( ).
out->write( |{ lv_low }: { lv_description }| ).
ENDLOOP.
ENDIF.

XCO pour les vues CDS

XCO permet de lire les metadonnees des vues CDS, y compris les champs, les associations et les annotations.

Lire les champs d’une vue CDS

CLASS zcl_xco_cds_example DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_xco_cds_example IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Lire la View Entity CDS
DATA(lo_view) = xco_cp_cds=>view_entity( 'I_PRODUCT' ).
IF lo_view->exists( ).
out->write( '=== Vue CDS: I_PRODUCT ===' ).
" Tous les champs de la vue
DATA(lt_fields) = lo_view->fields->all->get( ).
out->write( |Nombre de champs: { lines( lt_fields ) }| ).
LOOP AT lt_fields INTO DATA(lo_field).
DATA(lv_name) = lo_field->name.
DATA(lo_content) = lo_field->content( ).
" Determiner le type de donnees (si disponible)
TRY.
DATA(lv_data_element) = lo_content->get_data_element( )->name.
out->write( | { lv_name } -> { lv_data_element }| ).
CATCH cx_root.
out->write( | { lv_name } (pas de Data Element)| ).
ENDTRY.
ENDLOOP.
ELSE.
out->write( 'La vue n''existe pas.' ).
ENDIF.
ENDMETHOD.
ENDCLASS.

Lire les associations

" Vue CDS avec associations
DATA(lo_view) = xco_cp_cds=>view_entity( 'I_SALESORDER' ).
IF lo_view->exists( ).
" Toutes les associations de la vue
DATA(lt_associations) = lo_view->associations->all->get( ).
LOOP AT lt_associations INTO DATA(lo_assoc).
DATA(lv_assoc_name) = lo_assoc->name.
DATA(lo_target) = lo_assoc->content( )->get_target( ).
DATA(lv_target_name) = lo_target->name.
" Cardinalite
DATA(lo_cardinality) = lo_assoc->content( )->get_cardinality( ).
DATA(lv_min) = lo_cardinality->min.
DATA(lv_max) = lo_cardinality->max.
out->write( |Association { lv_assoc_name } -> { lv_target_name }| ).
out->write( | Cardinalite: [{ lv_min }..{ lv_max }]| ).
ENDLOOP.
ENDIF.

Lire les annotations

" Lire les annotations d'une vue CDS
DATA(lo_view) = xco_cp_cds=>view_entity( 'I_BUSINESSPARTNER' ).
IF lo_view->exists( ).
" Annotations au niveau de la vue
DATA(lt_annotations) = lo_view->annotations->all->get( ).
LOOP AT lt_annotations INTO DATA(lo_annotation).
DATA(lv_anno_name) = lo_annotation->name.
TRY.
DATA(lv_value) = lo_annotation->value->get_string( ).
out->write( |@{ lv_anno_name }: { lv_value }| ).
CATCH cx_root.
out->write( |@{ lv_anno_name }: (valeur complexe)| ).
ENDTRY.
ENDLOOP.
ENDIF.

XCO pour les packages et le transport

Lire les informations de package

CLASS zcl_xco_package_example DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_xco_package_example IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Lire le package
DATA(lo_package) = xco_cp_abap_repository=>package->for( 'ZRAP_DEMO' ).
IF lo_package->exists( ).
" Attributs du package
DATA(lo_content) = lo_package->read( ).
DATA(lv_description) = lo_content-short_description.
DATA(lv_sw_component) = lo_content-software_component.
out->write( |Package: ZRAP_DEMO| ).
out->write( |Description: { lv_description }| ).
out->write( |Composant logiciel: { lv_sw_component }| ).
" Lister les objets du package
out->write( '--- Objets du package ---' ).
" Classes
DATA(lt_classes) = lo_package->objects->where( xco_cp_abap=>object_type->clas
)->all->get( ).
LOOP AT lt_classes INTO DATA(lo_class).
out->write( |CLAS: { lo_class->name }| ).
ENDLOOP.
" Vues CDS
DATA(lt_ddls) = lo_package->objects->where( xco_cp_abap=>object_type->ddls
)->all->get( ).
LOOP AT lt_ddls INTO DATA(lo_ddls).
out->write( |DDLS: { lo_ddls->name }| ).
ENDLOOP.
ENDIF.
ENDMETHOD.
ENDCLASS.

Informations de transport

" Lire l'ordre de transport
DATA(lo_transport) = xco_cp_cts=>transport->for( 'NPLK900001' ).
IF lo_transport->exists( ).
DATA(lo_properties) = lo_transport->properties( ).
DATA(lv_owner) = lo_properties->get_owner( ).
DATA(lv_description) = lo_properties->get_description( ).
DATA(lv_status) = lo_properties->get_status( )->value.
out->write( |Transport: NPLK900001| ).
out->write( |Proprietaire: { lv_owner }| ).
out->write( |Statut: { lv_status }| ).
ENDIF.
" Transports systeme actuels de l'utilisateur
DATA(lt_transports) = xco_cp_cts=>transports->modifiable->owned_by(
xco_cp=>sy->user( )
)->where( xco_cp_transport=>type->request
)->all->get( ).
LOOP AT lt_transports INTO DATA(lo_my_transport).
out->write( |Mon transport: { lo_my_transport->value }| ).
ENDLOOP.

Generation d’objets de developpement

XCO permet egalement la generation programmatique d’objets ABAP - une fonctionnalite puissante pour les generateurs et les templates.

Generer une classe

CLASS zcl_xco_generation_example DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS generate_class
IMPORTING
iv_class_name TYPE sxco_ao_object_name
iv_package TYPE sxco_package
iv_transport TYPE sxco_transport
RAISING
cx_xco_gen_put_exception.
ENDCLASS.
CLASS zcl_xco_generation_example IMPLEMENTATION.
METHOD generate_class.
" Environnement de generation pour le transport
DATA(lo_environment) = xco_cp_generation=>environment->dev_system(
iv_transport
).
" Preparer l'operation PUT
DATA(lo_put) = lo_environment->create_put_operation( ).
" Specifier la classe
DATA(lo_class_spec) = lo_put->for-clas->add_object( iv_class_name
)->set_package( iv_package
)->create_form_specification( ).
" Definition de la classe
lo_class_spec->set_short_description( 'Generated by XCO' ).
lo_class_spec->definition->set_final( ).
lo_class_spec->definition->set_create_public( ).
" Ajouter une interface
lo_class_spec->definition->add_interface( 'IF_OO_ADT_CLASSRUN' ).
" Section publique avec methode
lo_class_spec->definition->section-public->add_method( 'PROCESS"
)->set_returning( VALUE #(
data_type = xco_cp_abap=>type-built_in->string
) ).
" Implementation
lo_class_spec->implementation->add_method( 'IF_OO_ADT_CLASSRUN~MAIN"
)->set_source( VALUE #(
( `out->write( 'Hello from generated class!' ).` )
) ).
lo_class_spec->implementation->add_method( 'PROCESS"
)->set_source( VALUE #(
( `result = |Processed at { sy-datum }|.` )
) ).
" Executer la generation
lo_put->execute( ).
ENDMETHOD.
ENDCLASS.

Generer une interface

METHOD generate_interface.
DATA(lo_environment) = xco_cp_generation=>environment->dev_system(
iv_transport
).
DATA(lo_put) = lo_environment->create_put_operation( ).
" Specifier l'interface
DATA(lo_intf_spec) = lo_put->for-intf->add_object( 'ZIF_GENERATED"
)->set_package( 'ZPACKAGE"
)->create_form_specification( ).
lo_intf_spec->set_short_description( 'Generated Interface' ).
" Ajouter des methodes
lo_intf_spec->add_method( 'GET_DATA"
)->set_returning( VALUE #(
data_type = xco_cp_abap=>type-built_in->string
) ).
lo_intf_spec->add_method( 'SET_DATA"
)->add_parameter( 'IV_VALUE"
)->set_importing(
)->set_data_type( xco_cp_abap=>type-built_in->string ).
lo_put->execute( ).
ENDMETHOD.

Generer une table de base de donnees

METHOD generate_table.
DATA(lo_environment) = xco_cp_generation=>environment->dev_system(
iv_transport
).
DATA(lo_put) = lo_environment->create_put_operation( ).
" Specifier la table
DATA(lo_table_spec) = lo_put->for-tabl-for-database_table->add_object(
'ZGENERATED_TAB"
)->set_package( 'ZPACKAGE"
)->create_form_specification( ).
lo_table_spec->set_short_description( 'Generated Table' ).
lo_table_spec->set_delivery_class( xco_cp_database_table=>delivery_class->a ).
lo_table_spec->set_data_maintenance( xco_cp_database_table=>data_maintenance->allowed ).
" Ajouter des champs
lo_table_spec->add_field( 'CLIENT"
)->set_type( xco_cp_abap_dictionary=>data_element( 'MANDT' )
)->set_key_indicator( ).
lo_table_spec->add_field( 'ID"
)->set_type( xco_cp_abap_dictionary=>built_in_type->numc( 10 )
)->set_key_indicator( ).
lo_table_spec->add_field( 'DESCRIPTION"
)->set_type( xco_cp_abap_dictionary=>data_element( 'TEXT255' ) ).
lo_table_spec->add_field( 'CREATED_AT"
)->set_type( xco_cp_abap_dictionary=>data_element( 'TIMESTAMPL' ) ).
lo_put->execute( ).
ENDMETHOD.

Exemple pratique : generateur de documentation

Cet exemple montre comment generer automatiquement une documentation pour tous les objets d’un package avec XCO :

CLASS zcl_package_documentation DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
METHODS generate_documentation
IMPORTING
iv_package TYPE sxco_package
RETURNING
VALUE(rt_doc) TYPE string_table.
ENDCLASS.
CLASS zcl_package_documentation IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(lt_documentation) = generate_documentation( 'ZRAP_DEMO' ).
LOOP AT lt_documentation INTO DATA(lv_line).
out->write( lv_line ).
ENDLOOP.
ENDMETHOD.
METHOD generate_documentation.
DATA(lo_package) = xco_cp_abap_repository=>package->for( iv_package ).
IF NOT lo_package->exists( ).
APPEND |Package { iv_package } non trouve.| TO rt_doc.
RETURN.
ENDIF.
APPEND |# Documentation Package: { iv_package }| TO rt_doc.
APPEND || TO rt_doc.
" Documenter les classes
APPEND |## Classes| TO rt_doc.
DATA(lt_classes) = lo_package->objects->where(
xco_cp_abap=>object_type->clas
)->all->get( ).
LOOP AT lt_classes INTO DATA(lo_class_obj).
DATA(lo_class) = xco_cp_abap=>class( CONV #( lo_class_obj->name ) ).
IF lo_class->exists( ).
DATA(lo_class_content) = lo_class->content( ).
DATA(lv_description) = lo_class_content->get_short_description( ).
APPEND |### { lo_class_obj->name }| TO rt_doc.
APPEND |{ lv_description }| TO rt_doc.
APPEND || TO rt_doc.
" Lister les methodes publiques
DATA(lt_methods) = lo_class->definition->section-public->components->method->all->get( ).
IF lt_methods IS NOT INITIAL.
APPEND |**Methodes publiques:**| TO rt_doc.
LOOP AT lt_methods INTO DATA(lo_method).
APPEND |- { lo_method->name }| TO rt_doc.
ENDLOOP.
APPEND || TO rt_doc.
ENDIF.
ENDIF.
ENDLOOP.
" Documenter les vues CDS
APPEND |## Vues CDS| TO rt_doc.
DATA(lt_ddls) = lo_package->objects->where(
xco_cp_abap=>object_type->ddls
)->all->get( ).
LOOP AT lt_ddls INTO DATA(lo_ddls_obj).
DATA(lo_view) = xco_cp_cds=>view_entity( CONV #( lo_ddls_obj->name ) ).
IF lo_view->exists( ).
DATA(lt_fields) = lo_view->fields->all->get( ).
APPEND |### { lo_ddls_obj->name }| TO rt_doc.
APPEND |Champs: { lines( lt_fields ) }| TO rt_doc.
APPEND || TO rt_doc.
ENDIF.
ENDLOOP.
" Documenter les interfaces
APPEND |## Interfaces| TO rt_doc.
DATA(lt_intfs) = lo_package->objects->where(
xco_cp_abap=>object_type->intf
)->all->get( ).
LOOP AT lt_intfs INTO DATA(lo_intf_obj).
APPEND |- { lo_intf_obj->name }| TO rt_doc.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Bonnes pratiques

1. Verification d’existence

Verifiez toujours si un objet existe avant d’y acceder :

DATA(lo_table) = xco_cp_abap_dictionary=>database_table( 'ZTABLE' ).
IF lo_table->exists( ).
" Acces securise
DATA(lo_content) = lo_table->content( ).
ENDIF.

2. Gestion des exceptions

TRY.
DATA(lv_value) = lo_annotation->value->get_string( ).
CATCH cx_xco_runtime_exception INTO DATA(lx_error).
" Annotation complexe ou autre erreur
DATA(lv_message) = lx_error->get_text( ).
ENDTRY.

3. Utiliser l’interface fluide

XCO est optimise pour les appels chaines :

" Bon: Fluide
DATA(lt_public_methods) = xco_cp_abap=>class( 'ZCL_MYCLASS"
)->definition->section-public->components->method->all->get( ).
" Moins bon: Beaucoup de variables intermediaires
DATA(lo_class) = xco_cp_abap=>class( 'ZCL_MYCLASS' ).
DATA(lo_definition) = lo_class->definition.
DATA(lo_public) = lo_definition->section-public.
" ...

4. Performance pour les operations en masse

" Pour de nombreux objets: filtrer d'abord, puis lire
DATA(lt_classes) = lo_package->objects->where(
xco_cp_abap=>object_type->clas
)->where( xco_cp_object=>object_name->range( VALUE #(
sign = 'I' option = 'CP' low = 'ZCL_*' )
) )->all->get( ).

Apercu XCO : points d’entree importants

Point d’entreeDescription
XCO_CP_ABAP_DICTIONARYDDIC : Tables, Views, Elements de donnees, Domaines
XCO_CP_CDSVues CDS, Entities, Abstracts
XCO_CP_ABAPClasses, Interfaces, Types d’objets
XCO_CP_ABAP_REPOSITORYPackages, Objets du repository
XCO_CP_CTSChange and Transport System
XCO_CP_GENERATIONGeneration d’objets
XCO_CPSysteme, User, Utilitaires

Conclusion

La bibliotheque XCO est un outil indispensable pour le developpement ABAP Cloud avance :

  • Analyse des metadonnees : Comprendre la structure de votre systeme par programmation
  • Generateurs de code : Automatiser la creation d’objets de developpement
  • Documentation : Generer automatiquement de la documentation technique
  • Outillage : Construire vos propres outils de developpement

Avec l’interface fluide et la structure API coherente, XCO est a la fois puissant et agreable a utiliser.

Articles connexes