3118 lines · 108.4 KB · Java
RawDownload
     * 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.     *     * @param chng a description of the change     */    protected void postRemoveUpdate(DefaultDocumentEvent chng) {        if( getProperty(I18NProperty).equals( Boolean.TRUE ) )            updateBidi( chng );    }      /**     * 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.     */    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 );    }      /**     * Calculate the levels array for a range of paragraphs.     */    private byte[] calculateBidiLevels( int firstPStart, int lastPEnd ) {         byte levels[] = new byte[ lastPEnd - firstPStart ];        int  levelsEnd = 0;        Boolean defaultDirection = null;        Object d = getProperty(TextAttribute.RUN_DIRECTION);        if (d instanceof Boolean) {            defaultDirection = (Boolean) d;        }         // For each paragraph in the given range of paragraphs, get its        // levels array and add it to the levels array for the entire span.        for(int o=firstPStart; o<lastPEnd; ) {            Element p = getParagraphElement( o );            int pStart = p.getStartOffset();            int pEnd = p.getEndOffset();             // default run direction for the paragraph.  This will be            // null if there is no direction override specified (i.e.            // the direction will be determined from the content).            Boolean direction = defaultDirection;            d = p.getAttributes().getAttribute(TextAttribute.RUN_DIRECTION);            if (d instanceof Boolean) {                direction = (Boolean) d;            }             //System.out.println("updateBidi: paragraph start = " + pStart + " paragraph end = " + pEnd);             // Create a Bidi over this paragraph then get the level            // array.            Segment seg = SegmentCache.getSharedSegment();            try {                getText(pStart, pEnd-pStart, seg);            } catch (BadLocationException e ) {                throw new Error("Internal error: " + e.toString());            }            // REMIND(bcb) we should really be using a Segment here.            Bidi bidiAnalyzer;            int bidiflag = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;            if (direction != null) {                if (TextAttribute.RUN_DIRECTION_LTR.equals(direction)) {                    bidiflag = Bidi.DIRECTION_LEFT_TO_RIGHT;                } else {                    bidiflag = Bidi.DIRECTION_RIGHT_TO_LEFT;                }            }            bidiAnalyzer = new Bidi(seg.array, seg.offset, null, 0, seg.count,                    bidiflag);            BidiUtils.getLevels(bidiAnalyzer, levels, levelsEnd);            levelsEnd += bidiAnalyzer.getLength();             o =  p.getEndOffset();            SegmentCache.releaseSharedSegment(seg);        }         // REMIND(bcb) remove this code when debugging is done.        if( levelsEnd != levels.length )            throw new Error("levelsEnd assertion failed.");         return levels;    }     /**     * Gives a diagnostic dump.     *     * @param out the output stream     */    public void dump(PrintStream out) {        Element root = getDefaultRootElement();        if (root instanceof AbstractElement) {            ((AbstractElement)root).dump(out, 0);        }        bidiRoot.dump(out,0);    }     /**     * Gets the content for the document.     *     * @return the content     */    protected final Content getContent() {        return data;    }     /**     * 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.     *     * @param parent the parent element     * @param a the attributes for the element     * @param p0 the beginning of the range >= 0     * @param p1 the end of the range >= p0     * @return the new element     */    protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {        return new LeafElement(parent, a, p0, p1);    }     /**     * Creates a document branch element, that can contain other elements.     *     * @param parent the parent element     * @param a the attributes     * @return the element     */    protected Element createBranchElement(Element parent, AttributeSet a) {        return new BranchElement(parent, a);    }     // --- Document locking ----------------------------------     /**     * 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.     *     * @return the thread actively modifying the document     *  or <code>null</code> if there are no modifications in progress     */    protected synchronized final Thread getCurrentWriter() {        return currWriter;    }     /**     * 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 <code>writeLock</code>,     * as long as it doesn't attempt to gain additional <code>writeLock</code>s     * from within document notification.  Attempting to gain a     * <code>writeLock</code> from within a DocumentListener notification will     * result in an <code>IllegalStateException</code>.  The ability     * to obtain more than one <code>writeLock</code> per thread allows     * subclasses to gain a writeLock, perform a number of operations, then     * release the lock.     * <p>     * Calls to <code>writeLock</code>     * must be balanced with calls to <code>writeUnlock</code>, else the     * <code>Document</code> will be left in a locked state so that no     * reading or writing can be done.     *     * @exception IllegalStateException thrown on illegal lock     *  attempt.  If the document is implemented properly, this can     *  only happen if a document listener attempts to mutate the     *  document.  This situation violates the bean event model     *  where order of delivery is not guaranteed and all listeners     *  should be notified before further mutations are allowed.     */    protected synchronized final 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");        }    }     /**     * Releases a write lock previously obtained via <code>writeLock</code>.     * After decrementing the lock count if there are no oustanding locks     * this will allow a new writer, or readers.     *     * @see #writeLock     */    protected synchronized final void writeUnlock() {        if (--numWriters <= 0) {            numWriters = 0;            currWriter = null;            notifyAll();        }    }     /**     * 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     * <code>readUnlock</code>.     *     * @see #readUnlock     */    public synchronized final 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");        }    }     /**     * 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.     * <pre><code>     * &nbsp;   readLock();     * &nbsp;   try {     * &nbsp;       // do something     * &nbsp;   } finally {     * &nbsp;       readUnlock();     * &nbsp;   }     * </code></pre>     *     * @see #readLock     */    public synchronized final 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();    }     // --- serialization ---------------------------------------------     private void readObject(ObjectInputStream s)      throws ClassNotFoundException, IOException    {        s.defaultReadObject();        listenerList = new EventListenerList();         // Restore bidi structure        //REMIND(bcb) This creates an initial bidi element to account for        //the \n that exists by default in the content.        bidiRoot = new BidiRootElement();        try {            writeLock();            Element[] p = new Element[1];            p[0] = new BidiElement( bidiRoot, 0, 1, 0 );            bidiRoot.replace(0,0,p);        } finally {            writeUnlock();        }        // At this point bidi root is only partially correct. To fully        // restore it we need access to getDefaultRootElement. But, this        // is created by the subclass and at this point will be null. We        // thus use registerValidation.        s.registerValidation(new ObjectInputValidation() {            public void validateObject() {                try {                    writeLock();                    DefaultDocumentEvent e = new DefaultDocumentEvent                                   (0, getLength(),                                    DocumentEvent.EventType.INSERT);                    updateBidi( e );                }                finally {                    writeUnlock();                }            }        }, 0);    }     // ----- member variables ------------------------------------------     private transient int numReaders;    private transient Thread currWriter;    /**     * The number of writers, all obtained from <code>currWriter</code>.     */    private transient int numWriters;    /**     * True will notifying listeners.     */    private transient boolean notifyingListeners;     private static Boolean defaultI18NProperty;     /**     * Storage for document-wide properties.     */    private Dictionary<Object,Object> documentProperties = null;     /**     * The event listener list for the document.     */    protected EventListenerList listenerList = new EventListenerList();     /**     * Where the text is actually stored, and a set of marks     * that track change as the document is edited are managed.     */    private Content data;     /**     * Factory for the attributes.  This is the strategy for     * attribute compression and control of the lifetime of     * a set of attributes as a collection.  This may be shared

Lines 1001–1500 of 3118. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t