Method from javax.swing.text.AbstractDocument Detail: |
public void addDocumentListener(DocumentListener listener) {
listenerList.add(DocumentListener.class, listener);
}
Adds a document listener for notification of any changes. |
public void addUndoableEditListener(UndoableEditListener listener) {
listenerList.add(UndoableEditListener.class, listener);
}
Adds an undo listener for notification of any changes.
Undo/Redo operations performed on the UndoableEdit
will cause the appropriate DocumentEvent to be fired to keep
the view(s) in sync with the model. |
protected Element createBranchElement(Element parent,
AttributeSet a) {
return new BranchElement(parent, a);
}
Creates a document branch element, that can contain other elements. |
protected Element createLeafElement(Element parent,
AttributeSet a,
int p0,
int p1) {
return new LeafElement(parent, a, p0, p1);
}
Creates a document leaf element.
Hook through which elements are created to represent the
document structure. Because this implementation keeps
structure and content separate, elements grow automatically
when content is extended so splits of existing elements
follow. The document itself gets to decide how to generate
elements to give flexibility in the type of elements used. |
public synchronized Position createPosition(int offs) throws BadLocationException {
return data.createPosition(offs);
}
Returns a position that will track change as the document
is altered.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
public void dump(PrintStream out) {
Element root = getDefaultRootElement();
if (root instanceof AbstractElement) {
((AbstractElement)root).dump(out, 0);
}
bidiRoot.dump(out,0);
}
|
protected void fireChangedUpdate(DocumentEvent e) {
notifyingListeners = true;
try {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i >=0; i-=2) {
if (listeners[i]==DocumentListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((DocumentListener)listeners[i+1]).changedUpdate(e);
}
}
} finally {
notifyingListeners = false;
}
}
Notifies all listeners that have registered interest for
notification on this event type. The event instance
is lazily created using the parameters passed into
the fire method. |
protected void fireInsertUpdate(DocumentEvent e) {
notifyingListeners = true;
try {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i >=0; i-=2) {
if (listeners[i]==DocumentListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((DocumentListener)listeners[i+1]).insertUpdate(e);
}
}
} finally {
notifyingListeners = false;
}
}
Notifies all listeners that have registered interest for
notification on this event type. The event instance
is lazily created using the parameters passed into
the fire method. |
protected void fireRemoveUpdate(DocumentEvent e) {
notifyingListeners = true;
try {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i >=0; i-=2) {
if (listeners[i]==DocumentListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((DocumentListener)listeners[i+1]).removeUpdate(e);
}
}
} finally {
notifyingListeners = false;
}
}
Notifies all listeners that have registered interest for
notification on this event type. The event instance
is lazily created using the parameters passed into
the fire method. |
protected void fireUndoableEditUpdate(UndoableEditEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i >=0; i-=2) {
if (listeners[i]==UndoableEditListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
}
}
}
Notifies all listeners that have registered interest for
notification on this event type. The event instance
is lazily created using the parameters passed into
the fire method. |
public int getAsynchronousLoadPriority() {
Integer loadPriority = (Integer)
getProperty(AbstractDocument.AsyncLoadPriority);
if (loadPriority != null) {
return loadPriority.intValue();
}
return -1;
}
Gets the asynchronous loading priority. If less than zero,
the document should not be loaded asynchronously. |
protected final AttributeContext getAttributeContext() {
return context;
}
Fetches the context for managing attributes. This
method effectively establishes the strategy used
for compressing AttributeSet information. |
public Element getBidiRootElement() {
return bidiRoot;
}
Returns the root element of the bidirectional structure for this
document. Its children represent character runs with a given
Unicode bidi level. |
protected final Content getContent() {
return data;
}
Gets the content for the document. |
protected final synchronized Thread getCurrentWriter() {
return currWriter;
}
Fetches the current writing thread if there is one.
This can be used to distinguish whether a method is
being called as part of an existing modification or
if a lock needs to be acquired and a new transaction
started. |
abstract public Element getDefaultRootElement()
Returns the root element that views should be based upon
unless some other mechanism for assigning views to element
structures is provided. |
public DocumentFilter getDocumentFilter() {
return documentFilter;
}
Returns the DocumentFilter that is responsible for
filtering of insertion/removal. A null return value
implies no filtering is to occur. |
public DocumentListener[] getDocumentListeners() {
return listenerList.getListeners(DocumentListener.class);
}
Returns an array of all the document listeners
registered on this document. |
public Dictionary<Object, Object> getDocumentProperties() {
if (documentProperties == null) {
documentProperties = new Hashtable< Object, Object >(2);
}
return documentProperties;
}
Supports managing a set of properties. Callers
can use the documentProperties dictionary
to annotate the document with document-wide properties. |
public final Position getEndPosition() {
Position p;
try {
p = createPosition(data.length());
} catch (BadLocationException bl) {
p = null;
}
return p;
}
Returns a position that represents the end of the document. The
position returned can be counted on to track change and stay
located at the end of the document. |
public int getLength() {
return data.length() - 1;
}
Returns the length of the data. This is the number of
characters of content that represents the users data. |
public T[] getListeners(Class<T> listenerType) {
return listenerList.getListeners(listenerType);
}
Returns an array of all the objects currently registered
as FooListener s
upon this document.
FooListener s are registered using the
addFooListener method.
You can specify the listenerType argument
with a class literal, such as
FooListener.class .
For example, you can query a
document d
for its document listeners with the following code:
DocumentListener[] mls = (DocumentListener[])(d.getListeners(DocumentListener.class));
If no such listeners exist, this method returns an empty array. |
abstract public Element getParagraphElement(int pos)
Get the paragraph element containing the given position. Sub-classes
must define for themselves what exactly constitutes a paragraph. They
should keep in mind however that a paragraph should at least be the
unit of text over which to run the Unicode bidirectional algorithm. |
public final Object getProperty(Object key) {
return getDocumentProperties().get(key);
}
A convenience method for looking up a property value. It is
equivalent to:
getDocumentProperties().get(key);
|
public Element[] getRootElements() {
Element[] elems = new Element[2];
elems[0] = getDefaultRootElement();
elems[1] = getBidiRootElement();
return elems;
}
Gets all root elements defined. Typically, there
will only be one so the default implementation
is to return the default root element. |
public final Position getStartPosition() {
Position p;
try {
p = createPosition(0);
} catch (BadLocationException bl) {
p = null;
}
return p;
}
Returns a position that represents the start of the document. The
position returned can be counted on to track change and stay
located at the beginning of the document. |
public String getText(int offset,
int length) throws BadLocationException {
if (length < 0) {
throw new BadLocationException("Length must be positive", length);
}
String str = data.getString(offset, length);
return str;
}
Gets a sequence of text from the document. |
public void getText(int offset,
int length,
Segment txt) throws BadLocationException {
if (length < 0) {
throw new BadLocationException("Length must be positive", length);
}
data.getChars(offset, length, txt);
}
Fetches the text contained within the given portion
of the document.
If the partialReturn property on the txt parameter is false, the
data returned in the Segment will be the entire length requested and
may or may not be a copy depending upon how the data was stored.
If the partialReturn property is true, only the amount of text that
can be returned without creating a copy is returned. Using partial
returns will give better performance for situations where large
parts of the document are being scanned. The following is an example
of using the partial return to access the entire document:
int nleft = doc.getDocumentLength();
Segment text = new Segment();
int offs = 0;
text.setPartialReturn(true);
while (nleft > 0) {
doc.getText(offs, nleft, text);
// do something with text
nleft -= text.count;
offs += text.count;
}
|
public UndoableEditListener[] getUndoableEditListeners() {
return listenerList.getListeners(UndoableEditListener.class);
}
Returns an array of all the undoable edit listeners
registered on this document. |
void handleInsertString(int offs,
String str,
AttributeSet a) throws BadLocationException {
if ((str == null) || (str.length() == 0)) {
return;
}
UndoableEdit u = data.insertString(offs, str);
DefaultDocumentEvent e =
new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT);
if (u != null) {
e.addEdit(u);
}
// see if complex glyph layout support is needed
if( getProperty(I18NProperty).equals( Boolean.FALSE ) ) {
// if a default direction of right-to-left has been specified,
// we want complex layout even if the text is all left to right.
Object d = getProperty(TextAttribute.RUN_DIRECTION);
if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) {
putProperty( I18NProperty, Boolean.TRUE);
} else {
char[] chars = str.toCharArray();
if (SwingUtilities2.isComplexLayout(chars, 0, chars.length)) {
putProperty( I18NProperty, Boolean.TRUE);
}
}
}
insertUpdate(e, a);
// Mark the edit as done.
e.end();
fireInsertUpdate(e);
// only fire undo if Content implementation supports it
// undo for the composed text is not supported for now
if (u != null &&
(a == null || !a.isDefined(StyleConstants.ComposedTextAttribute))) {
fireUndoableEditUpdate(new UndoableEditEvent(this, e));
}
}
Performs the actual work of inserting the text; it is assumed the
caller has obtained a write lock before invoking this. |
void handleRemove(int offs,
int len) throws BadLocationException {
if (len > 0) {
if (offs < 0 || (offs + len) > getLength()) {
throw new BadLocationException("Invalid remove",
getLength() + 1);
}
DefaultDocumentEvent chng =
new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
boolean isComposedTextElement;
// Check whether the position of interest is the composed text
isComposedTextElement = Utilities.isComposedTextElement(this, offs);
removeUpdate(chng);
UndoableEdit u = data.remove(offs, len);
if (u != null) {
chng.addEdit(u);
}
postRemoveUpdate(chng);
// Mark the edit as done.
chng.end();
fireRemoveUpdate(chng);
// only fire undo if Content implementation supports it
// undo for the composed text is not supported for now
if ((u != null) && !isComposedTextElement) {
fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
}
}
}
Performs the actual work of the remove. It is assumed the caller
will have obtained a writeLock before invoking this. |
public void insertString(int offs,
String str,
AttributeSet a) throws BadLocationException {
if ((str == null) || (str.length() == 0)) {
return;
}
DocumentFilter filter = getDocumentFilter();
writeLock();
try {
if (filter != null) {
filter.insertString(getFilterBypass(), offs, str, a);
}
else {
handleInsertString(offs, str, a);
}
} finally {
writeUnlock();
}
}
Inserts some content into the document.
Inserting content causes a write lock to be held while the
actual changes are taking place, followed by notification
to the observers on the thread that grabbed the write lock.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
protected void insertUpdate(DefaultDocumentEvent chng,
AttributeSet attr) {
if( getProperty(I18NProperty).equals( Boolean.TRUE ) )
updateBidi( chng );
// Check if a multi byte is encountered in the inserted text.
if (chng.type == DocumentEvent.EventType.INSERT &&
chng.getLength() > 0 &&
!Boolean.TRUE.equals(getProperty(MultiByteProperty))) {
Segment segment = SegmentCache.getSharedSegment();
try {
getText(chng.getOffset(), chng.getLength(), segment);
segment.first();
do {
if ((int)segment.current() > 255) {
putProperty(MultiByteProperty, Boolean.TRUE);
break;
}
} while (segment.next() != Segment.DONE);
} catch (BadLocationException ble) {
// Should never happen
}
SegmentCache.releaseSharedSegment(segment);
}
}
Updates document structure as a result of text insertion. This
will happen within a write lock. If a subclass of
this class reimplements this method, it should delegate to the
superclass as well. |
boolean isLeftToRight(int p0,
int p1) {
if(!getProperty(I18NProperty).equals(Boolean.TRUE)) {
return true;
}
Element bidiRoot = getBidiRootElement();
int index = bidiRoot.getElementIndex(p0);
Element bidiElem = bidiRoot.getElement(index);
if(bidiElem.getEndOffset() >= p1) {
AttributeSet bidiAttrs = bidiElem.getAttributes();
return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0);
}
return true;
}
Returns true if the text in the range p0 to
p1 is left to right. |
protected void postRemoveUpdate(DefaultDocumentEvent chng) {
if( getProperty(I18NProperty).equals( Boolean.TRUE ) )
updateBidi( chng );
}
Updates any document structure as a result of text removal. This
method is called after the text has been removed from the Content.
This will happen within a write lock. If a subclass
of this class reimplements this method, it should delegate to the
superclass as well. |
public final void putProperty(Object key,
Object value) {
if (value != null) {
getDocumentProperties().put(key, value);
} else {
getDocumentProperties().remove(key);
}
if( key == TextAttribute.RUN_DIRECTION
&& Boolean.TRUE.equals(getProperty(I18NProperty)) )
{
//REMIND - this needs to flip on the i18n property if run dir
//is rtl and the i18n property is not already on.
writeLock();
try {
DefaultDocumentEvent e
= new DefaultDocumentEvent(0, getLength(),
DocumentEvent.EventType.INSERT);
updateBidi( e );
} finally {
writeUnlock();
}
}
}
A convenience method for storing up a property value. It is
equivalent to:
getDocumentProperties().put(key, value);
If value is null this method will
remove the property. |
public final synchronized void readLock() {
try {
while (currWriter != null) {
if (currWriter == Thread.currentThread()) {
// writer has full read access.... may try to acquire
// lock in notification
return;
}
wait();
}
numReaders += 1;
} catch (InterruptedException e) {
throw new Error("Interrupted attempt to aquire read lock");
}
}
Acquires a lock to begin reading some state from the
document. There can be multiple readers at the same time.
Writing blocks the readers until notification of the change
to the listeners has been completed. This method should
be used very carefully to avoid unintended compromise
of the document. It should always be balanced with a
readUnlock . |
public final synchronized void readUnlock() {
if (currWriter == Thread.currentThread()) {
// writer has full read access.... may try to acquire
// lock in notification
return;
}
if (numReaders < = 0) {
throw new StateInvariantError(BAD_LOCK_STATE);
}
numReaders -= 1;
notify();
}
Does a read unlock. This signals that one
of the readers is done. If there are no more readers
then writing can begin again. This should be balanced
with a readLock, and should occur in a finally statement
so that the balance is guaranteed. The following is an
example.
readLock();
try {
// do something
} finally {
readUnlock();
}
|
public void remove(int offs,
int len) throws BadLocationException {
DocumentFilter filter = getDocumentFilter();
writeLock();
try {
if (filter != null) {
filter.remove(getFilterBypass(), offs, len);
}
else {
handleRemove(offs, len);
}
} finally {
writeUnlock();
}
}
Removes some content from the document.
Removing content causes a write lock to be held while the
actual changes are taking place. Observers are notified
of the change on the thread that called this method.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
public void removeDocumentListener(DocumentListener listener) {
listenerList.remove(DocumentListener.class, listener);
}
Removes a document listener. |
public void removeUndoableEditListener(UndoableEditListener listener) {
listenerList.remove(UndoableEditListener.class, listener);
}
Removes an undo listener. |
protected void removeUpdate(DefaultDocumentEvent chng) {
}
Updates any document structure as a result of text removal. This
method is called before the text is actually removed from the Content.
This will happen within a write lock. If a subclass
of this class reimplements this method, it should delegate to the
superclass as well. |
public void render(Runnable r) {
readLock();
try {
r.run();
} finally {
readUnlock();
}
}
This allows the model to be safely rendered in the presence
of currency, if the model supports being updated asynchronously.
The given runnable will be executed in a way that allows it
to safely read the model with no changes while the runnable
is being executed. The runnable itself may not
make any mutations.
This is implemented to aquire a read lock for the duration
of the runnables execution. There may be multiple runnables
executing at the same time, and all writers will be blocked
while there are active rendering runnables. If the runnable
throws an exception, its lock will be safely released.
There is no protection against a runnable that never exits,
which will effectively leave the document locked for it's
lifetime.
If the given runnable attempts to make any mutations in
this implementation, a deadlock will occur. There is
no tracking of individual rendering threads to enable
detecting this situation, but a subclass could incur
the overhead of tracking them and throwing an error.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
public void replace(int offset,
int length,
String text,
AttributeSet attrs) throws BadLocationException {
if (length == 0 && (text == null || text.length() == 0)) {
return;
}
DocumentFilter filter = getDocumentFilter();
writeLock();
try {
if (filter != null) {
filter.replace(getFilterBypass(), offset, length, text,
attrs);
}
else {
if (length > 0) {
remove(offset, length);
}
if (text != null && text.length() > 0) {
insertString(offset, text, attrs);
}
}
} finally {
writeUnlock();
}
}
Deletes the region of text from offset to
offset + length , and replaces it with text .
It is up to the implementation as to how this is implemented, some
implementations may treat this as two distinct operations: a remove
followed by an insert, others may treat the replace as one atomic
operation. |
public void setAsynchronousLoadPriority(int p) {
Integer loadPriority = (p >= 0) ? Integer.valueOf(p) : null;
putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);
}
Sets the asynchronous loading priority. |
public void setDocumentFilter(DocumentFilter filter) {
documentFilter = filter;
}
Sets the DocumentFilter . The DocumentFilter
is passed insert and remove to conditionally
allow inserting/deleting of the text. A null value
indicates that no filtering will occur. |
public void setDocumentProperties(Dictionary<Object, Object> x) {
documentProperties = x;
}
Replaces the document properties dictionary for this document. |
void updateBidi(DefaultDocumentEvent chng) {
// Calculate the range of paragraphs affected by the change.
int firstPStart;
int lastPEnd;
if( chng.type == DocumentEvent.EventType.INSERT
|| chng.type == DocumentEvent.EventType.CHANGE )
{
int chngStart = chng.getOffset();
int chngEnd = chngStart + chng.getLength();
firstPStart = getParagraphElement(chngStart).getStartOffset();
lastPEnd = getParagraphElement(chngEnd).getEndOffset();
} else if( chng.type == DocumentEvent.EventType.REMOVE ) {
Element paragraph = getParagraphElement( chng.getOffset() );
firstPStart = paragraph.getStartOffset();
lastPEnd = paragraph.getEndOffset();
} else {
throw new Error("Internal error: unknown event type.");
}
//System.out.println("updateBidi: firstPStart = " + firstPStart + " lastPEnd = " + lastPEnd );
// Calculate the bidi levels for the affected range of paragraphs. The
// levels array will contain a bidi level for each character in the
// affected text.
byte levels[] = calculateBidiLevels( firstPStart, lastPEnd );
Vector< Element > newElements = new Vector< Element >();
// Calculate the first span of characters in the affected range with
// the same bidi level. If this level is the same as the level of the
// previous bidi element (the existing bidi element containing
// firstPStart-1), then merge in the previous element. If not, but
// the previous element overlaps the affected range, truncate the
// previous element at firstPStart.
int firstSpanStart = firstPStart;
int removeFromIndex = 0;
if( firstSpanStart > 0 ) {
int prevElemIndex = bidiRoot.getElementIndex(firstPStart-1);
removeFromIndex = prevElemIndex;
Element prevElem = bidiRoot.getElement(prevElemIndex);
int prevLevel=StyleConstants.getBidiLevel(prevElem.getAttributes());
//System.out.println("createbidiElements: prevElem= " + prevElem + " prevLevel= " + prevLevel + "level[0] = " + levels[0]);
if( prevLevel==levels[0] ) {
firstSpanStart = prevElem.getStartOffset();
} else if( prevElem.getEndOffset() > firstPStart ) {
newElements.addElement(new BidiElement(bidiRoot,
prevElem.getStartOffset(),
firstPStart, prevLevel));
} else {
removeFromIndex++;
}
}
int firstSpanEnd = 0;
while((firstSpanEnd< levels.length) && (levels[firstSpanEnd]==levels[0]))
firstSpanEnd++;
// Calculate the last span of characters in the affected range with
// the same bidi level. If this level is the same as the level of the
// next bidi element (the existing bidi element containing lastPEnd),
// then merge in the next element. If not, but the next element
// overlaps the affected range, adjust the next element to start at
// lastPEnd.
int lastSpanEnd = lastPEnd;
Element newNextElem = null;
int removeToIndex = bidiRoot.getElementCount() - 1;
if( lastSpanEnd < = getLength() ) {
int nextElemIndex = bidiRoot.getElementIndex( lastPEnd );
removeToIndex = nextElemIndex;
Element nextElem = bidiRoot.getElement( nextElemIndex );
int nextLevel = StyleConstants.getBidiLevel(nextElem.getAttributes());
if( nextLevel == levels[levels.length-1] ) {
lastSpanEnd = nextElem.getEndOffset();
} else if( nextElem.getStartOffset() < lastPEnd ) {
newNextElem = new BidiElement(bidiRoot, lastPEnd,
nextElem.getEndOffset(),
nextLevel);
} else {
removeToIndex--;
}
}
int lastSpanStart = levels.length;
while( (lastSpanStart >firstSpanEnd)
&& (levels[lastSpanStart-1]==levels[levels.length-1]) )
lastSpanStart--;
// If the first and last spans are contiguous and have the same level,
// merge them and create a single new element for the entire span.
// Otherwise, create elements for the first and last spans as well as
// any spans in between.
if((firstSpanEnd==lastSpanStart)&&(levels[0]==levels[levels.length-1])){
newElements.addElement(new BidiElement(bidiRoot, firstSpanStart,
lastSpanEnd, levels[0]));
} else {
// Create an element for the first span.
newElements.addElement(new BidiElement(bidiRoot, firstSpanStart,
firstSpanEnd+firstPStart,
levels[0]));
// Create elements for the spans in between the first and last
for( int i=firstSpanEnd; i< lastSpanStart; ) {
//System.out.println("executed line 872");
int j;
for( j=i; (j< levels.length) && (levels[j] == levels[i]); j++ );
newElements.addElement(new BidiElement(bidiRoot, firstPStart+i,
firstPStart+j,
(int)levels[i]));
i=j;
}
// Create an element for the last span.
newElements.addElement(new BidiElement(bidiRoot,
lastSpanStart+firstPStart,
lastSpanEnd,
levels[levels.length-1]));
}
if( newNextElem != null )
newElements.addElement( newNextElem );
// Calculate the set of existing bidi elements which must be
// removed.
int removedElemCount = 0;
if( bidiRoot.getElementCount() > 0 ) {
removedElemCount = removeToIndex - removeFromIndex + 1;
}
Element[] removedElems = new Element[removedElemCount];
for( int i=0; i< removedElemCount; i++ ) {
removedElems[i] = bidiRoot.getElement(removeFromIndex+i);
}
Element[] addedElems = new Element[ newElements.size() ];
newElements.copyInto( addedElems );
// Update the change record.
ElementEdit ee = new ElementEdit( bidiRoot, removeFromIndex,
removedElems, addedElems );
chng.addEdit( ee );
// Update the bidi element structure.
bidiRoot.replace( removeFromIndex, removedElems.length, addedElems );
}
Update the bidi element structure as a result of the given change
to the document. The given change will be updated to reflect the
changes made to the bidi structure.
This method assumes that every offset in the model is contained in
exactly one paragraph. This method also assumes that it is called
after the change is made to the default element structure. |
protected final synchronized void writeLock() {
try {
while ((numReaders > 0) || (currWriter != null)) {
if (Thread.currentThread() == currWriter) {
if (notifyingListeners) {
// Assuming one doesn't do something wrong in a
// subclass this should only happen if a
// DocumentListener tries to mutate the document.
throw new IllegalStateException(
"Attempt to mutate in notification");
}
numWriters++;
return;
}
wait();
}
currWriter = Thread.currentThread();
numWriters = 1;
} catch (InterruptedException e) {
throw new Error("Interrupted attempt to aquire write lock");
}
}
Acquires a lock to begin mutating the document this lock
protects. There can be no writing, notification of changes, or
reading going on in order to gain the lock. Additionally a thread is
allowed to gain more than one writeLock ,
as long as it doesn't attempt to gain additional writeLock s
from within document notification. Attempting to gain a
writeLock from within a DocumentListener notification will
result in an IllegalStateException . The ability
to obtain more than one writeLock per thread allows
subclasses to gain a writeLock, perform a number of operations, then
release the lock.
Calls to writeLock
must be balanced with calls to writeUnlock , else the
Document will be left in a locked state so that no
reading or writing can be done. |
protected final synchronized void writeUnlock() {
if (--numWriters < = 0) {
numWriters = 0;
currWriter = null;
notifyAll();
}
}
Releases a write lock previously obtained via writeLock .
After decrementing the lock count if there are no oustanding locks
this will allow a new writer, or readers. |