The method expression.SupportsService
is commonly used in connection with service names such as com.sun.star.sheet.SpreadsheetDocument
or com.sun.star.sdb.OfficeDatabaseDocument
to determine whether a document object represented by expression
is a specific type of LibreOffice document.
.
I understand that expression.SupportsService
can also be used with other object types, such as CurrentSelection
to determine the type of object currently selected, but that is beyond the scope of this question. Here, I am only interested in use cases where expression
represents a document object of some type.
.
My question is whether there are use cases for the SupportsService
method when it is used in connection with a document object that go beyond determining the type of document? For example, it would also be possible to test a document object to see if it supports the Service com.sun.star.document.OfficeDocument
, but I’m not sure how this would be useful.
Install the extension (oxt) from Release Version 1.3.4 · hanya/MRI · GitHub and restart LO.
Open a spreadsheet.
Call menu:Tools>Add-Ons>Mri
You get a list of all properties of the current document with a little search box near the bottom.
Scroll down to “Sheets” and double-click that line. You get a list of all sheet properties.
Switch to “Methods” and double-click “getByIndex”, enter an index number and you get everything about that sheet.
Now hit Ctrl+H and you see the resulting code of a built-in macro recorder that records your actions in MRI.
The other categories show interfaces and services of the current object.
Interfaces are bundles of methods. You find all methods in the list of methods.
There are supported services and available services. The former describe what the object is, the latter services can be derived from the current object like this: myObj = doc.createInstance("com.sun.star.style.CellStyle")
“Pseudo-Properties” are syntactic sugar for most of the get/set methods:
Method call v = oCell.getValue()
is equivalent to pseudo-property v = oCell.Value
.
Method call oCell.setValue(v)
is equivalent to pseudo-property oCell.Value = v
.
After setting up the MRI config, you have a direct link from most of the items to the UNO reference.
I’ve spent the last few days reading about LibreOffice Basic and the UNO API. Like I mentioned in one of my comments (which weirdly seems to have disappeared from this thread now) the VBA and LO Basic syntax and language runtime features look almost identical to me.
.
I’m seeing more clearly now that the UNO API is where I’m having problems. I’m used to object models where the top-level object is the Application, and all objects, properties and methods are organized in a hierarchy under that top-level object. By typing Application.
into the IDE, I can use code completion (intellisense) to work my way down through the hierarchy to any child object, property or method that exists. This, by the way, has nothing to do with VBA. It works exactly the same in Visual Studio and VSTO.
.
Up till now I’ve assumed it would be similar in LO and that I had simply not fully understood or discovered what that top-level object is and how to work with all of the wonderful child objects, properties and methods I imagined would be underneath it. I don’t know if anybody remembers, but about a year ago, I started this other thread about programming IDEs and code completion. I was frustrated because I thought the LO Basic IDE was simply refusing to show me the hierarchical structure I assumed was there.
.
I’ve only been able to scratch the surface in the last couple of days, but from what I can see, I was completely wrong about this. It appears that instead of having a hierarchy as described above, LO has individual “services”, which appear to be something like mini-applications that provide narrow sets of functions. I have no idea whether code completion for UNO services might work in other languages and IDEs, but it seems pretty clear now that it cannot work in LO Basic. If for no other reason, then at the very least because UNO services are instantiated using the generic object variable type.
.
I want to apologize to everyone for asking what are undoubtedly annoying questions sometimes. I also want to thank everyone who patiently commented and provided input because at the end of the day, all this has helped me see where the actual problem is and what I have to learn in order to overcome it.
Top level object is StarDesktop, and everything is one big application for all types of documents.
This IDE is more or less a clone of the very first VBA IDE in Office 95. With Office 97 MS introduced a full blown IDE with class modules and completion.
With SupportsService
you can consult any service, it will return you real if you support it and false if not.
Thank you, but I’m afraid this does not answer my question. I understand that you can query any object for any service name, because the service names are just strings. Again, my question is this:
My question is whether there are use cases for the
SupportsService
method when it is used in connection with a document object that go beyond determining the type of document? For example, it would also be possible to test a document object to see if it supports the Servicecom.sun.star.document.OfficeDocument
, but I’m not sure how this would be useful.
The answer is yes. But, is better:
By the way, could you please tell me which tool you are using in this picture? This does not look like MRI.
Tools->Development tools
Thanks!
Thank you very much for this. I initially didn’t see the link you provided.
.
If I understand correctly, you mean that it would be better to use the function identify()
instead of supportsService
. I would love to try this out in a Basic macro, but I am having difficulty understanding from this documentation how to translate this into a function call in Basic.
.
I know this will probably get me into trouble again, but this is a general problem for me with the LibreOffice documentation. Is there any kind of tutorial or explanation where I could learn how to translate this type of LibreOffice API documentation into concrete Basic function calls?
The identify
method might not give you what you expect. It checks XModule
interface first; and the identifier there may be overridden by user.
There is no “translation” of the API documentation into “Basic function calls” in this sense. The API documentation tells you what methods specific interfaces services in UNO have; which interfaces and which properties are included in which services; which singletons, enumerations and constants there are… But this documentation itself does not tell you, which objects implement which interfaces or support which services. So you have no direct connection from the API documentation’s things to the objects that you have in Basic (or any other language) - e.g., using the language-specific entry points like ThisComponent
(in Basic).
In this specific case of suggested identify
method of XModuleManager
interface, your question should be much more specific, not “how to translate this into a function call”, but rather “how do I get an XModuleManager
variable (or more correctly, an object implementing XModuleManager
interface)”. This kind of question would show that you understand the idea what you need to do. So looking at the interface documentation, we see its inheritance diagram:
and we can see there, that there is a service called ModuleManager
, that includes the interface. Unfortunately, none of the objects in the inheritance chain include a link to a guide in their documentation (sometimes we do have such links - see e.g. Detailed Description
on XModel). But in this case, we need to know, that we can often create instances of generic services in Basic, using its CreateUnoService
function. Note that not all services allow such creation; but nothing prevents you from trying.
So in this case, your code would be something like
manager = CreateUnoService("com.sun.star.frame.ModuleManager")
MsgBox manager.identify(ThisComponent)
We have a dedicated Macros wiki page, where we collect different resources on the topic, including Basic Programmer’s Guide, LibreOffice Basic Guide, Developer’s Guide, the wonderful Andrew Pitonyak’s book, and so on. These resources are likely what you need to learn to get an idea how to manage objects - that in the end connect to the API elements.
A side note: see how the identify
method accepts any of the three kinds of objects: it can be a “model”, a “frame”, and a “controller”. And the identify
method would find the module
corresponding to any of them, and return the module’s identifier (usually a service name, but not always). Note that all documents implement the XModel
interface; so in this case, the implementation of the identify
would need to know, if the passed object is XModel
, to know how to obtain its identifier (because if that’s not an XModel
, but e.g. an XFrame
, the method will be different) - but it doesn’t need to know, if this XModel
is e.g. a spreadsheet or a presentation, so in this case, it makes sense for that method to query for an ancestor interface, not for the most derived. This is similar (even if not identical) to your original question.
- It is not
SupportsService
, butsupportsService
. Yes, Basic language is case-insensitive, but this method is not part of Basic, but UNO API; and is used with any language binding, including case-sensitive languages like Python and Java. Thus, it’s best to use proper case when talking about API. -
OfficeDocument
is a parent service for e.g.OfficeDatabaseDocument
that you mentioned. Any service is an API contract (guarantee), that lets you know e.g. which methods this objects has. So if you are only interested in the methods / properties supported by the more generalOfficeDocument
(as opposed to methods / properties specific to more derived document types services), e.g. when you create functions that may take arbitrary objects (documents, selections, paragraphs, buttons, …, and do things depending on their “class” - it’s not that often, but it happens) - then you would likely use that parent service name.
One area where such a function would be useful is when you deal with Frame-Controller-Model paradigm. In many cases, you might need to arrive to, e.g., frame, given either a frame, or a model (which is often the document object, which we discuss here), or a controller as the original object. Then you would use “parent” service names, not most derived ones. (Or you might decide to use Basic’s HasUnoInterfaces
.)
.
This is a very interesting point. I completely agree with you about this, but I am wondering about something. There are situations when an error in the case of UNO service name will cause problems. For example, if the active document is a Calc spreadsheet, the following statements will all return True
:
.
ThisComponent.supportsService("com.sun.star.sheet.SpreadsheetDocument")
ThisComponent.SupportsService("com.sun.star.sheet.SpreadsheetDocument")
ThisComponent.SUPPORTSSERVICE("com.sun.star.sheet.SpreadsheetDocument")
.
The following statement, however, will return False
:
.
ThisComponent.supportsService("COM.SUN.STAR.SHEET.SpreadsheetDocument")
.
So the the supportsService
method is not part of Basic… except that it actually does kind of seem to be part of Basic when it is used as a method of a Basic object variable. Does this mean that the supportsService method as it is implemented in Basic is something like a case-insensitive wrapper for the actual API call, which is case-sensitive?
.
So, maybe I should explain why I’m even asking this question. I’m putting together a collection of information on the LibreOffice API as it is used in Basic, because as a VBA programmer my primary interest is Basic. I don’t program Python, so I’m not concerned with how things work in that language.
.
At some point, I understood that it is important when working with LibreOffice documents in Basic to first test the document type to ensure that it has the properties and methods that you intend to use, because otherwise you get runtime errors, and that you use the supportsService
method together with the service names that correspond to specific document types to determine this.
.
This got me thinking. What other services might different document types support, and what might be the utility of testing the document object for these? It’s important to note the distinction here. Not of what utility is the service itself along with its properties and methods, but what is the utility of testing for it.
.
So I decided to see if I could find out what other services are supported by different document types (and only by document types at this point, not for example by CurrentSelection
), so that I could get a feel for whether it would be meaningful to test for them.
.
One source of information for this was the MRI tool. Using MRI, I was able to see that different document objects returned by ThisComponent
have a string array property called SupportedServiceNames
with the following values for the different document types:
.
Calc
.
(000) = com.sun.star.sheet.SpreadsheetDocument
(001) = com.sun.star.sheet.SpreadsheetDocumentSettings
(002) = com.sun.star.document.OfficeDocument
.
Writer
.
(000) = com.sun.star.document.OfficeDocument
(001) = com.sun.star.text.GenericTextDocument
(002) = com.sun.star.text.TextDocument
.
Impress
.
(000) = com.sun.star.document.OfficeDocument
(001) = com.sun.star.drawing.GenericDrawingDocument
(002) = com.sun.star.drawing.DrawingDocumentFactory
(003) = com.sun.star.presentation.PresentationDocument
.
Draw
.
(000) = com.sun.star.document.OfficeDocument
(001) = com.sun.star.drawing.GenericDrawingDocument
(002) = com.sun.star.drawing.DrawingDocumentFactory
(003) = com.sun.star.drawing.DrawingDocument
.
Math
.
(000) = com.sun.star.document.OfficeDocument
(001) = com.sun.star.formula.FormulaProperties
.
Base
.
(000) = com.sun.star.sdb.OfficeDatabaseDocument
(001) = com.sun.star.document.OfficeDocument
.
This information leads to two questions regarding the services supported by the different document types:
- Is it safe to assume that these values are always valid for any document object of a specific type?
.
For example, it seems safe to assume that every document of type Calc spreadsheet always supports the servicecom.sun.star.sheet.SpreadsheetDocument
.
.
It also seems to safe to assume that all documents always support the servicecom.sun.star.document.OfficeDocument
because this is the generic template from which other document types are derived. So even though I can test whether a document supports this service if I’m very bored, there seems to be no point because the result will always beTrue
.
.
But what aboutcom.sun.star.sheet.SpreadsheetDocumentSettings
for example? Is it conceivable that a document that supportscom.sun.star.sheet.SpreadsheetDocument
might NOT supportcom.sun.star.sheet.SpreadsheetDocumentSettings
… i.e. is there any reason to test for this? Or can I safely assume that all Calc spreadsheet documents always supportcom.sun.star.sheet.SpreadsheetDocumentSettings
?
.
Hence my original question:
.
are [there] use cases for the
SupportsService
method when it is used in connection with a document object that go beyond determining the type of document? For example, it would also be possible to test a document object to see if it supports the Servicecom.sun.star.document.OfficeDocument
, but I’m not sure how this would be useful.
- Are the services I detected with MRI and listed above the only ones supported by document objects (again, it is important to note the distinction: supported by document objects, not by any other object types)? Or should I expect document objects to support other services in addition to these?
StarBasic is your taxi cab.
UNO is the city you are cruising in.
You can cruise the same city with Python, JavaScript, BeanShell, Java and C++. The other cabs are less tolerant, reading the electric street signs case-sensitively as documented.
I recommend to stick with the documented notation which makes it very easy to “translate” code from StarBasic into real programming languages.
Once you passed the string over to the UNO API, it is interpreted case-sensitively.
- Services are not types, they are just contracts. The types are interfaces. It’s interfaces that actually define which methods an object has.
- You explicitly narrow down the scope of question, down to a meaningless example. Yes, when you have an object, and you already know that it is a document object, then no, asking if it supports
com.sun.star.document.OfficeDocument
(if we ignore a possibility of another document type, not one of LibreOffice-defined document types). - But in general, you search for shortcuts. Instead of checking if an object supports a service, and then use that service’s methods / properties, you try to rely on an assumption that if the object supports another unrelated service (like
com.sun.star.sheet.SpreadsheetDocument
), it would also support this service. It will mostly work. I would be on the safe side, but it’s up to you. I know that I can implement any interface using Basic, implement in them any services, e.g.com.sun.star.sheet.SpreadsheetDocument
, but notcom.sun.star.sheet.SpreadsheetDocumentSettings
, and that object would fail your assumptions.
No. Basic is the language. When it’s asked to call a method of an object, its binding introspects into the object’s methods, and tries to find any, which name is equals case-insensitively. If it finds, it calls it.
See the difference between the method name (which may be found by Basic using case-insensitive search, if the methods of the objects are known - using introspection), and the string passed to the method (which will be processed somewhere else, so there is no way for Basic to affect the external processing to magically become case-insensitive).
While that may be true, I believe you may have a different perspective than a VBA programmer who is trying to understand LibreOffice Basic.
.
I do not program Python, so I asked ChatGPT to give a Python example of how to use the supportsService method to test ThisComponent for a specific service name. Here is the result, and I hope it’s accurate enough:
.
# Access the desktop
desktop = context.ServiceManager.createInstanceWithContext(
"com.sun.star.frame.Desktop", context
)
# Get the current component (active document)
model = desktop.getCurrentComponent()
# Check if the active document supports the specified service
if model.supportsService("com.sun.star.sheet.SpreadsheetDocumentSettings"):
print("The active document supports the service 'com.sun.star.sheet.SpreadsheetDocumentSettings'.")
else:
print("The active document does NOT support the service 'com.sun.star.sheet.SpreadsheetDocumentSettings'.")
Now please compare this to the following Basic code:
.
Print ThisComponent.supportsService("com.sun.star.sheet.SpreadsheetDocumentSettings")
.
I’m not an expert, but as a VBA programmer moving to LibreOffice Basic, it seems to me that there are huge differences in the syntax for how to use supportsService in Python and Basic, and as I mentioned, I am trying to understand the LibreOffice API as it would be used in Basic.
.
I recommend to stick with the documented notation which makes it very easy to “translate” code from StarBasic into real programming languages.
Well obviously everyone is entitled to have their preferences. Nevertheless, LibreOffice Basic exists and can be used by programmers if they wish. As far as I know, the Document Foundation does not have plans to remove Basic entirely from LibreOffice. Or does it? Because if it doesn’t intend to drop Basic entirely, then I fail to see why people should not use it. Why all of this soft pressure to switch to Python? My personal opinion is that if Basic is so bad, then remove it from LibreOffice. Just get rid of it. But if you don’t remove it, then, for better or worse, people will want to understand and use it.
I’m sorry, my bad. In VBA you deal with objects, methods, properties and events. VBA does not have contracts or guarantees, so I failed to see this distinction. I will study this more to understand it better. Thank you for pointing this out.
I’m very sorry you feel this way. I don’t feel like my example is meaningless at all. The following is a valid Basic statement:
.
Print ThisComponent.supportsService("com.sun.star.sheet.SpreadsheetDocumentSettings")
So I can do this, if I want to. But is there any situation where I would ever want to? This is a sincere question. I’m not trying to annoy anyone. I’m not being pedantic. I’m just curious. For all I know, the answer might be, “Yes, there are good reasons to do this.”