On Jan 14, 2007, at 22:51, J.Pietschmann wrote:

Andreas L Delmelle wrote:
Based on Jörgs statistics, I'd say that the number of children will most likely never reach the level where using direct index- based access (ArrayList) has its benefits over traversing a tree of references (LinkedList).

There may be FOs, specifically fo:flow and fo:table-body, which may
have hundreds of children in real documents.

If the FOs use access functions for the children, even for adding,
each FO can implement a mechanism which suites its purposes best. In
particular, page masters and tables can store the regions in typed
fields, FOs which don't have children can get rid of the field

This happens already for some FOs. Tables for instance, override addChildNode() and store only bodies as their 'child nodes'. The header and footer have separate typed fields, the columns end up in a separate standard List. Behavior for markers is another example, they too end up in a separate collection (map).

I've already been experimenting locally with removing the List member, and replacing it with pointers to the neighbors. As I re- implemented FObj.addChildNode(), it occurred to me that FOs with a possibly larger amount of children would need to traverse the entire list to append child nodes to the end. For a handful of children, this costs virtually nothing, but as the list gets bigger... Could easily be solved though for the applicable FOs, by keeping a reference to the last added child, and giving it an addSibling() method.

I'm not sure whether the layout engine places any restrictions on the
access to FO children. Is it possible that access is not random?

FTM, there are only a handful of places in the code where the childNodes list is accessed using an index. The only way it can be accessed by classes outside the fo package is through FObj.getChildNodes(), and that method returns a ListIterator, not the list itself. The LMs only need access to these iterators while creating their childLMs, which is done in forward sequential order, i.e.

for (Iterator i = AbstractLM.fobjIter; i.hasNext();) { ... }

[Note: these are ArrayLists we're talking about. Iterators are far from the best approach with lists implementing RandomAccess. Not that this matters much if the lists are small...]

This fobjIter is created in the constructor of the LM. IIC, this means that there is currently no way to, for example, instantiate the FlowLM as soon as the first endBlock() event occurs, and keep extending the flow's list of child nodes in a different thread. The FlowLM's fobjIter will then throw a ConcurrentModificationException().

In the structure I'm currently playing with, it would be possible to construct an iterator over an FObj whose child nodes are not all known, move to the last node in the list, put the iterator in a stack (or keep a reference as an instance member), continue adding nodes in another thread, return, and continue iterating (a call to next() will then return the first node that was added after the iterator was paused).

That would be mighty dangerous if the access to the nodes was not so predictable, but as it stands now, this could turn out to be a benefit, if documented properly. Removal from and addition to the tail of the list are never a problem. The iterator will simply keep following the following- sibling reference until it is null. The head of the list might become a problem if one needs a decent implementation of nextIndex() and previousIndex(). If we never really need the index, then the fact that a child was added at a previous position will simply make the iterator go back a few steps further than where it started.



Reply via email to