FileDocCategorySizeDatePackage
AsyncBoxView.javaAPI DocJava SE 6 API42264Tue Jun 10 00:26:56 BST 2008javax.swing.text

AsyncBoxView

public class AsyncBoxView extends View
A box that does layout asynchronously. This is useful to keep the GUI event thread moving by not doing any layout on it. The layout is done on a granularity of operations on the child views. After each child view is accessed for some part of layout (a potentially time consuming operation) the remaining tasks can be abandoned or a new higher priority task (i.e. to service a synchronous request or a visible area) can be taken on.

While the child view is being accessed a read lock is aquired on the associated document so that the model is stable while being accessed.

author
Timothy Prinzing
version
1.18 04/07/06
since
1.3

Fields Summary
int
axis
The major axis against which the children are tiled.
List
stats
The children and their layout statistics.
float
majorSpan
Current span along the major axis. This is also the value returned by getMinimumSize, getPreferredSize, and getMaximumSize along the major axis.
boolean
estimatedMajorSpan
Is the span along the major axis estimated?
float
minorSpan
Current span along the minor axis. This is what layout was done against (i.e. things are flexible in this direction).
protected ChildLocator
locator
Object that manages the offsets of the children. All locking for management of child locations is on this object.
float
topInset
float
bottomInset
float
leftInset
float
rightInset
ChildState
minRequest
ChildState
prefRequest
boolean
majorChanged
boolean
minorChanged
Runnable
flushTask
ChildState
changing
Child that is actively changing size. This often causes a preferenceChanged, so this is a cache to possibly speed up the marking the state. It also helps flag an opportunity to avoid adding to flush task to the layout queue.
Constructors Summary
public AsyncBoxView(Element elem, int axis)
Construct a box view that does asynchronous layout.

param
elem the element of the model to represent
param
axis the axis to tile along. This can be either X_AXIS or Y_AXIS.

	super(elem);
	stats = new ArrayList();
	this.axis = axis;
	locator = new ChildLocator();
	flushTask = new FlushTask();
	minorSpan = Short.MAX_VALUE;
	estimatedMajorSpan = false;
    
Methods Summary
protected javax.swing.text.AsyncBoxView$ChildStatecreateChildState(javax.swing.text.View v)
New ChildState records are created through this method to allow subclasses the extend the ChildState records to do/hold more

	return new ChildState(v);
    
protected voidflushRequirementChanges()
Publish the changes in preferences upward to the parent view. This is normally called by the layout thread.

	AbstractDocument doc = (AbstractDocument) getDocument();
	try {
	    doc.readLock();

	    View parent = null;
	    boolean horizontal = false;
	    boolean vertical = false;

	    synchronized(this) {
		// perform tasks that iterate over the children while
		// preventing the collection from changing.
		synchronized(stats) {
		    int n = getViewCount();
		    if ((n > 0) && (minorChanged || estimatedMajorSpan)) {
			LayoutQueue q = getLayoutQueue();
			ChildState min = getChildState(0);
			ChildState pref = getChildState(0);
			float span = 0f;
			for (int i = 1; i < n; i++) {
			    ChildState cs = getChildState(i);
			    if (minorChanged) {
				if (cs.min > min.min) {
				    min = cs;
				}
				if (cs.pref > pref.pref) {
				    pref = cs;
				}
			    }
			    if (estimatedMajorSpan) {
				span += cs.getMajorSpan();
			    }
			}

			if (minorChanged) {
			    minRequest = min;
			    prefRequest = pref;
			}
			if (estimatedMajorSpan) {
			    majorSpan = span;
			    estimatedMajorSpan = false;
			    majorChanged = true;
			}
		    }
		}

		// message preferenceChanged 
		if (majorChanged || minorChanged) {
		    parent = getParent();
		    if (parent != null) {
			if (axis == X_AXIS) {
			    horizontal = majorChanged;
			    vertical = minorChanged;
			} else {
			    vertical = majorChanged;
			    horizontal = minorChanged;
			}
		    }
		    majorChanged = false;
		    minorChanged = false;
		}
	    }

	    // propagate a preferenceChanged, using the
	    // layout thread.
	    if (parent != null) {
		parent.preferenceChanged(this, horizontal, vertical);
			
		// probably want to change this to be more exact.
		Component c = getContainer();
		if (c != null) {
		    c.repaint();
		}
	    }
	} finally {
	    doc.readUnlock();
	}
    
public floatgetBottomInset()
Get the bottom part of the margin around the view.

	return bottomInset;
    
public java.awt.ShapegetChildAllocation(int index, java.awt.Shape a)
Fetches the allocation for the given child view. This enables finding out where various views are located, without assuming the views store their location. This returns null since the default is to not have any child views.

param
index the index of the child, >= 0 && < getViewCount()
param
a the allocation to this view.
return
the allocation to the child

	Shape ca = locator.getChildAllocation(index, a);
	return ca;
    
protected javax.swing.text.AsyncBoxView$ChildStategetChildState(int index)
Fetch the object representing the layout state of of the child at the given index.

param
index the child index. This should be a value >= 0 and < getViewCount().

	synchronized(stats) {
	    if ((index >= 0) && (index < stats.size())) {
		return (ChildState) stats.get(index);
	    }
	    return null;
	}
    
protected booleangetEstimatedMajorSpan()
Is the major span currently estimated?

since
1.4

	return estimatedMajorSpan;
    
protected floatgetInsetSpan(int axis)
Fetch the span along an axis that is taken up by the insets.

param
axis the axis to determine the total insets along, either X_AXIS or Y_AXIS.
since
1.4

	float margin = (axis == X_AXIS) ? 
	    getLeftInset() + getRightInset() : getTopInset() + getBottomInset();
	return margin;
    
protected javax.swing.text.LayoutQueuegetLayoutQueue()
Fetch the queue to use for layout.

	return LayoutQueue.getDefaultQueue();
    
public floatgetLeftInset()
Get the left part of the margin around the view.

	return leftInset;
    
public intgetMajorAxis()
Fetch the major axis (the axis the children are tiled along). This will have a value of either X_AXIS or Y_AXIS.

	return axis;
    
public floatgetMaximumSpan(int axis)
Determines the maximum span for this view along an axis.

param
axis may be either View.X_AXIS or View.Y_AXIS
return
the span the view would like to be rendered into >= 0. Typically the view is told to render into the span that is returned, although there is no guarantee. The parent may choose to resize or break the view.
exception
IllegalArgumentException for an invalid axis type

	if (axis == this.axis) {
	    return getPreferredSpan(axis);
	}
	return Integer.MAX_VALUE;
    
public floatgetMinimumSpan(int axis)
Determines the minimum span for this view along an axis.

param
axis may be either View.X_AXIS or View.Y_AXIS
return
the span the view would like to be rendered into >= 0. Typically the view is told to render into the span that is returned, although there is no guarantee. The parent may choose to resize or break the view.
exception
IllegalArgumentException for an invalid axis type

	if (axis == this.axis) {
	    return getPreferredSpan(axis);
	}
	if (minRequest != null) {
	    View child = minRequest.getChildView();
	    return child.getMinimumSpan(axis);
	}

	// nothing is known about the children yet
	if (axis == X_AXIS) {
	    return getLeftInset() + getRightInset() + 5;
	} else {
	    return getTopInset() + getBottomInset() + 5;
	}
    
public intgetMinorAxis()
Fetch the minor axis (the axis orthoginal to the tiled axis). This will have a value of either X_AXIS or Y_AXIS.

	return (axis == X_AXIS) ? Y_AXIS : X_AXIS;
    
public intgetNextVisualPositionFrom(int pos, javax.swing.text.Position$Bias b, java.awt.Shape a, int direction, javax.swing.text.Position$Bias[] biasRet)
Provides a way to determine the next visually represented model location that one might place a caret. Some views may not be visible, they might not be in the same order found in the model, or they just might not allow access to some of the locations in the model.

param
pos the position to convert >= 0
param
a the allocated region to render into
param
direction the direction from the current position that can be thought of as the arrow keys typically found on a keyboard; this may be one of the following:
    SwingConstants.WEST SwingConstants.EAST SwingConstants.NORTH SwingConstants.SOUTH
param
biasRet an array contain the bias that was checked
return
the location within the model that best represents the next location visual position
exception
BadLocationException
exception
IllegalArgumentException if direction is invalid

        return Utilities.getNextVisualPositionFrom(
                            this, pos, b, a, direction, biasRet);
    
public floatgetPreferredSpan(int axis)
Determines the preferred span for this view along an axis.

param
axis may be either View.X_AXIS or View.Y_AXIS
return
the span the view would like to be rendered into >= 0. Typically the view is told to render into the span that is returned, although there is no guarantee. The parent may choose to resize or break the view.
exception
IllegalArgumentException for an invalid axis type

	float margin = getInsetSpan(axis);
	if (axis == this.axis) {
	    return majorSpan + margin;
	}
	if (prefRequest != null) {
	    View child = prefRequest.getChildView();
	    return child.getPreferredSpan(axis) + margin;
	}

	// nothing is known about the children yet
	return margin + 30;
    
public floatgetRightInset()
Get the right part of the margin around the view.

	return rightInset;
    
floatgetSpanOnAxis(int axis)
Retrieves the size of the view along an axis.

param
axis may be either View.X_AXIS or View.Y_AXIS
return
the current span of the view along the given axis, >= 0

	if (axis == getMajorAxis()) {
	    return majorSpan;
	}
	return minorSpan;
    
public floatgetTopInset()
Get the top part of the margin around the view.

	return topInset;
    
public javax.swing.text.ViewgetView(int n)
Gets the nth child view. Since there are no children by default, this returns null.

param
n the number of the view to get, >= 0 && < getViewCount()
return
the view

	ChildState cs = getChildState(n);
	if (cs != null) {
	    return cs.getChildView();
	}
	return null;
    
public intgetViewCount()
Returns the number of views in this view. Since the default is to not be a composite view this returns 0.

return
the number of views >= 0
see
View#getViewCount

	synchronized(stats) {
	    return stats.size();
	}
    
public intgetViewIndex(int pos, javax.swing.text.Position$Bias b)
Returns the child view index representing the given position in the model. By default a view has no children so this is implemented to return -1 to indicate there is no valid child index for any position.

param
pos the position >= 0
return
index of the view representing the given position, or -1 if no view represents that position
since
1.3

        return getViewIndexAtPosition(pos, b);
    
protected synchronized intgetViewIndexAtPosition(int pos, javax.swing.text.Position$Bias b)
Fetches the child view index representing the given position in the model. This is implemented to fetch the view in the case where there is a child view for each child element.

param
pos the position >= 0
return
index of the view representing the given position, or -1 if no view represents that position

	boolean isBackward = (b == Position.Bias.Backward);
	pos = (isBackward) ? Math.max(0, pos - 1) : pos;
	Element elem = getElement();
	return elem.getElementIndex(pos);
    
protected voidloadChildren(javax.swing.text.ViewFactory f)
Loads all of the children to initialize the view. This is called by the setParent method. Subclasses can reimplement this to initialize their child views in a different manner. The default implementation creates a child view for each child element.

Normally a write-lock is held on the Document while the children are being changed, which keeps the rendering and layout threads safe. The exception to this is when the view is initialized to represent an existing element (via this method), so it is synchronized to exclude preferenceChanged while we are initializing.

param
f the view factory
see
#setParent

	Element e = getElement();
	int n = e.getElementCount();
	if (n > 0) {
	    View[] added = new View[n];
	    for (int i = 0; i < n; i++) {
		added[i] = f.create(e.getElement(i));
	    }
	    replace(0, 0, added);
	}
    
protected synchronized voidmajorRequirementChange(javax.swing.text.AsyncBoxView$ChildState cs, float delta)
Requirements changed along the major axis. This is called by the thread doing layout for the given ChildState object when it has completed fetching the child views new preferences. Typically this would be the layout thread, but might be the event thread if it is trying to update something immediately (such as to perform a model/view translation).

This is implemented to mark the major axis as having changed so that a future check to see if the requirements need to be published to the parent view will consider the major axis. If the span along the major axis is not estimated, it is updated by the given delta to reflect the incremental change. The delta is ignored if the major span is estimated.

	if (estimatedMajorSpan == false) {
	    majorSpan += delta;
	}
	majorChanged = true;
    
protected synchronized voidminorRequirementChange(javax.swing.text.AsyncBoxView$ChildState cs)
Requirements changed along the minor axis. This is called by the thread doing layout for the given ChildState object when it has completed fetching the child views new preferences. Typically this would be the layout thread, but might be the GUI thread if it is trying to update something immediately (such as to perform a model/view translation).

	minorChanged = true;
    
public java.awt.ShapemodelToView(int pos, java.awt.Shape a, javax.swing.text.Position$Bias b)
Provides a mapping from the document model coordinate space to the coordinate space of the view mapped to it.

param
pos the position to convert >= 0
param
a the allocated region to render into
param
b the bias toward the previous character or the next character represented by the offset, in case the position is a boundary of two views.
return
the bounding box of the given position is returned
exception
BadLocationException if the given position does not represent a valid location in the associated document
exception
IllegalArgumentException for an invalid bias argument
see
View#viewToModel

	int index = getViewIndex(pos, b);
	Shape ca = locator.getChildAllocation(index, a);

	// forward to the child view, and make sure we don't
	// interact with the layout thread by synchronizing
	// on the child state.
	ChildState cs = getChildState(index);
	synchronized (cs) {
	    View cv = cs.getChildView();
	    Shape v = cv.modelToView(pos, ca, b);
	    return v;
	}
    
public voidpaint(java.awt.Graphics g, java.awt.Shape alloc)
Render the view using the given allocation and rendering surface.

This is implemented to determine whether or not the desired region to be rendered (i.e. the unclipped area) is up to date or not. If up-to-date the children are rendered. If not up-to-date, a task to build the desired area is placed on the layout queue as a high priority task. This keeps by event thread moving by rendering if ready, and postponing until a later time if not ready (since paint requests can be rescheduled).

param
g the rendering surface to use
param
alloc the allocated region to render into
see
View#paint

	synchronized (locator) {
	    locator.setAllocation(alloc);
	    locator.paintChildren(g);
	}
    
public synchronized voidpreferenceChanged(javax.swing.text.View child, boolean width, boolean height)
Child views can call this on the parent to indicate that the preference has changed and should be reconsidered for layout. This is reimplemented to queue new work on the layout thread. This method gets messaged from multiple threads via the children.

param
child the child view
param
width true if the width preference has changed
param
height true if the height preference has changed
see
javax.swing.JComponent#revalidate

	if (child == null) {
	    getParent().preferenceChanged(this, width, height);
	} else {
	    if (changing != null) {
		View cv = changing.getChildView();
		if (cv == child) {
		    // size was being changed on the child, no need to
		    // queue work for it.
		    changing.preferenceChanged(width, height);
		    return;
		}
	    }
	    int index = getViewIndex(child.getStartOffset(), 
				     Position.Bias.Forward);
	    ChildState cs = getChildState(index);
	    cs.preferenceChanged(width, height);
	    LayoutQueue q = getLayoutQueue();
	    q.addTask(cs);
	    q.addTask(flushTask);
	}
    
public voidreplace(int offset, int length, javax.swing.text.View[] views)
Calls the superclass to update the child views, and updates the status records for the children. This is expected to be called while a write lock is held on the model so that interaction with the layout thread will not happen (i.e. the layout thread acquires a read lock before doing anything).

param
offset the starting offset into the child views >= 0
param
length the number of existing views to replace >= 0
param
views the child views to insert

	synchronized(stats) {
	    // remove the replaced state records
	    for (int i = 0; i < length; i++) {
                ChildState cs = (ChildState)stats.remove(offset);
                float csSpan = cs.getMajorSpan();

                cs.getChildView().setParent(null);
                if (csSpan != 0) {
                    majorRequirementChange(cs, -csSpan);
                }
	    }

	    // insert the state records for the new children
	    LayoutQueue q = getLayoutQueue();
	    if (views != null) {
		for (int i = 0; i < views.length; i++) {
		    ChildState s = createChildState(views[i]);
		    stats.add(offset + i, s);
		    q.addTask(s);
		}
	    }

	    // notify that the size changed
	    q.addTask(flushTask);
	}
    
public voidsetBottomInset(float i)
Set the bottom part of the margin around the view.

param
i the value of the inset

	bottomInset = i;
    
protected voidsetEstimatedMajorSpan(boolean isEstimated)
Set the estimatedMajorSpan property that determines if the major span should be treated as being estimated. If this property is true, the value of setSize along the major axis will change the requirements along the major axis and incremental changes will be ignored until all of the children have been updated (which will cause the property to automatically be set to false). If the property is false the value of the majorSpan will be considered to be accurate and incremental changes will be added into the total as they are calculated.

since
1.4

	estimatedMajorSpan = isEstimated;
    
public voidsetLeftInset(float i)
Set the left part of the margin around the view.

param
i the value of the inset

	leftInset = i;
    
public voidsetParent(javax.swing.text.View parent)
Sets the parent of the view. This is reimplemented to provide the superclass behavior as well as calling the loadChildren method if this view does not already have children. The children should not be loaded in the constructor because the act of setting the parent may cause them to try to search up the hierarchy (to get the hosting Container for example). If this view has children (the view is being moved from one place in the view hierarchy to another), the loadChildren method will not be called.

param
parent the parent of the view, null if none

	super.setParent(parent);
	if ((parent != null) && (getViewCount() == 0)) {
	    ViewFactory f = getViewFactory();
	    loadChildren(f);
	}
    
public voidsetRightInset(float i)
Set the right part of the margin around the view.

param
i the value of the inset

	rightInset = i;
    
public voidsetSize(float width, float height)
Sets the size of the view. This should cause layout of the view if the view caches any layout information.

Since the major axis is updated asynchronously and should be the sum of the tiled children the call is ignored for the major axis. Since the minor axis is flexible, work is queued to resize the children if the minor span changes.

param
width the width >= 0
param
height the height >= 0

	setSpanOnAxis(X_AXIS, width);
	setSpanOnAxis(Y_AXIS, height);
    
voidsetSpanOnAxis(int axis, float span)
Sets the size of the view along an axis. Since the major axis is updated asynchronously and should be the sum of the tiled children the call is ignored for the major axis. Since the minor axis is flexible, work is queued to resize the children if the minor span changes.

param
axis may be either View.X_AXIS or View.Y_AXIS
param
span the span to layout to >= 0

	float margin = getInsetSpan(axis);
	if (axis == getMinorAxis()) {
	    float targetSpan = span - margin;
	    if (targetSpan != minorSpan) {
		minorSpan = targetSpan;

		// mark all of the ChildState instances as needing to
		// resize the child, and queue up work to fix them.
		int n = getViewCount();
		if (n != 0) {
		    LayoutQueue q = getLayoutQueue();
		    for (int i = 0; i < n; i++) {
			ChildState cs = getChildState(i);
			cs.childSizeValid = false;
			q.addTask(cs);
		    }
		    q.addTask(flushTask);
		}
	    }
	} else {
	    // along the major axis the value is ignored 
	    // unless the estimatedMajorSpan property is
	    // true.
	    if (estimatedMajorSpan) {
		majorSpan = span - margin;
	    }
	}
    
public voidsetTopInset(float i)
Set the top part of the margin around the view.

param
i the value of the inset

	topInset = i;
    
protected voidupdateLayout(javax.swing.event.DocumentEvent$ElementChange ec, javax.swing.event.DocumentEvent e, java.awt.Shape a)
Update the layout in response to receiving notification of change from the model. This is implemented to note the change on the ChildLocator so that offsets of the children will be correctly computed.

param
ec changes to the element this view is responsible for (may be null if there were no changes).
param
e the change information from the associated document
param
a the current allocation of the view
see
#insertUpdate
see
#removeUpdate
see
#changedUpdate

	if (ec != null) {
	    // the newly inserted children don't have a valid
	    // offset so the child locator needs to be messaged
	    // that the child prior to the new children has 
	    // changed size.
	    int index = Math.max(ec.getIndex() - 1, 0);
	    ChildState cs = getChildState(index);
	    locator.childChanged(cs);
	}
    
public intviewToModel(float x, float y, java.awt.Shape a, javax.swing.text.Position$Bias[] biasReturn)
Provides a mapping from the view coordinate space to the logical coordinate space of the model. The biasReturn argument will be filled in to indicate that the point given is closer to the next character in the model or the previous character in the model.

This is expected to be called by the GUI thread, holding a read-lock on the associated model. It is implemented to locate the child view and determine it's allocation with a lock on the ChildLocator object, and to call viewToModel on the child view with a lock on the ChildState object to avoid interaction with the layout thread.

param
x the X coordinate >= 0
param
y the Y coordinate >= 0
param
a the allocated region to render into
return
the location within the model that best represents the given point in the view >= 0. The biasReturn argument will be filled in to indicate that the point given is closer to the next character in the model or the previous character in the model.

	int pos;    // return position
        int index;  // child index to forward to
	Shape ca;   // child allocation
	
	// locate the child view and it's allocation so that
	// we can forward to it.  Make sure the layout thread
	// doesn't change anything by trying to flush changes
	// to the parent while the GUI thread is trying to
	// find the child and it's allocation.
	synchronized (locator) {
	    index = locator.getViewIndexAtPoint(x, y, a);
	    ca = locator.getChildAllocation(index, a);
	}

	// forward to the child view, and make sure we don't
	// interact with the layout thread by synchronizing
	// on the child state.
	ChildState cs = getChildState(index);
	synchronized (cs) {
	    View v = cs.getChildView();
	    pos = v.viewToModel(x, y, ca, biasReturn);
	}
	return pos;