Monikers mail (_really_ long)



Hi, 

Well, as the title says, this is a mail about Monikers.

Monikers are a real tough thing. difficult to understand
and OLE 2 way of solving the problem Monikers represent
is extremly complex (to say the least). Also, Inside OLE
does a VERY bad job at explaining how they work (chapter 9)
So, 
1) will explain what Monikers are used for.
2) will exlain how they use Monikers in OLE.
3) will explain how we will use Monikers in Bonobo.

1) What is a moniker for ?
--------------------------

Monikers are used for Linking. They are one of the main 
feature of OLE (Object LINKING and Embedding) at least
as important as Embedding (which works now in Bonobo).

Monikers don't really work in Bonobo now. (Slap me Miguel,
slap me... ahhh... /me likes this !)

1.1) Linking.

You have done this more than once if you ever used Mikrosoft's
products: it is possible to embedd a linked object in your 
documents. Actually, linked objects look like any other
embedded object in your document (actually, OLE specifies that 
linked embeddables should have a continuous frame border
in their container. So, I am partly wrong but they behave just 
like any other embeddable.). 
The only specific thing about them is that the data they create
a view for is not included in your document but in another 
file. Typically, this means that if you work on your embedded 
image with Gimp adn you save your changes, this will be 
reflected in your other document: the data has changed...
This is similar to pointers in C versus the variables themselves.

So, a moniker is a pointer.

A moniker is a CORBA object which implements a certain 
amount of interfaces which allow you to manipulate the link, 
get the CORBA object which can embedd this link and so on...

1.2) Why do we need monikers for this ?

Why not having the user type in the name of the file for 
this image and save the file name in your container and use it 
directly to embedd stuff ?
Okay, the whole idea of monikers is that they allow you to
work with any kind of links. Actually, links can be much more 
complex than a simple file name. Links can refer to embedded 
objects from another document, to parts of another embedded 
Object from another document... 

For example, the folowing is a valid link: 
file:///home/mathieu/list.gnumeric!sheet 1!A2:A9
This link refers to the data contained in cells A2 through
A9 in the first sheet of the list.gnumeric file.
The idea behind all this is that your container cannot have 
enough knowledge for all kinds of embeddables to be able to
understand all these types of links.

Monikers are used to hold all the magic of links inside them.
They are supposed to be able to save and load themselves from
any Stream and they can "bind". ie: they can return a CORBA 
object given the corresponding link and hide all the resolving
in themselves. Monikers implemment some kind of Name Service.

1.3) OLE.

Linking in OLE is done with 3 main interfaces:
IMoniker is an interface which inherits the IPersistStream
interface.
IOLEParseDisplayName is an interface which has only the
ParseDisplayName method.
IOleItemContainer is an interface which has the GetObject method.
IPersistFile is necessary for the embeddable to load its
state from a linked file.



2) how do monikers work in OLE?
-------------------------------

Note: in the folowing, I use Bonobo interfaces
to demonstrate how it works in OLE.
If this is not the way it works now in Bonobo,
It is what Miguel wants to implement.

2.1) CompositeMonikers, FileMonikers, ItemMonikers.

OLE defines a number of interfaces inheriting the Moniker
interface. In OLE, we have the 3 folowing interfaces:
- CompositeMonikers
- FileMonikers
- ItemMonikers
Composite Monikers are composites of File or Item or even
other Composite Monikers.

The folowing schema shows how a Composite Moniker is built.
-------------------------------------------------------
|                     Composite                       |
|               Left                        Right     |
| ---------------------------------      -----------  |
| |           Composite           |      | Item    |  |
| |     Left            Right     |      |         |  |
| |  -----------    -----------   |      | Moniker |  |
| |  |  File   |    |  Item   |   |      |         |  |
| |  | Moniker |    | Moniker |   |      -----------  |
| |  -----------    -----------   |                   |
| ---------------------------------                   |
-------------------------------------------------------

A composite Moniker can be built either incrementaly or
from a Data Stream.
- You can call Bonobo::Moniker::compose (moniker). This 
will return a new Composite Moniker made of a Left Moniker 
and a Right Moniker.
- You can call Bonobo::PersistStream::load on a newly
created Moniker. 

File Monikers hold the path to the file of the link and
the information which allows them to lauch the application
which can open this file.

Item Monikers hold the rest of the link data. ie: the part 
to the right of the File Moniker.

The folowing link: 
file:///home/mathieu/list.gnumeric!sheet 1!A2:A9
Would be encapsulated in the folowing Moniker
-------------------------------------------------------
|                     Composite                       |
|               Left                        Right     |
| ---------------------------------      -----------  |
| |           Composite           |      |   Item  |  |
| |     Left            Right     |      |  A2:A9  |  |
| |  -----------    -----------   |      |         |  |
| |  | File    |    |  Item   |   |      |         |  |
| |  |  /home/ |    | Sheet 1 |   |      |         |  |
| |  | mathieu/|    |         |   |      -----------  |
| |  | list.gnu|    |         |   |                   |
| |  -----------    -----------   |                   |
| ---------------------------------                   |
-------------------------------------------------------

2.2) How to use such a Moniker ?

Okay, now, the idea of this Moniker is that your Container 
will behave with it just like with any other embedded component.
More precisely, It will hold a reference to it.
To save its state, it will ask the Moniker to save itself.
This is possible because the Moniker supports the 
Bonobo::PersistStream interface just like any other component.
When the container needs to display the embeddable, it will
recreate the Moniker from the saved stream and it will ask the 
moniker to give him back the real object's reference.
This means the Moniker must lauch the application responsible
for the opening of the linked file, ask the application to load
the corresponding file, ask eth application for its Embeddable
interface to embedd it.

To do all this, all you have to do is to call the
Bonobo::Moniker::bind_to_object method.
In the example above, you would call the outer bind_to_object.
This Moniker will call the bind_to_object of the inner Monikers.

2.3) How to create a Moniker ?

Now, what we have seen till then is how to deal with Monikers
which already existed or which load their state from data
streams which were created by Monikers which existed once.
The main problem is: how do you create a Moniker from scratch ?

There are mainly 3 ways to obtain Monikers: 
	- The user gives a flat string which represents the 
	linked data:
	file:///home/mathieu/list.gnumeric!A5:A10.
	- The Linking client (ie: the container) receives 
	Stream Data from a Moniker which saved itself either 
	in the clipboard or in a file.
	- The Linking client (ie: the container) receives 
	some data from the Server which created a moniker 
	through the clipboard. This data could be the GOAD_ID
	of the Moniker created by the Server.

points 2 and 3 are rather similar in that point 2 will resolve 
eventually to 3 because the client can create a Moniker and 
call Moniker::load (inherited through its PersistStream 
interface.)
point 1 is solved in InsideOle by the MkParseDisplayName API 
function which hides all the magic of such a name parsing. In 
Bonobo, we will need a similar API function to do this.
So, the question is: what is all this magic ?

Basically, it's rather complex. This is where most of the Magic 
lies in the Linking stuff.

2.4) What is the magic ?

The MkParseDisplayName function will return a newly created 
Composite Moniker correctly initialized to hold all of the Link data.
Basically, this method will parse the link string till it gets to the 
1st "!" (or "bang" as it is called by the OLE people). Then, it will 
find the application associated with the corresponding file and 
create a FileMoniker initializing it with the file path and a 
reference to the necessary application.
Then, the function cannot really parse the string because it has 
no knowledge of the inner workings of the application which gave 
away the link.
This simply means that the MkParseDisplayName must delegate the 
parsing to the application which gave away the original link 
string.
This is done with the ParseDisplayName method which the Container 
must implement. the MkParseDisplayName will call the container's 

ParseDisplayName method and give it as parameters the part of the 
link string which has not been parsed.
The container is supposed to return a CompositeMoniker which holds 
the rest the link.
The details of the way an ItemMoniker works are not really important.
The only thing people must know is that ItemMonikers will call the 
application's GetObject method during the binding process.
(In OLE, GetObject is in the IOleItemContainer interface which must 
be implemented by the container. In Bonobo, get_object is in the 
Bonobo::Container interface)
Actually (I can't resist the need of explaining it), get_object 
will be called by the ItemMonikers with as a parameter the string 
which represents the Item.
In the case I explained above, the Bonobo::Container::get_object 
method will be called with first the "Sheet 1" parameter and then 
with the "A5:A10" parameter.
So, here is the list of calls made by the different Monikers of 
the outer CompositeMoniker's bind_to_object.
The cascaded calls of bind_to_object will result in:
	- the FileMoniker launching the application.
	- The most inner ItemMoniker calling the application's 
	app = Container::get_object ("Sheet 1").
	- The most outer ItemMoniker calling the app
	Container::get_object ("A5:A10").




3) How could we do it ?
-----------------------


Actually, Bonobo plans to do it the OLE way. Miguel is 
aware of the fact that Bonobo implementation of Monikers 
is "SubOptimal" (to say the least).

3.1) How Miguel plans to do it.

I think that what miguel plans to do is the folowing:

Here is the bonobo-moniker.idl file which should be used:
module Bonobo {	
	interface Moniker {
			       
		exception MissingInterface {
			string interface_name;
			string object_name;
		};

		/**
		 * bind_to_object:
		 * @left_moniker: moniker for the left side
		 * @requested_interface: which interface we want in the end
		 *
		 * Binds a moniker under bind_context, the left
		 * moniker (ie, the parent moniker is left_moniker), once the
		 * service has been registered, it will attempt to get
		 * the @requester_interface
		 */

		Object bind_to_object (in Moniker left_moniker,
				       in string requested_interface)
			raises (MissingInterface);

		/**
		 * bind_to_storage:
		 * @left_moniker: parent moniker (left side)
		 * @persistent_interface_name: the name of the persistent
		 * object (GNOME::Storage, Bonobo::Stream, GNOME::Lockbytes).
		 */
		Object bind_to_storage (in Moniker left_moniker,
					in string persistent_interface_name);

		Moniker compose_with (in Moniker right, in boolean only_if_exists);

		typedef sequence<Moniker> MonikerList;

		/**
		 * get_display_name:
		 * 
		 * Returns a representation of the Moniker
		 */
		string get_display_name (in Moniker left);

					    
	};

	interface ParseDisplayName {
		/**
		 * parse_display_name:
		 *
		 * Returns a moniker from the string representation
		 */
		Moniker parse_display_name (in Moniker left,
					    in string display_name,
					    out short display_name_bytes_parsed);
	}

};


The BindContext would be used here as a repository to 
hold objects during the binding phase: it would hold a 
reference to the objects so that they stay in memory 
during the binding.
Monikers would be requested to implement the PersistStream 
interface also.
Containers would be requested to implement the ParseDisplayName
interface also.
Please, note that I have removed a lot of cruft from the 
bonobo-moniker.idl original file. More specifically, I 
have removed the BindContext stuff and the Options flags
during binding. What I removed is stuff used to optimize 
the binding. (Miguel, prove me wrong: why is what I 
removed so necessary ?)

This precedent proposition would also need a 
BonoboMoniker *
bonobo_moniker_relative_new (BonoboMoniker moniker, char **path) 
API function. This function would:
	- call the moniker::get_display_name
	- compare the "path" and the returned display name.
	and create a relative path to the linked document
	from the "path".
	- call bonobo_moniker_parse_display_name on the 
	newly created path to get a relative moniker
	designating the same linked document.

3.2) How we could do it.

Basically, the precedent proposition is nothing but OLE 
without all its optimizations and without a lot of its 
options.
OLE has a lot of other cruft.
It has interfaces designed to be implemented by the container 
to provide smooth standard Dialog Boxes (like the "Links"
DialogBox.
It has optimizations to make sure that the binding (which 
can be VERY costly in terms of computing time) does not
take more time than a given timeout. BindContexts can be
used for this also.

Basically, I feel that the above proposition is nice and 
all. However, it's fucking complex (although much more 
simple than OLE...) and it will get even more complex 
if we really want to support the "Links" DialogBox, 
the optimizations, and so on...

I am not too sure what how we could do this otherwise
but I must say that all this seems quite complex for me
and perhaps we should try to find another way to do it.
/me is noew getting through the OpenDoc way to dealing 
with this problem.

I hope that this mail will raise an interesting 
discussion on this topic.


regards,
Mathieu



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]