alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.text;

import java.util.*;
import java.io.*;

import org.openide.util.RequestProcessor;

/** Class that holds line information about one document.
* Defines operations that can be executed on the objects, the implementation
* can change when we find that it is too slow.
*
* @author Jaroslav Tulach
*/
final class LineStruct extends Object {
    /** max number of lines to work with */
    private static final int MAX = Integer.MAX_VALUE / 2;

    /** Holding the original and current number of lines.
    */
    private static final class Info extends Object {
        /** constants for distintion of the type of info */
        public static final int AREA_ORIGINAL = 0;
        public static final int AREA_INSERT = 1;
        public static final int AREA_REMOVE = -1;

        /** original number */
        public int original;
        /** current number */
        public int current;

        public Info (int o, int c) {
            original = o;
            current = c;
        }

        /** Finds the type.
        */
        public int type () {
            if (current == original) return AREA_ORIGINAL;
            if (current == 0) return AREA_REMOVE;
            if (original == 0) return AREA_INSERT;
            throw new IllegalStateException("Original: " + original + " current: " + current); // NOI18N
        }

        /** Performs insert on this Info object.
        * @param pos position to insert to
        * @param count how much objects to insert
        * @param it iterator that just returned this object
        * @return how much lines to insert after this object
        */
        public int insert (int pos, int count, ListIterator it) {
            switch (type ()) {
            case AREA_INSERT:
                // insert area, add to it all
                current += count;
                return 0;
            case AREA_ORIGINAL:
                if (pos == current) {
                    // if the insert position is at the end,
                    // then let all the characters be added by next
                    // item
                    return count;
                }
                if (pos == 0) {
                    // prepend the insert area before the current
                    // Info in the chain
                    Info ni = new Info (original, original);
                    original = 0;
                    current = count;
                    it.add (ni);
                    // everything has been prepended
                    return 0;
                }
                // we have to devided the interval to two parts
                // and insert insert block between them
                Info ni = new Info (original - pos, original - pos);
                // the area from 0 to pos
                original = current = pos;
                // insert the insert area
                it.add (new Info (0, count));
                // the rest of the area
                it.add (ni);
                return 0;
            case AREA_REMOVE:
                // supposing that pos == 0
                if (pos != 0) {
                    throw new IllegalStateException ("Pos: " + pos); // NOI18N
                }

                // check the previous Info if it cannot be merged
                Info prev = (Info)it.previous (); // current item

                // prev.type () == AREA_REMOVE => prev is used
                // only if the it.hasPrevious is true and prev
                // is changed again

                if (it.hasPrevious ()) {
                    prev = (Info)it.previous (); // previous
                    it.next (); // previous
                }

                it.next (); // current

                // inserted lines can be put instead of the orignal
                // ones
                if (count < original) {
                    if (prev.type () == AREA_ORIGINAL) {
                        prev.original += count;
                        prev.current += count;

                        // modify this remove object
                        original -= count;
                    } else {
                        ni = new Info (original - count, 0);
                        // turn this to regular part
                        original = current = count;
                        // insert the new delete part
                        it.add (ni);
                    }
                    // everything processed
                    return 0;
                } else {
                    if (prev.type () == AREA_ORIGINAL) {
                        prev.current += original;
                        prev.original += original;
                        it.remove ();
                        return count - original;
                    } else {
                        // turn whole delete part to regular one
                        current = original;
                        // the rest of characters to proceed
                        return count - current;
                    }
                }
            default:
                throw new IllegalStateException ("Type: " + type ()); // NOI18N
            }
        }

        /** A method that handles the delete operation.
        * @param pos position in the Info block where delete started
        * @param info 
        *   info.original the amount of lines to be deleted
        *   info.current the amount of lines that should be later marked as deleted
        * @param it the iterator that previously returned this instance
        * @return 
        *   info.original the amount of lines to be yet deleted
        *   info.current the amount of lines that needs to be later marked as deleted
        *     this will be put before the 
        */
        public Info delete (int pos, Info info, ListIterator it) {
            switch (type ()) {
            case AREA_ORIGINAL:
                if (pos != 0) {
                    // specials
                    int size = current - pos;
                    current = original = pos;
                    if (size >= info.original) {
                        // delete is whole only in this block
                        Info ni = new Info (size, size);
                        it.add (ni);
                        info.current += info.original;
                        info.original = 0;
                        return info;
                    } else {
                        // something is resting after this block
                        info.original -= size;
                        info.current += size;
                        return info;
                    }
                } else {
                    // deleting from first position
                    if (current >= info.original) {
                        // something is resting from me (at the end)

                        // number of lines to mark as deleted
                        info.current += info.original;

                        // number of lines in this block is decreased
                        current -= info.original;
                        original = current;


                        // number of lines to be yet deleted
                        info.original = 0;

                        return info;
                    } else {
                        // I am completelly deleted
                        it.remove ();

                        // number of lines to mark as deleted
                        info.current += current;
                        info.original -= current;
                        return info;
                    }
                }
            case AREA_INSERT:
                if (pos != 0) {
                    // specials
                    int size = current - pos;
                    if (size >= info.original) {
                        // delete is whole only in this block
                        current -= info.original;

                        info.original = 0;

                        return info;
                    } else {
                        // something is resting after this block
                        current = pos;

                        info.original -= size;
                        return info;
                    }
                } else {
                    // deleting from first position
                    if (current >= info.original) {
                        // something is resting from me (at the end)

                        // number of lines in this block is decreased
                        current -= info.original;

                        // number of lines to be yet deleted
                        info.original = 0;

                        it.remove ();

                        return info;
                    } else {
                        // I am completelly deleted
                        it.remove ();

                        // how much lines to be deleted yet
                        info.original -= current;
                        return info;
                    }
                }
            case AREA_REMOVE:
                // only derease the number of lines that needs to be deleted
                // because this area can absorb some
                original += info.current;
                info.current = 0;
                return info;
            default:
                throw new IllegalStateException("Type: " + type ()); // NOI18N
            }
        }
    }

    /** processor for all requests */
    private static final RequestProcessor PROCESSOR =
        new RequestProcessor("LineStruct Processor"); // NOI18N

    /** list of Info objects that represents the whole document */
    private LinkedList list;

    /** Constructor.
    */
    public LineStruct () {
        list = new LinkedList ();
        list.add (new Info (MAX, MAX));
    }

    /** Converts original numbering to the new one.
    * @param line the line number in the original
    * @return line number in the new numbering
    */
    public int convert (int line, final boolean currentToOriginal) {
        // class to compute in the request processor thread
        class Compute extends Object implements Runnable {
            public int result;

            public Compute (int i) {
                result = i;
            }

            public void run () {
                if (currentToOriginal) {
                    result = originalToCurrentImpl (result);
                } else {
                    result = currentToOriginalImpl (result);
                }
            }
        }

        Compute c = new Compute (line);

        // post the computation and wait till it is finished
        PROCESSOR.post (c).waitFinished ();

        // return result
        return c.result;
    }

    /** Inserts line(s) at given position.
    * @param line the line number in current numbering
    * @param count number of lines inserted
    */
    public void insertLines (final int line, final int count) {
        PROCESSOR.post (new Runnable () {
                            public void run () {
                                insertLinesImpl (line, count);
                            }
                        });
    }

    /** Method that deletes some lines in the current state of
    * the document.
    *
    * @param line the line number in current numbering
    * @param 
    */
    public void deleteLines (final int line, final int count) {
        PROCESSOR.post (new Runnable () {
                            public void run () {
                                deleteLinesImpl (line, count);
                            }
                        });
    }

    /** Converts original numbering to the new one.
    * @param line the line number in the original
    * @return line number in the new numbering
    */
    private int originalToCurrentImpl (int line) {
        Iterator it = list.iterator ();
        int cur = 0;
        for (;;) {
            Info i = (Info)it.next ();
            if (i.original > line) {
                // ok we found the segment that contained this line
                return line > i.current ? cur + i.current : cur + line;
            }
            cur += i.current;
            line -= i.original;
        }
    }
    
    /** Converts the current numbering to original
    * @param line the line number now
    * @return line number in the original numbering
    */
    private int currentToOriginalImpl (int line) {
        Iterator it = list.iterator ();
        int cur = 0;
        for (;;) {
            Info i = (Info)it.next ();
            if (i.current > line) {
                // ok we found the segment that contained this line
                return line > i.original ? cur + i.original : cur + line;
            }
            cur += i.original;
            line -= i.current;
        }
    }

    /** Inserts line(s) at given position.
    * @param line the line number in current numbering
    * @param count number of lines inserted
    */
    private void insertLinesImpl (int line, int count) {
        ListIterator it = list.listIterator ();
        for (;;) {
            Info i = (Info)it.next ();
            if (i.current >= line) {
                for (;;) {
                    count = i.insert (line, count, it);
                    if (count == 0) {
                        return;
                    }
                    i = (Info)it.next ();
                    line = 0;
                }
            }
            line -= i.current;
        }
    }

    /** Method that deletes some lines in the current state of
    * the document.
    *
    * @param line the line number in current numbering
    * @param 
    */
    private void deleteLinesImpl (int line, int count) {
        ListIterator it = list.listIterator ();
        for (;;) {
            Info i = (Info)it.next ();
            if (i.current >= line) {
                // information to hold both the number of lines to delete (original)
                // and the number of lines to mark as delete at the end (current)
                Info stat = new Info (count, 0);
                for (;;) {
                    stat = i.delete (line, stat, it);

                    if (stat.original == 0) {
                        break;
                    }

                    i = (Info)it.next ();
                    line = 0;
                }

                // insert the amount of lines to mark deleted before current position
                if (stat.current > 0 && it.hasPrevious()) {
                    Info prev = (Info)it.previous ();
                    boolean hasPrev = it.hasPrevious ();

                    if (hasPrev) {
                        prev = (Info)it.previous ();
                    }

                    if (prev.current == 0) {
                        prev.original += stat.current;
                    } else {
                        if (hasPrev) {
                            it.next ();
                        }
                        it.add (new Info (stat.current, 0));
                    }
                }
                return;
            }
            line -= i.current;
        }
    }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.