JavaSwingText components and Document: Document


Text access: getLength, getText (twice)

There is no way to access a single character. Either you need the clumsy Segment setup, or have a String created. This is really awkward.

Text modification: insertString, remove

Usual discussion: how exactly do the methods ensure which modifications have actually taken place? As a) JDK1.4 introduces a DocumentFilter anyway and b) arbitrary removals would damage the document structure in sometimes unexpected ways (at least with DefaultStyledDocument), these methods can be regarded only as suggestions. That means that the Document may convert the text as it likes and ignore any of the given attributes. (But if any changes happen, I would assume it to take place at the given positions and not somewhere else).

insertString(): There may be cases where you would want to specify the insertion position with a bias, for example, when inserting between two elements, you may want to associate the text to the first or the second element (With the standard element structure, this doesn't matter, as elements will be created implicitly based on the attributes anyway, but there is nothing in document that requires that). If there were, position handling would be still more complicated.

Are the attributes taking in addition to those present at the specified position or only them? (the latter with standard implementations)

There are several reasons why the AttributeSet given to insertString() isn't used directly:

There are no direct methods to suggest replacing a text range by another. In fact, the event protocol (DocumentEvent) does not make it possible to do this atomically: you always have to handle the remove, then fire an event, than the add (or the other way round) and fire a second event. As both insertString() and remove() are to be regarded as suggestions and may only do part of the action, straight-forward replacing may have confusing results. Concerning multithreading, there is no general way for atomic replaces, even if if insertString() and remove() obtain a (write) lock, the document may change inbetween.

Position management: getStartPosition, getEndPosition, createPosition

As getStartPosition()'s result Position always returns 0, and getEndPosition()'s always should return Document.getLength(), the existence of these methods is rather senseless, since the implementation can be the same for all Documents.

Except with AbstractDocument: getEndPosition() returns getLength() + 1 which I believe is wrong, since the index of the position is not valid. The two methods, are never used internally.

The Positions created by createPosition() associate forwards (except with argument 0 , which is a design bug, since you cannot get positions which are at the current start of the document and move by any insertions at the beginning). This seems to be intentional because it is needed by the Element implementations (which could have been done differently: it is done differently for every paragraph but the first one).

createPosition() maybe should have taken a bias argument to indicate whether the position should stay before or after text inserted by insertString() at its index.

Implementation note: Positions can not be implemented simply as normal DocumentListeners because then they might update too late. They must be updated before firing DocumentEvents to other listeners.

Anyway, the Document has to know about the created position, so there are problems with garbage collection. The standard implementations use indirection (both Document and Position hold a reference to the actual index, and references to the Position are counted manually, if there are no more, the actual index object is removed); it is also possible to use weak references.

The notification mechanism of document doesn't allow text replacements - only additions and removals. That means that positions contained in a range of text that is "changed" (i.e. first the new text is added, then the old removed, or vice versa), always move to the beginning or end of the range, depending on the order of the mutations.

As noted above, position 0 is a special case. Any position (created by the standard methods) that ever moved to index 0 will always remain there (the end position can never be at index 0 because of the implied newline with AbstractDocument). See (#4204837).

Element access: getRootElements, getDefaultRootElement

It is virtually impossible (except by circumventing BasicTextUI) to display the element structure of another than the default root element.

Property management: getProperty, putProperty

This is discussed in a separate text.

Multithreading support: render

See multiple threads article.

BadLocationException (rant)

What is the difference between BadLocationException and IndexOutOfBoundsException? Why should one be checked and the other not?

In JDK1.3.0_01, BadLocationException is declared 137 times, thrown 32 times (and each time it is clearly a precondition validation), caught 101 times, about half of that time it is ignored, about one third another exception is thrown, the rest of time either beep(), or return some default value ('null' or -1).

The only justification (not "reason") I can imagine for BadLocationException existing at all is that because of the strange way of multithreading support, often indices are provided which were valid by the time their were calculated, but aren't any more by the time are used. If IndexOutOfBoundsException were thrown (and not caught), it would terminate the processing of the current event and cause a lot of Objects with an unconsistent state. Using the checked BadLocationException instead forces the caller to catch it, so event processing at a higher level still can continue. All this may happen only because multithreading for Document is broken. It wouldn't be a problem if locking could be done properly; then such invalid indices could never occur.

Result: A BadLocationException always is bug in the caller, and thus shouldn't be checked. To workaround its existance, a) always ensure the preconditions (as you should do anyway), b) for clearer code, use wrapper methods that catch BadLocationException and rethrow IndexOutOfBoundsException. Don't call methods that throw it directly.

(C) 2001-2009 Christian Kaufhold (