Writing Professional Components

From Apache OpenOffice Wiki
Revision as of 13:03, 3 August 2009 by SergeMoutou (Talk | contribs)

Jump to: navigation, search
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.

Java Inspector

I have already tackled Java Inspector in this document (see here). But in this section we will discuss how we can use Java Inspector in a component.

Creating the Header Files

Creating header files could be done with exporting the rdb with extension manager and then using cppumaker 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 :

// 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

// Java Introspection
//	any <<= xChildMutTreeNode;
//	m_xInspector->inspect(any,OUString::createFromAscii("Inspector"));        
	xParentNode->appendChild(xChildMutTreeNode); */

My own Inspector

I have already presented in this document 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 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.

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. Be begin with the header :

#ifndef REFLECTIONHELPER_H
#define REFLECTIONHELPER_H
#include <rtl/ustring.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/Any.hxx>
#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;
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 {
public:
	ReflectionHelper(Any toInspect,Reference< XMultiComponentFactory > const & oSVM, Reference< XComponentContext > const & xContext);
 
	Sequence < OUString > 	getMethods(),
				getTypes(),
				getServices(),
				getPropertiesWithoutValues(),
				getPropertiesWithValues();
	Sequence< Reference< XIdlMethod > > getIdlMethods();
	Reference< XIdlMethod > getXIdlMethodByName(OUString aName);
	void getTypeAndValue(Any toInspect);
	sal_Bool m_simpleObject;
	OUString m_OUStrAnyTypeAndValue; // for simple object data
// Print out
//	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

And now the corresponding implementation :

// 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>
#include <rtl/ustring.hxx>
#include <rtl/string.hxx>
//#define DEBUG
 
using namespace com::sun::star::awt;
//using namespace com::sun::star::frame;
using namespace com::sun::star::lang;
using namespace com::sun::star::container;
 
 
// Inspector constructor
ReflectionHelper::ReflectionHelper(Any toInspect, Reference< XMultiComponentFactory > const & oSVM, 
					Reference< XComponentContext > const & xContext)
	: m_toInspect(toInspect), m_xServiceManager(oSVM), m_xContext(xContext) {
	m_xIntrospection = Reference< XIntrospection >( m_xServiceManager->createInstanceWithContext(
                                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
}
 
 
//OK
Sequence < OUString > ReflectionHelper::getServices() {
	return m_xServiceInfo->getSupportedServiceNames();
}
 
//OK
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(){
	return mMethods;
}
// 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
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
// 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
// Get properties without values but types
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>
// 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
OUString ReflectionHelper::convert2OUStrInDEBUGMODE(Sequence < sal_Int8 > seqBytes){
  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;
}

The Component

Managing exceptions

Transforming a component

Personal tools