LibreOffice Developer's Guide: Appendix A - LibreOffice API-Design-Guidelines
The following rules apply to all external programming interface specifications for OpenOffice. The API consists of the following stereotypes or design elements:
Structures
- Structures are used to specify simple composed data elements.(Structures only consist of data, not methods.)
Exceptions
- Exceptions are used for error handling.(Exceptions can be thrown or transported using an any.)
Interfaces
- Interfaces are used to specify a single aspect in behavior.(Interfaces only consist of methods, not data.)
Services
- Services are used to specify abstract objects.(Services specify properties and the interaction of the supported interfaces.)
Typedefs
- Typedefs are used to define basic types for specific purposes.(This stereotype should be used carefully.)
General Design Rules
These rules describe basic concepts used in LibreOffice API design. They are mandatory for all LibreOffice contributions. They are recommended good practice for development of third-party software.
Universality
It is preferable to design and use universal interfaces instead of specialized ones. Interface reuse should prevail. Whenever a new interface is about to be created, consider the possibility of similar requirements in other application areas and design the interface in a general manner.
Orthogonality
The functionality of interfaces should extend each other. Avoid redundancy, but if it leads to a major simplification for application programmers, proceed. In general, functionality that can be acquired from basic interfaces should not be added directly. If necessary, create an extra service which provides the functionality and works on external data.
Inheritance
All interfaces are derived from com.sun.star.uno.XInterface. Other superclasses are only allowed if the following terms are true:
- the derived interface is a direct extension of the superclass
- the superclass is necessary in every case for the interface and inheritance if it is logical for the application programmer
- the superclass is the only possible superclass due to this definition
Uniformity
All identifiers have to follow uniform rules for semantics, lexical names, and order of arguments. Programmers and developers who are familiar with any portion of the API can work with any other part intuitively.
Correct English
Whoever designs API elements is responsible for the correct spelling and meaning of the applied English terms, especially for lexical names of interfaces, methods, structures and exceptions, as well as members of structures and exceptions. If not absolutely certain, use Merriam-Webster's Dictionary (https://www.m-w.com). We use U.S. spelling.
Mixed capitalization or underscores (the latter only for lexical constants and enumeration values) are used to separate words within single identifiers. Apply the word separation appropriately. English generally does not have compound words, unlike, for example, German.
Identifier Naming Convention
For common naming of identifiers, and to prevent potential restrictions for identifiers in UNO language bindings, a general naming convention has been defined. This naming convention allows a restricted set of identifiers, where no problems in any language bindings are expected. However, this cannot be guaranteed.
See the following pseudo-grammar, which shows, in a short but precise form, the permitted set of identifiers.
r* : zero or more r's, where r is a regular expression r+ : one or more r's r? : zero or one r (that is, "an optional r") r|s : either an r or a s [abc] : a "character class"; in this case the pattern matches either an 'a', a 'b' or a 'c' [a-z] : a "character class" with a range; matches any letter from 'a' through 'z' "xy" : the literal string xy (r) : match an r; parenthesis are used to override precedence
DIGIT [0-9] CAPITAL [A-Z] SMALL [a-z] ALPHA CAPITAL|SMALL IDENTIFIER (ALPHA(ALPHA|DIGIT)*)|(CAPITAL("_"?(ALPHA|DIGIT)+)*)
The following examples of valid and invalid identifiers should make the preceding grammar clear.
Valid identifiers:
- getByName
- XText
- XText_2
- CLOSEABLE
- FATAL_ERROR
- Message
Invalid identifiers:
- _getByName
- _Name
- _name
- get_Name
- myName_2
When you define your own IDL specification, you should take this naming convention into account.
Definition of API Elements
In this chapter, several API elements are defined, and how they are used and the rules that apply.
Attributes
Attributes are used to access data members of objects through an interface.
Naming
Attributes are defined in interfaces as get and optional set methods. Although UNOIDL knows attributes for compatibility reasons, this feature is not used. The attribute identifier begins with a capital letter. The mixed upper and lower case method is used to separate single words. Only letters and numbers are allowed with no underscores, for example, getParent()
and setParent()
.
Usage
Attributes are used to express structural relationships, with and without lifetime coupling. For scripting languages, the attributes are accessed as properties.
Methods
Methods are functions defined within an interface. Technically, an interface only consists of methods. There is a syntax for attributes, but these map to methods.
Naming
Method identifiers begin with a verb in lowercase, for example, close, and continue with initial caps, that is, the first letter of each word is capitalized with no underscores. For example, getFormat()
.
Method names consisting of a verb without any additional terms can only be used if they refer to the object as a whole, and do not operate on parts of the object specified with arguments of this method. This makes names semantically more precise, and we avoid the risk of two method names of two different interfaces at the same object folding into each other causing problems with scripting languages.
Special attention should be given to uniformity within semantically related interfaces. This means, if a method is named destroyRecord()
, an insert method should be called insertRecord()
.
If a method refers to a part of the object and an argument specifies this part, the type or role of the part is appended to the verbal part of the method,for example, removeFrame( [in] XFrame xFrame )
. If the name of the part or its position is specified as an argument, ByName
or ByIndex
is additionally appended, for example, removeFrameByName( [in] string aName )
or removeFrameByIndex( [in] long nIndex )
.
The following method prefixes have special meanings:
get
- To return non-boolean values or interfaces of other objects that have a lasting relationship with the object the associated interface belongs to, similar to being an attribute. This prefix is generated automatically for readable attributes. Multiple calls to the same method at the same object with the same arguments, without modifying the object in between, returns the same value or interface.
set
- To set values or interfaces of other objects that get into a lasting relationship with the object the associated interface belongs to, similar to becoming attribute values. This prefix is generated automatically for writable attributes.
query
- This prefix is used to return values, including interfaces that have to be calculated at runtime or do not have the character of being a structural part of the object which belongs to the associated interface. Multiple calls, even without modifying the object in between, do not necessarily return the same value and interface; but this can be specified in the specific methods.
is/has
- Usage is similar to get, and is used for boolean values.
create
- This prefix is used for factory methods. Factory methods create and return new instances of objects. In many cases, the same or a related interface has an insert or add method.
insert
- This prefix inserts new sub objects into an object when the insertion position is specified.
add
- This prefix inserts new sub objects into an object when the insertion position is not specified by any argument of the method.
append
- This prefix inserts new sub objects into an object when the new sub object gets appended at the end of the collection of sub objects.
remove
- This prefix removes sub objects from a container. Use destroy if the removal implies the explicit destruction of the sub object. If the sub object is given as an argument, use it's type or role additionally, for example,
removeFrame()
if the argument is a Frame. If the position index or name of the sub object to remove is given, use a name similar toremoveFrameByName()
. For generic interfaces, useremoveByName()
without the type name or role, which are unknown in that case.
destroy
- This prefix removes sub objects from a container and explicitly destroys them in this process. Use destroy as a verbal prefix. For more details, see the description of remove.
clear
- This prefix clears contents of an object as a whole, the verb itself, or certain parts of the object, such as add a specifying name giving something like
clearDelegates()
.
dispose
- This prefix initiates a broadcast message to related objects to clear references to the object. Normally, this verb is only used in
XComponent
.
approve
- This prefix is used for the approval notification in listener interfaces of prohibited events.
Usage
Non-structural attributes are represented by the property concept, and not by get and set methods, or attributes of interfaces.
Consider possible implementations if there are several possible interfaces where you could put a method. For example, a file cannot destroy itself, but the container directory could.
Do not use const
as an attribute for methods, because future versions of UNOIDL will not support this feature.
Interfaces
Interfaces are collections of methods belonging to a single aspect of behavior. Methods do not have data or implementation.
Once an interface gets into an official release of the API, it may no longer be changed. This means that no methods or attributes can be added, removed or modified, not even arguments of methods. This rule covers syntax, as well as semantics.
Interfaces are identified by their name.
Naming
Identifiers of interfaces begin with the prefix 'X' followed by the name itself in initial caps, capitalizing the first letter after the 'X', for example, XFrameListener
. Avoid abbreviations.
We apply the prefix 'X', because interfaces have to be treated differently than pointers in C/C++ and also in normal interfaces in Java. It is also likely that the main interface of a service should get the same name as the service that can cause confusion or ambiguity in documentation.
It is a bad design if the name or abbreviation of a specific component appears within the name of an interface, for example, XSfxFrame
or XVclComponent
.
Usage
Interfaces represent stable aspects of design objects. A single interface only contains methods that belong to one aspect of object behavior, never collections of arbitrary methods. Both aspects of usage, client and server, should be considered in design. Keep the role of the object in mind. If some methods of your new interface are only used in one role and others in another role, your design is probably flawed.
Properties
Properties are descriptive attributes of an objects that can be queried and changed at runtime using the XPropertySet
interface.
Naming
In non-scripting languages, such as Java or C/C++, property identifiers are simply strings. These identifiers always begin with an uppercase letter and use initial caps, for example, BackgroundColor
. Avoid abbreviations.
Usage
Properties are used for non-structural attributes of an object. For structural attributes (composition) use get and set methods in an interface instead.
Events
Events are notifications that you can register as listeners. This concept is actually expressed by registration or unregistration methods for the broadcaster, listener interfaces for the listener and event structures for the event.
Naming
If an object broadcasts a certain event, it offers a pair of methods like addEventNameListener()
and removeEventNameListener()
. This scheme conforms to the naming scheme of JavaBeans and does not mean that the implementation keeps track of a separate list for each event.
The event methods of the listener interface use the past tense form of the verb that specifies the event, usually in combination with the subject to which it applies, for example, mouseDragged()
. For events which are notified before the event actually happens, the method begins with notify, for example, notifyTermination()
. Event methods for prohibited events start with the prefix approve, for example, approveTermination()
.
Usage
Use events if other, previously unknown objects have to be notified about status changes in your object.
Normally, the methods add...Listener()
and remove...Listener()
have a single argument. The type of argument is an interface derived from com.sun.star.lang.XEventListener.
The event is a struct derived from com.sun.star.lang.EventObject, therefore this struct contains the source of the event.
Services
Services are collections of related interfaces and properties. They specify the behavior of implementation objects at an abstract level by specifying the relationship and interaction between these interfaces and properties. Like interfaces, services are strictly abstract.
Naming
Service identifiers begin with an uppercase letter and are put in initial caps , for example, com.sun.star.text.TextDocument. Avoid abbreviations.
Usage
Services are used by a factory to create objects which fulfill certain requirements. Not all services are able to be instantiated by a factory, but they are used for documentation of properties or interface compositions. In a service, you can specify in detail what methods expect as arguments or what they return.
Exceptions
Exceptions are special classes which describe exceptional states.
Naming
Exception identifiers begin with a capital uppercase letter, and are put in initial caps and always end with Exception, for example, com.sun.star.lang.IllegalArgumentException. Avoid abbreviations.
Usage
The LibreOffice API uses exceptions as the general error handling concept. However, the API should be designed that it is possible to avoid exceptions in typical error situations, such as opening non-existent files.
Enums
Enums are non-arbitrary sets of identifying values. If an interface uses an enum type, all implementations have to implement all specified enum values. It is possible to specify exceptions at the interface. Extending enums is not allowed, because this would cause incompatibilities.
Naming
Enum types begin with an uppercase letter and are put in initial caps. Avoid abbreviations. If there is a possible name-conflict with structs using the same name, add Type or Style to the enum identifier.
Enum values are completely capitalized in uppercase and words are separated by underscores. Do not use a variant of the enum type name as a prefix for the values, because some language bindings will do that automatically.
enum FooBarType
{
NONE,
READ,
WRITE,
USER_DEFINED = 255
};
struct FooBar
{
FooBarType Type;
string FileName
};
Three typical endings of special enum values are _NONE
, _ALL
and _USER_DEFINED
.
Usage
If by general fact an enum represents the most common values within an open set, add a value for USER_DEFINED
and specify the actual meaning by a string in the same object or argument list where the enum is used. In this case, offer a method that returns a sequence of all possible values of this string.
Typedefs
Typedefs specify new names for existing types.
Naming
Typedefs begin with an uppercase letter and are put in initial caps. Avoid abbreviations.
Usage
Do not use typedefs in the LibreOffice API.
Structs
Structs are static collections of multiple values that belong to a single aspect and could be considered as a single, complex value.
Naming
Structs begin with an uppercase letter and are put in initial caps. Avoid abbreviations.
If the actual name for the struct does not sound correct, do not add Attributes
, Properties
or the suffixes suggested for enums. These two words refer to different concepts within the LibreOffice API. Instead, use words like Format
or Descriptor
.
Usage
Use structs as data containers. Data other than interfaces are always copied by value. This is an efficiency gain, especially in distributed systems.
Structs with just a single member are wrong by definition.
Parameter
Parameters are names for arguments of methods.
Naming
Argument identifiers begin with a special lowercase letter as a prefix and put in initial caps later, for example, nItemCount.
Use the following prefixes:
- 'x' for interfaces
- 'b' for boolean values
- 'n' for integer numbers
- 'f' for floating point numbers
- 'a' for all other types. These are represented as classes in programming languages.
Usage
The order of parameters is defined by the following rule: Where, What, How. Within these groups, order by importance. For example, insertContent(USHORT nPos
, XContent
xContent
,and boolean bAllowMultiInsert
.
Special Cases
Error Handling (Exceptions/Error-Codes)
Runtime errors caused by the wrong usage of interfaces or do not happen regularly, raise exceptions. Runtime errors that happen regularly without a programming mistake, such as the non-existence of a file for a file opening method, should be handled by using error codes as return values.
Collection Interfaces
Collection-Services usually support one or multiple X...Access-Interfaces and sometimes add access methods specialized to the content type. For example,
XInterface XIndexAccess::getElementByIndex(unsigned short)
becomes
XField XFields::getFieldByIndex(unsigned short).
Postfix Document for Document-like Components
Components, whose instances are called a document, get the postfix Document to their name, for example, service com.sun.star.text.TextDocument.
Postfixes Start/End vs. Begin/End
The postfixes ...Start/...End are to be preferred over ...Begin/...End.
Abbreviations
Avoid abbreviations in identifiers of interfaces, services, enums, structs, exceptions and constant groups, as well as identifiers of constants and enum values. Use the following open list of abbreviations if your identifier is longer than 20 characters. Remain consistent in parallel constructions, such as addSomething()
or removeSomething()
.
Abs
: AbsoluteBack
: BackgroundChar
: CharacterDoc
: DocumentExt
: Extended, ExtensionDesc
: Description, DescriptorRef
: ReferenceHori
: HorizontalOrient
: OrientationPara
: ParagraphVar
: VariableRel
: RelativeVert
: Vertical
Source Files and Types
For each type, create a separate IDL file with the same base name and the extension .idl.