Difference between revisions of "Writing Professional Components"

From Apache OpenOffice Wiki
Jump to: navigation, search
m (Creating the header files)
 
(70 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{Documentation/Caution|This chapter is under construction for a while because I don't master all the topics evocated here at the moment. [[User:SergeMoutou|SergeMoutou]] 12:20, 3 August 2009 (UTC)}}
+
{{Warn|This chapter is under construction for a while because I don't master all the topics evocated here at the moment. [[User:SergeMoutou|SergeMoutou]] 12:20, 3 August 2009 (UTC)}}
Writing components is not as easy as developping binary executable. When an exception occurs in your component a recovery process of your document begins. This process is too long to try some code and correct the errors. When designing a component, it would be a good idea to conceive it as a binary executable before to change it into a component. In this chapter we will discuss this idea and also the exceptions. But before starting we examine at first the inspection tools.
+
Writing components is not as easy as developping binary executable. When an exception occurs in your component a recovery process of your document begins. This process is too long to try some code and correct the errors. When designing a component, it would be a good idea to conceive it as a binary executable before to change it into a component. In this chapter we will discuss this idea and also the exceptions. But before starting we examine at first the inspection tools. why ? Because there is a lot of things you cannot do without an inspection tool. In fact there is always an other way : to read the documentation but it can take hours and hours to solve your problem with reading documentation and only ten minutes with an inspection tool.
 
=Java Inspector=
 
=Java Inspector=
I have already tackled [[Object Inspector|Java Inspector]] in this document ([[Programming_OooWriter#Going_further_with_Inspector|see here]]). But in this section we will discuss how we can use Java Inspector in a component.
+
I have already tackled [[Object Inspector|Java Inspector]] in this document ([[Programming_OooWriter#Going_further_with_Inspector|see here]]). But we used in fact the addon part of the component because it was called with the corresponding button, not with code. In this section we will discuss how we can use Java Inspector in a component, to put it differently, how to call Java inspector with C++ code ?
 
==Creating the Header Files==
 
==Creating the Header Files==
Creating header files could be done with exporting the rdb with extension manager and then using <code>cppumaker</code> tool. Because the service name is "org.openoffice.InstanceInspector" put the created header files in "<OOo_SDK>/includes/org/openoffice/" folder. You can then use :
+
Creating header files could be done with exporting the Inspector.uno.rdb with extension manager. This task was easy with old versions (I think before 3.0) because a tree was available in the extension manager and this is no more the case. When done, use the <code>cppumaker</code> tool to create header files. Because the service name is "org.openoffice.InstanceInspector" put the created header files in "<OOo_SDK>/includes/org/openoffice/" folder. You can then use :
 
<source lang="cpp">
 
<source lang="cpp">
 
// C++
 
// C++
Line 26: Line 26:
 
where you see a member data "m_xInspector" has to be added in your component class. The good place for this code is naturally in the constructor of your component class.
 
where you see a member data "m_xInspector" has to be added in your component class. The good place for this code is naturally in the constructor of your component class.
  
==calling the corresponding interface==
+
==Calling the corresponding Interface==
 
If you want to call and see the Java Inspector Dialog, you have to put a code as follows
 
If you want to call and see the Java Inspector Dialog, you have to put a code as follows
 
<source lang="cpp">
 
<source lang="cpp">
 +
// C++
 +
// Listing 3
 
// Java Introspection
 
// Java Introspection
// any <<= xChildMutTreeNode;
+
any <<= somethingToInspect;
// m_xInspector->inspect(any,OUString::createFromAscii("Inspector"));         
+
// name of the tab : inspector
xParentNode->appendChild(xChildMutTreeNode); */
+
m_xInspector->inspect(any,OUString::createFromAscii("Inspector"));         
 +
xParentNode->appendChild(xChildMutTreeNode);  
 
</source>
 
</source>
 +
where you have to adapt the "somethingToInspect".
  
=My own Inspector=
+
=Our own Inspector=
  
I have already presented in this document [[Constructing_Helpers#Reflection_Helper|my own inspector]] but it wasn't a component. Then it was only valuable when writing binary executable, not component. In this section I will show how to transform it as a component. Because [[Object Inspector|Java Inspector]] is more advanced my component is only interesting when using old an inefficient computers. But for me it represents the only experience with writing a great component.  
+
I have already presented in this document [[Constructing_Helpers#Reflection_Helper|our own inspector]] but it wasn't a component. Then it was only valuable when writing binary executable, not for components. In this section I will show how to transform it as a component. Because [[Object Inspector|Java Inspector]] is more advanced than my component it is only interesting when using old and inefficient computers where Java runs slowly. But for me it represents the only experience with writing a big component.  
{{Documentation/Caution|The code presented here is not safe. You can see many "FIXME" in the code. At the moment the version is labelled 0.4}}
+
{{Warn|The code presented here is not safe. You can see many "FIXME" in the code. At the moment the version is labelled 0.4}}
  
 
This component is composed with two separate class (at the moment)
 
This component is composed with two separate class (at the moment)
  
 
==The ReflectionHelper class==
 
==The ReflectionHelper class==
This class is composed with two source files. Be begin with the header :
+
This class is composed with two source files. See the [[Developpement/Cpp/Component/RelectionHelper|corresponding code]]. This class is under construction particularly the
 
<source lang="cpp">
 
<source lang="cpp">
#ifndef REFLECTIONHELPER_H
+
// C++
#define REFLECTIONHELPER_H
+
// Listing
#include <rtl/ustring.hxx>
+
void getTypeAndValue(Any toInspect);
#include <com/sun/star/uno/Sequence.hxx>
+
</source>
#include <com/sun/star/uno/Any.hxx>
+
method. Its code is running but this method has to be the main entry of the class. And it is also called every time you invoke (see below) and this is not a good thing because it is perturbing the member data of the class. In other word there is a problem of conception here !
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
#include <com/sun/star/beans/XIntrospection.hpp>
+
#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+
#include <com/sun/star/lang/XTypeProvider.hpp>
+
#include <com/sun/star/beans/MethodConcept.hpp>
+
#include <com/sun/star/reflection/XIdlMethod.hpp>
+
#include <com/sun/star/uno/Type.hxx>
+
#include <com/sun/star/beans/Property.hpp>
+
#include <com/sun/star/lang/XServiceInfo.hpp>
+
#include <com/sun/star/reflection/ParamMode.hpp>
+
  
using rtl::OUString;
+
==The Component==
using namespace com::sun::star::reflection;
+
using namespace com::sun::star::beans;
+
using namespace com::sun::star::lang;
+
using namespace com::sun::star::uno;
+
  
class ReflectionHelper {
+
See the [[Developpement/Cpp/Component/RelectionHelper|corresponding code]].
public:
+
ReflectionHelper(Any toInspect,Reference< XMultiComponentFactory > const & oSVM, Reference< XComponentContext > const & xContext);
+
  
Sequence < OUString > getMethods(),
+
==The OOoBasic Code and Result==
getTypes(),
+
Calling this component is done with the code as follows :
getServices(),
+
<source lang="oobas">
getPropertiesWithoutValues(),
+
'Listing 6
getPropertiesWithValues();
+
REM  *****  BASIC  *****
Sequence< Reference< XIdlMethod > > getIdlMethods();
+
Reference< XIdlMethod > getXIdlMethodByName(OUString aName);
+
Sub demonstrateSimpleComponent
void getTypeAndValue(Any toInspect);
+
    Dim oSimpleComponent
sal_Bool m_simpleObject;
+
oSimpleComponent = CreateUnoService( "foo.Counter" )
OUString m_OUStrAnyTypeAndValue; // for simple object data
+
oSimpleComponent.myInspect(oSimpleComponent)
// Print out
+
End Sub
// void printOut(); //perhaps const ?
+
+
private:
+
Any m_toInspect;
+
Reference< XMultiComponentFactory > m_xServiceManager;
+
Reference< XIntrospection >m_xIntrospection;
+
Reference< XIntrospectionAccess > m_xIntrospec;
+
Reference< XTypeProvider > m_xTypeProvider;
+
Reference< XComponentContext > m_xContext;
+
OUString getValueName(Any object);
+
OUString getParamMode(ParamMode paramMode);
+
// methods
+
Sequence< Reference< XIdlMethod > > mMethods;
+
// Interfaces
+
Sequence< Type > types;
+
// Services
+
Reference< XServiceInfo > m_xServiceInfo;
+
// Sequence< OUString > services;
+
// Properties
+
Sequence< Property > Properties;
+
// protected methods
+
protected:
+
Sequence< OUString > convert2SeqOUStrInDEBUGMODE(Sequence < sal_Int8 > seqBytes);
+
// same as "convert2SeqOUStrInDEBUGMODE" but in one OUString
+
OUString convert2OUStrInDEBUGMODE(Sequence < sal_Int8 > seqBytes);
+
};
+
#endif //REFLECTIONHELPER_H
+
 
</source>
 
</source>
And now the corresponding implementation :
+
which yields :
<source lang="cpp">
+
// C++
+
// Serge Moutou
+
// with help of <OpenOffice1.1_SDK>/examples/java/Inspector
+
// and Bernard Marcelly XRay tool
+
// version 0.1 (22 Dec 2004)
+
// version 0.2 (4 Jun 2005) added printout in a dialog
+
// version 0.2.1 (4 Apr 2009) added name of parameters in methods
+
// version 0.3 (23 Apr 2009) added DEBUG MODE for []bytes properties
+
// version 0.4 (28 Jul 2009) changed to be used with a component:
+
// ****** constructor uses XMultiComponentFactoty instead of XMultiServiceFactory
+
// To do : Exception Handling, to go further with properties values
+
//#include <org/openoffice/cpp/XInstanceIspector.hpp>
+
#include "./ReflectionHelper.hpp"
+
#include <com/sun/star/reflection/XIdlClass.hpp>
+
#include <com/sun/star/beans/PropertyConcept.hpp>
+
#include <com/sun/star/beans/XPropertySet.hpp>
+
//#include <com/sun/star/reflection/ParamMode.hpp> done in ReflectionHelper.hpp
+
#include <com/sun/star/container/XNameContainer.hpp>
+
//#include <cppuhelper/implbase1.hxx>
+
#include <com/sun/star/awt/XTextComponent.hpp>
+
//#include <com/sun/star/awt/XActionListener.hpp>
+
#include <com/sun/star/awt/XToolkit.hpp>
+
#include <com/sun/star/awt/XControlContainer.hpp>
+
//#include <com/sun/star/awt/PushButtonType.hpp>
+
//#include <com/sun/star/awt/XButton.hpp>
+
//#include <com/sun/star/awt/XWindow.hpp>
+
//#include <com/sun/star/awt/XDialog.hpp>
+
  
//#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
[[Image:CppInspector.png|center|thumb|700px|Our Inspector dialog created in OOoBasic GUI]]
#include <rtl/ustring.hxx>
+
#include <rtl/string.hxx>
+
//#define DEBUG
+
  
using namespace com::sun::star::awt;
+
When invoking the selected "getSupportedMethodNames()" you see the result in the text control : "LosingFocus" and "Invoke". That means the "Invoke" button is partially working.
//using namespace com::sun::star::frame;
+
using namespace com::sun::star::lang;
+
using namespace com::sun::star::container;
+
  
 +
==General Remarks==
 +
Please read carefully the code and search the "FIXME" to see what the limitations are. If you never press the "invoke" button, all is OK. If you want to see what happens when invoking please read below.
 +
{{Warn|The "Invoke" button is not safe. Because starting with a Tree selection, I have to parse this selection to retrieve the name of the method. The parsing code provided only works when no argument is present in the method. Don't invoke <code>myInspect</code> for instance because it has an any variable as argument : it will start an exception in  OpenOffice.org}}
 +
{{Note|The name of the service is "foo.Counter". It would be better to change it.}}
 +
{{Note|You cannot walk through the tree at the moment. The invoke button is only present for tests.}}
  
// Inspector constructor
+
=Managing exceptions=
ReflectionHelper::ReflectionHelper(Any toInspect, Reference< XMultiComponentFactory > const & oSVM,
+
For the first time I want to deal with exception. This problem is not simple when writing component. The question is : what we have to do in the "catch" part of the exception ? When writing binary executable you can print on the terminal screen all what you want. But in a component ? If possible, when writing a component you have to avoid exceptions. But in fact every time you use an OpenOffice Interface you can rise an exception. For instance in my inspector the <code>getXIdlMethodByName(OUString aMethodName)</code> can rise an exception if the aMethodName string is not a correct method name. You have two possibilities to work around :
Reference< XComponentContext > const & xContext)
+
* to write your own search of a method name and when not found to make something which is not an exception,
: m_toInspect(toInspect), m_xServiceManager(oSVM), m_xContext(xContext) {
+
* to carefuly design your dialog (and the corresponding code) to be almost sure the method name is coming from a "method selection" and not from a user choice.
m_xIntrospection = Reference< XIntrospection >( m_xServiceManager->createInstanceWithContext(
+
Because writing new code is never safe the second way is the best in my opinion. But I think it's not always possible to design a dialog to always avoid exception.
                                OUString::createFromAscii("com.sun.star.beans.Introspection" ),m_xContext), UNO_QUERY_THROW);
+
getTypeAndValue(m_toInspect);
+
+
}
+
//FIXME:Bad idea to take a method of ReflectionHelper for that ?
+
//**** modify data member and then doesn't keep coherence in data ?
+
void ReflectionHelper::getTypeAndValue(Any toInspect){
+
// what kind of object object ?
+
OUString OUStrAnyTypeAndValue, OUStrTypeValue;
+
switch (toInspect.pType->eTypeClass)
+
{
+
// const defined in <OOo_SDK>/include/typelib/typeclass.h
+
case typelib_TypeClass_VOID:
+
OUStrAnyTypeAndValue = OUString::createFromAscii("(")+ OUStrAnyTypeAndValue+
+
OUString::createFromAscii(")");
+
m_simpleObject = sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
break;
+
case typelib_TypeClass_BOOLEAN:
+
sal_Bool MyBool;
+
toInspect >>= MyBool;
+
OUStrAnyTypeAndValue = OUString::createFromAscii("(")+ OUStrAnyTypeAndValue+
+
OUString::createFromAscii(")=") + OUStrTypeValue.valueOf((sal_Bool) MyBool);
+
m_simpleObject=sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
break;
+
// all in a 32 bit data at the moment
+
case typelib_TypeClass_BYTE:
+
case typelib_TypeClass_CHAR:
+
case typelib_TypeClass_SHORT:
+
case typelib_TypeClass_UNSIGNED_SHORT:
+
case typelib_TypeClass_UNSIGNED_LONG:
+
case typelib_TypeClass_LONG:
+
sal_Int32 *MyLong;
+
MyLong = (sal_Int32*) toInspect.getValue();
+
OUStrAnyTypeAndValue = OUString::createFromAscii("(")+ OUStrAnyTypeAndValue+
+
OUString::createFromAscii(")=") + OUStrTypeValue.valueOf((sal_Int32) *MyLong);
+
m_simpleObject = sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
break;
+
// all in the double data at the moment
+
case typelib_TypeClass_FLOAT:
+
case typelib_TypeClass_DOUBLE:
+
        double *myDouble;
+
myDouble = (double*) toInspect.getValue();
+
OUStrAnyTypeAndValue = OUString::createFromAscii("(")+ OUStrAnyTypeAndValue+
+
OUString::createFromAscii(")=") + OUStrTypeValue.valueOf((double) *myDouble);
+
m_simpleObject = sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
        break;
+
/* case typelib_TypeClass_HYPER:
+
        break;
+
case typelib_TypeClass_ANY:
+
break; */
+
case typelib_TypeClass_STRING:
+
toInspect >>= OUStrTypeValue;
+
OUStrAnyTypeAndValue = OUString::createFromAscii("(")+ OUStrAnyTypeAndValue+
+
OUString::createFromAscii(")=")+OUStrTypeValue;
+
m_simpleObject = sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
break;
+
case typelib_TypeClass_SEQUENCE:
+
  if (toInspect.getValueTypeName() == OUString::createFromAscii("[]byte")) {
+
Sequence< sal_Int8 > SeqByte;
+
toInspect >>= SeqByte;
+
OUString OUStr=OUString::createFromAscii("([]byte)");
+
/* OUStr = OUString::createFromAscii("Length:");
+
OUStr=OUStr.concat(OUStr.valueOf((sal_Int32) SeqByte.getLength())+
+
OUString::createFromAscii(" : "));*/
+
OUStrAnyTypeAndValue=OUStr + convert2OUStrInDEBUGMODE(SeqByte); //0.3 version
+
m_simpleObject = sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
  } else
+
  if (toInspect.getValueTypeName() == OUString::createFromAscii("[]string")) {
+
Sequence< OUString > SeqOUStr;
+
OUString OUStr;
+
toInspect >>= SeqOUStr;
+
OUStr = OUString::createFromAscii("Length:");
+
OUStr=OUStr.concat(OUStr.valueOf((sal_Int32) SeqOUStr.getLength())+
+
OUString::createFromAscii(" : "));
+
for (sal_Int32 i=0; i<SeqOUStr.getLength(); i++){
+
OUStr=OUStr.concat(OUString::createFromAscii("\"")
+
                  +SeqOUStr[i]
+
  + OUString::createFromAscii("\""));
+
}
+
OUStrAnyTypeAndValue = OUString::createFromAscii("([]string) ") + OUStr;
+
m_simpleObject = sal_True;
+
m_OUStrAnyTypeAndValue = OUStrAnyTypeAndValue;
+
  }
+
  break;
+
case typelib_TypeClass_INTERFACE:
+
m_xIntrospec = m_xIntrospection->inspect(toInspect);
+
mMethods = m_xIntrospec -> getMethods(MethodConcept::ALL);
+
m_xTypeProvider = Reference< XTypeProvider> (toInspect,UNO_QUERY);
+
types = m_xTypeProvider->getTypes();
+
m_xServiceInfo = Reference< XServiceInfo>(toInspect,UNO_QUERY);
+
Properties = m_xIntrospec -> getProperties(PropertyConcept::ALL);
+
m_simpleObject = sal_False;
+
break;
+
/* case typelib_TypeClass_TYPE: break;
+
case typelib_TypeClass_ARRAY: break;
+
case typelib_TypeClass_SERVICE: break;
+
case typelib_TypeClass_INTERFACE_METHOD: break;
+
case typelib_TypeClass_INTERFACE_ATTRIBUTE:break;
+
case typelib_TypeClass_ENUM: break;
+
case typelib_TypeClass_STRUCT:break; */
+
//default:
+
} // switch
+
}
+
  
 +
TO DO
  
//OK
+
=Transforming a component into a binary executable=
Sequence < OUString > ReflectionHelper::getServices() {
+
As already mentioned, writing a component is a harder task than writing a binary executable. Then a good idea to assist the developement of a component is to transform it at first into a binary executable and when you are gratified with your code to transform it back into a component. We will examine in this section this idea.
return m_xServiceInfo->getSupportedServiceNames();
+
==The Service Manager Problem==
}
+
When transforming a component into a binary executable, the first problem is the service manager. The component context allows you to retrieve a <idl>com.sun.star.lang.XMultiComponentFactory</idl> while a binary executable only works with <idl>com.sun.star.lang.XMultiServiceFactory</idl>. I have encounter this problem when developping my C++ inspection tool. I don't use the same Service Manager in the [[Developpement/Cpp/Component/RelectionHelper|The Component Helper]]
 +
and int the [[Development/Cpp/Helper/ReflectionHelper|Binary Executable Helper]] even if the task is the same.
  
//OK
+
TO DO going further...
Sequence < OUString > ReflectionHelper::getMethods(){
+
Sequence< OUString > methods(mMethods.getLength());
+
for (int i=0;i<mMethods.getLength();i++){
+
OUString params;
+
params=OUString::createFromAscii("(");
+
Sequence< ParamInfo > ParamInfos = mMethods[i]->getParameterInfos();
+
if (ParamInfos.getLength() > 0) {
+
for (int j=0;j<ParamInfos.getLength();j++){
+
Reference< XIdlClass > xIdlClass = ParamInfos[j].aType;
+
if (j == 0)
+
// first parameter has no leading comma
+
params += OUString::createFromAscii("[") + getParamMode(ParamInfos[j].aMode)+
+
OUString::createFromAscii("]") +
+
xIdlClass->getName() + OUString::createFromAscii(" ") + ParamInfos[j].aName;
+
else
+
params += OUString::createFromAscii(",[") + getParamMode(ParamInfos[j].aMode)+
+
OUString::createFromAscii("]")+
+
xIdlClass->getName() + OUString::createFromAscii(" ") + ParamInfos[j].aName;
+
}
+
}
+
params += OUString::createFromAscii(")");
+
methods[i] = mMethods[i]->getReturnType()->getName()+OUString::createFromAscii(" ")+
+
mMethods[i]->getName()+params;
+
}
+
return methods;
+
}
+
  
Sequence< Reference< XIdlMethod > > ReflectionHelper::getIdlMethods(){
+
==The Dialog Problem==
return mMethods;
+
All the components I imagine today are related to a dialog. I have already presented the [[Writing_Professional_Components#My_own_Inspector|C++ Inspector]] but I can also imagine an automatic tool which starts from a GUI dialog and automaticaly generate all the C++ code for the corresponding component.  
}
+
// added for 0.4 version
+
Reference< XIdlMethod > ReflectionHelper::getXIdlMethodByName(OUString aName){
+
Reference< XIdlMethod > xIdlMethod = m_xIntrospec->getMethod(aName,MethodConcept::ALL);
+
return xIdlMethod;
+
}
+
// OK
+
Sequence < OUString > ReflectionHelper::getTypes(){
+
Sequence< OUString > interfaces(types.getLength());
+
for (int i=0;i<types.getLength();i++){
+
interfaces[i] = types[i].getTypeName();
+
}
+
return interfaces;
+
}
+
  
// to improve : change all the tests with getCppuType : probably quicker than a string test
+
That doesn't mean all components have to manage a dialog but we are interested in this section by this situation. The problem of this situation is that I don't know if it is possible to manage dialog in binary executable like in component. [[Documentation/DevGuide/WritingUNO/Accessing_Dialogs|Developer's Guide]] seems to state only components methods can be bound to dialog event but the fact binary executable have to do [[UNO_registery_and_Bootstrapping|with registery]] rises always the question in my head. This is one of the greatest problem I have to solve in the futur, but spending time to solve a problem you don't know if it has a solution is not for today.
OUString ReflectionHelper::getValueName(Any object){
+
OUString OUStr;
+
OUStr = OUString::createFromAscii("!! Unable to compute values !!");
+
if (object.hasValue()) {
+
if (object.isExtractableTo(getCppuBooleanType())){
+
sal_Bool MyBool;
+
object >>= MyBool;
+
return OUStr.valueOf((sal_Bool) MyBool);
+
} else
+
if (object.getValueTypeName() == OUString::createFromAscii("string")) {
+
OUString *MyOUStr;
+
MyOUStr = (OUString *) object.getValue();
+
OUStr = OUString::createFromAscii("\"");
+
return OUStr + *MyOUStr + OUString::createFromAscii("\"");
+
} else
+
if (object.getValueTypeName() == OUString::createFromAscii("long")) {
+
sal_Int32 *MyLong;
+
MyLong = (sal_Int32*) object.getValue();
+
return OUStr.valueOf((sal_Int32) *MyLong);
+
} else
+
if (object.getValueTypeName() == OUString::createFromAscii("short")) {
+
sal_Int16 *MyShort;
+
MyShort = (sal_Int16*) object.getValue();
+
return OUStr.valueOf((sal_Int32) *MyShort);
+
} else
+
if (object.getValueTypeName() == OUString::createFromAscii("[]byte")) {
+
Sequence< sal_Int8 > SeqByte;
+
object >>= SeqByte;
+
OUString OUStr=convert2OUStrInDEBUGMODE(SeqByte); //0.3 version
+
return OUStr;
+
} else
+
if (object.getValueTypeName() == OUString::createFromAscii("[]string")) {
+
Sequence< OUString > SeqOUStr;
+
object >>= SeqOUStr;
+
OUStr = OUString::createFromAscii("Length:");
+
OUStr=OUStr.concat(OUStr.valueOf((sal_Int32) SeqOUStr.getLength())+
+
OUString::createFromAscii(" : "));
+
for (sal_Int32 i=0; i<SeqOUStr.getLength(); i++){
+
OUStr=OUStr.concat(OUString::createFromAscii("\"")
+
                  +SeqOUStr[i]
+
  + OUString::createFromAscii("\""));
+
}
+
return OUStr;
+
} else return OUStr;
+
} else return OUStr;
+
}
+
  
// OK
+
TO DO going further...
// Get properties with values : only those computed in getValueName
+
// améliorer avec :
+
// voir SDK2.3.1 portable IUT <SDK>/examples/cpp/extensions/source/objectbrowser/uno_lang.cxx
+
Sequence < OUString > ReflectionHelper::getPropertiesWithValues(){
+
Sequence< OUString > propWithVal(Properties.getLength());
+
for (int i=0;i<Properties.getLength();i++){
+
Type typ =  getCppuType( (const Reference< XPropertySet > *)0);
+
Reference< XPropertySet > rPropertySet(m_xIntrospec->queryAdapter(typ),UNO_QUERY);
+
Reference< XPropertySetInfo > rPropertySetInfo=rPropertySet->getPropertySetInfo();
+
Any object;
+
if (rPropertySetInfo->hasPropertyByName(Properties[i].Name)){
+
object <<= rPropertySet->getPropertyValue(Properties[i].Name);
+
//if (object.hasValue()) printf("Valeur trouvee : \n");
+
propWithVal[i] = Properties[i].Name + OUString::createFromAscii(" = (")+
+
Properties[i].Type.getTypeName() + OUString::createFromAscii(") ")
+
+ getValueName(object);
+
//+ object.getValueTypeName();
+
}
+
}
+
return propWithVal;
+
}
+
  
// OK
+
==Reflections on the transformation==
// Get properties without values but types
+
The transformation is not always as easy as we can imagine. For instance I have worked in this document many times around the counter : how can I transform the counter into a binary executable ? If the answer is not so simple, is it only because the counter is not an OpenOffice component ? (I mean the counter code doesn't need OpenOffice to run, it only uses the dialog of OpenOffice, then XWindows, Windows or MacOSX could be used instead). When can we really start to call a component an OpenOffice component ? At first a good answer seems to be : when the component uses OpenOffice.org services and interfaces. OK, but if you have a closer look at the counter C++ code, the fact that it has to use a GUI dialog makes it very dependent of OpenOffice.org services and interfaces : how many UNO_QUERY in the code ? Sixteen in the counter with [[Going_further_with_Dialog_and_Component#Multi-Page_Dialog|Multi-Page Dialog]] only in the <code>callHandlerMethod()</code> function. Does that mean the services and interfaces related to Dialog doesn't count ? Reasonably not. Even such a component as the counter has to be labelled OpenOffice.org component : in toher words we have to find a solution to transform it into a binary executable.
Sequence < OUString > ReflectionHelper::getPropertiesWithoutValues(){
+
Sequence< OUString > propWithVal(Properties.getLength());
+
for (int i=0;i<Properties.getLength();i++){
+
Type typ =  getCppuType( (const Reference< XPropertySet > *)0);
+
Reference< XPropertySet > xPropertySet(m_xIntrospec->queryAdapter(typ),UNO_QUERY);
+
Reference< XPropertySetInfo > xPropertySetInfo=xPropertySet->getPropertySetInfo();
+
if (xPropertySetInfo->hasPropertyByName(Properties[i].Name)){
+
propWithVal[i] = Properties[i].Name + OUString::createFromAscii(" = (")+
+
Properties[i].Type.getTypeName() + OUString::createFromAscii(")");
+
}
+
}
+
return propWithVal;
+
}
+
  
// Don't forget to add : #include <com/sun/star/reflection/ParamMode.hpp>
+
TO DO : going further
// Don't forget to add "com.sun.star.reflection.ParamMode \" in the makefile
+
OUString ReflectionHelper::getParamMode(ParamMode paramMode) {
+
  OUString toReturn;
+
  toReturn = OUString::createFromAscii("");
+
  if (paramMode == ParamMode_IN) toReturn = OUString::createFromAscii("IN"); else
+
    if (paramMode == ParamMode_OUT) toReturn = OUString::createFromAscii("OUT"); else
+
      if (paramMode == ParamMode_INOUT) toReturn = OUString::createFromAscii("INOUT");
+
  return toReturn;
+
}
+
// added for 0.3 version
+
Sequence< OUString > ReflectionHelper::convert2SeqOUStrInDEBUGMODE(Sequence < sal_Int8 > seqBytes){
+
  sal_Int8 Hexa[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
  sal_Int32 lin,col;
+
  sal_Char line[80];
+
  sal_Int32 len,sequenceSize,index;
+
  len=seqBytes.getLength();
+
  sequenceSize=(len>>4)+2;
+
  if ((len&0XFFF0)==len) sequenceSize--; //if len%16==0
+
  Sequence< OUString > SeqOUStr(sequenceSize);
+
  // First line with length
+
  SeqOUStr[0] = OUString::createFromAscii("Length:")+
+
                OUString::valueOf((sal_Int32) seqBytes.getLength())+
+
OUString::createFromAscii(" ");
+
  len = len&0XFFF0; // remove the modulo 16
+
  for(lin=0;lin<len;lin=lin+16){
+
      for(col=0;col<16;col++){
+
        line[3*col]=Hexa[((unsigned char)seqBytes[lin+col])>>4];
+
line[3*col+1]=Hexa[seqBytes[lin+col]&0x0F];
+
line[3*col+2]=' ';
+
        if ((seqBytes[lin+col]<128)&&(seqBytes[lin+col]>20)) line[50+col]=seqBytes[lin+col];
+
        else line[50+col]='.';
+
      } /* end of for */
+
      line[66]=0; /* end of cstring...*/
+
      line[48]=' ';line[49]=' ';
+
  // ready to add the OUString in Sequence
+
      if ((lin%16)==0) index = lin/16+1; else index=lin/16+2;
+
      SeqOUStr[index]=OUString::createFromAscii(line);
+
  } /* end of for */
+
  // the last line is more complicated because not complete
+
  // we only keep modulo
+
  len=seqBytes.getLength()&0x000F;
+
  if (len>0) { // only a line here if non-empty
+
  for (lin=0;lin<len;lin++){
+
    col=lin;
+
    line[3*col]=Hexa[((unsigned char)seqBytes[lin])>>4];
+
    line[3*col+1]=Hexa[seqBytes[lin]&0x0F];
+
    line[3*col+2]=' ';
+
    if ((seqBytes[lin]<128)&&(seqBytes[lin]>20)) line[50+col]=seqBytes[lin];
+
    else line[50+col]='.';
+
  }
+
  // we complete the line
+
  for (++col;col<16;col++){
+
line[3*col]=' ';line[3*col+1]=' ';line[3*col+2]=' ';line[50+col]=' ';
+
  }
+
  line[66]=0; /* end of string...*/
+
  line[48]=' ';line[49]=' ';
+
  // ready to add the OUString in Sequence
+
  SeqOUStr[lin/16+2]=OUString::createFromAscii(line);
+
  } //end if
+
  return SeqOUStr;
+
}
+
  
// added for 0.3 version
+
=What I am : a Component or an Add-on ?=
OUString ReflectionHelper::convert2OUStrInDEBUGMODE(Sequence < sal_Int8 > seqBytes){
+
In many situations you are unable to say if your code has to belong to a [[Constructing_Components|component]] or to an [[Add-on|add-on]]. It's only because the distinction between both situation is only a terminology notion. In practical components it's a good idea to call them with a menu. Then your [[Constructing_Components|component]] becomes an [[Add-on|add-on]] with no IDL-file. But you can encounter more complex situations. For instance a scriptable component (with IDL file) but with a configuration dialog called with a menu.
  Sequence <OUString> SeqOUStr = convert2SeqOUStrInDEBUGMODE(seqBytes);
+
  OUString OUStr;
+
  for(sal_Int32 i=0;i<SeqOUStr.getLength();i++)
+
    OUStr = OUStr+SeqOUStr[i]+OUString::createFromAscii("\n");
+
  return OUStr;
+
}
+
</source>
+
==The Component==
+
  
=Managing exceptions=
+
TO DO going further ...
=Transforming a component=
+
 
 +
=Packaging a Dialog and a Dynamic Library in an oxt File=
 +
This is the kind of things very important I am not able to manage at the moment. [[User:SergeMoutou|SergeMoutou]] 14:09, 19 August 2009 (UTC)
 +
 
 +
Hope to remove this sentence as soon as possible.
 +
 
 +
=Home Page=
 +
 
 +
[[Image:HomePageCpp.png]] [[Using_Cpp_with_the_OOo_SDK|Return to Document Home page]]
 +
 
 +
=See also=
 +
* [[Constructing_Components|Constructing Components]]
 +
* [[Component_and_Dialog|Component and Dialog]]
 +
* [[Going_further_with_Dialog_and_Component|Going further with Dialog and Components]]
 +
* [[Add-on|Constructing an Addon]]
 +
* [[CompleteAddIn | OOoCalc addin in C++]]
 +
 
 +
 
 +
[[Category:Cpp]]
 +
[[Category:Uno]]
 +
[[Category:Tutorial]]
 +
[[Category:Extensions]]
 +
[[Category:Samples]]

Latest revision as of 22:15, 13 July 2018

Documentation caution.png This chapter is under construction for a while because I don't master all the topics evocated here at the moment. SergeMoutou 12:20, 3 August 2009 (UTC)

Writing components is not as easy as developping binary executable. When an exception occurs in your component a recovery process of your document begins. This process is too long to try some code and correct the errors. When designing a component, it would be a good idea to conceive it as a binary executable before to change it into a component. In this chapter we will discuss this idea and also the exceptions. But before starting we examine at first the inspection tools. why ? Because there is a lot of things you cannot do without an inspection tool. In fact there is always an other way : to read the documentation but it can take hours and hours to solve your problem with reading documentation and only ten minutes with an inspection tool.

Java Inspector

I have already tackled Java Inspector in this document (see here). But we used in fact the addon part of the component because it was called with the corresponding button, not with code. In this section we will discuss how we can use Java Inspector in a component, to put it differently, how to call Java inspector with C++ code ?

Creating the Header Files

Creating header files could be done with exporting the Inspector.uno.rdb with extension manager. This task was easy with old versions (I think before 3.0) because a tree was available in the extension manager and this is no more the case. When done, use the cppumaker tool to create header files. Because the service name is "org.openoffice.InstanceInspector" put the created header files in "<OOo_SDK>/includes/org/openoffice/" folder. You can then use :

// C++
// Listing 1
// Java Inspector
#include <org/openoffice/XInstanceInspector.hpp>

in your component.

Creating the corresponding Service

This is done with :

// C++
// Listing 2
// If you want to use java Inspector
	m_xInspector = Reference< XInstanceInspector > (m_xMCF->createInstanceWithContext( 
			OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.InstanceInspector" ) ),
			m_xContext),
			UNO_QUERY_THROW );

where you see a member data "m_xInspector" has to be added in your component class. The good place for this code is naturally in the constructor of your component class.

Calling the corresponding Interface

If you want to call and see the Java Inspector Dialog, you have to put a code as follows

// C++
// Listing 3
// Java Introspection
	any <<= somethingToInspect;
// name of the tab : inspector
	m_xInspector->inspect(any,OUString::createFromAscii("Inspector"));        
	xParentNode->appendChild(xChildMutTreeNode);

where you have to adapt the "somethingToInspect".

Our own Inspector

I have already presented in this document our own inspector but it wasn't a component. Then it was only valuable when writing binary executable, not for components. In this section I will show how to transform it as a component. Because Java Inspector is more advanced than my component it is only interesting when using old and inefficient computers where Java runs slowly. But for me it represents the only experience with writing a big component.

Documentation caution.png The code presented here is not safe. You can see many "FIXME" in the code. At the moment the version is labelled 0.4

This component is composed with two separate class (at the moment)

The ReflectionHelper class

This class is composed with two source files. See the corresponding code. This class is under construction particularly the

// C++
// Listing 
void getTypeAndValue(Any toInspect);

method. Its code is running but this method has to be the main entry of the class. And it is also called every time you invoke (see below) and this is not a good thing because it is perturbing the member data of the class. In other word there is a problem of conception here !

The Component

See the corresponding code.

The OOoBasic Code and Result

Calling this component is done with the code as follows :

'Listing 6
REM  *****  BASIC  *****
 
Sub demonstrateSimpleComponent
    Dim oSimpleComponent
	oSimpleComponent = CreateUnoService( "foo.Counter" )
	oSimpleComponent.myInspect(oSimpleComponent)
End Sub

which yields :

Our Inspector dialog created in OOoBasic GUI

When invoking the selected "getSupportedMethodNames()" you see the result in the text control : "LosingFocus" and "Invoke". That means the "Invoke" button is partially working.

General Remarks

Please read carefully the code and search the "FIXME" to see what the limitations are. If you never press the "invoke" button, all is OK. If you want to see what happens when invoking please read below.

Documentation caution.png The "Invoke" button is not safe. Because starting with a Tree selection, I have to parse this selection to retrieve the name of the method. The parsing code provided only works when no argument is present in the method. Don't invoke myInspect for instance because it has an any variable as argument : it will start an exception in OpenOffice.org
Documentation note.png The name of the service is "foo.Counter". It would be better to change it.
Documentation note.png You cannot walk through the tree at the moment. The invoke button is only present for tests.

Managing exceptions

For the first time I want to deal with exception. This problem is not simple when writing component. The question is : what we have to do in the "catch" part of the exception ? When writing binary executable you can print on the terminal screen all what you want. But in a component ? If possible, when writing a component you have to avoid exceptions. But in fact every time you use an OpenOffice Interface you can rise an exception. For instance in my inspector the getXIdlMethodByName(OUString aMethodName) can rise an exception if the aMethodName string is not a correct method name. You have two possibilities to work around :

  • to write your own search of a method name and when not found to make something which is not an exception,
  • to carefuly design your dialog (and the corresponding code) to be almost sure the method name is coming from a "method selection" and not from a user choice.

Because writing new code is never safe the second way is the best in my opinion. But I think it's not always possible to design a dialog to always avoid exception.

TO DO

Transforming a component into a binary executable

As already mentioned, writing a component is a harder task than writing a binary executable. Then a good idea to assist the developement of a component is to transform it at first into a binary executable and when you are gratified with your code to transform it back into a component. We will examine in this section this idea.

The Service Manager Problem

When transforming a component into a binary executable, the first problem is the service manager. The component context allows you to retrieve a com.sun.star.lang.XMultiComponentFactory while a binary executable only works with com.sun.star.lang.XMultiServiceFactory. I have encounter this problem when developping my C++ inspection tool. I don't use the same Service Manager in the The Component Helper and int the Binary Executable Helper even if the task is the same.

TO DO going further...

The Dialog Problem

All the components I imagine today are related to a dialog. I have already presented the C++ Inspector but I can also imagine an automatic tool which starts from a GUI dialog and automaticaly generate all the C++ code for the corresponding component.

That doesn't mean all components have to manage a dialog but we are interested in this section by this situation. The problem of this situation is that I don't know if it is possible to manage dialog in binary executable like in component. Developer's Guide seems to state only components methods can be bound to dialog event but the fact binary executable have to do with registery rises always the question in my head. This is one of the greatest problem I have to solve in the futur, but spending time to solve a problem you don't know if it has a solution is not for today.

TO DO going further...

Reflections on the transformation

The transformation is not always as easy as we can imagine. For instance I have worked in this document many times around the counter : how can I transform the counter into a binary executable ? If the answer is not so simple, is it only because the counter is not an OpenOffice component ? (I mean the counter code doesn't need OpenOffice to run, it only uses the dialog of OpenOffice, then XWindows, Windows or MacOSX could be used instead). When can we really start to call a component an OpenOffice component ? At first a good answer seems to be : when the component uses OpenOffice.org services and interfaces. OK, but if you have a closer look at the counter C++ code, the fact that it has to use a GUI dialog makes it very dependent of OpenOffice.org services and interfaces : how many UNO_QUERY in the code ? Sixteen in the counter with Multi-Page Dialog only in the callHandlerMethod() function. Does that mean the services and interfaces related to Dialog doesn't count ? Reasonably not. Even such a component as the counter has to be labelled OpenOffice.org component : in toher words we have to find a solution to transform it into a binary executable.

TO DO : going further

What I am : a Component or an Add-on ?

In many situations you are unable to say if your code has to belong to a component or to an add-on. It's only because the distinction between both situation is only a terminology notion. In practical components it's a good idea to call them with a menu. Then your component becomes an add-on with no IDL-file. But you can encounter more complex situations. For instance a scriptable component (with IDL file) but with a configuration dialog called with a menu.

TO DO going further ...

Packaging a Dialog and a Dynamic Library in an oxt File

This is the kind of things very important I am not able to manage at the moment. SergeMoutou 14:09, 19 August 2009 (UTC)

Hope to remove this sentence as soon as possible.

Home Page

HomePageCpp.png Return to Document Home page

See also

Personal tools