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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor;

import java.awt.Rectangle;
import java.awt.Frame;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.Writer;
import javax.swing.SwingUtilities;
import javax.swing.Action;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;
import javax.swing.text.BadLocationException;
import javax.swing.text.AttributeSet;
import javax.swing.text.EditorKit;
import javax.swing.text.Document;
import javax.swing.text.TextAction;
import javax.swing.text.Caret;
import javax.swing.plaf.TextUI;
import javax.swing.text.Element;
import javax.swing.text.View;
import org.openide.ErrorManager;

/**
* Various useful editor functions. Some of the methods have
* the same names and signatures like in javax.swing.Utilities but
* there is also many other useful methods.
* All the methods are static so there's no reason to instantiate Utilities.
*
* All the methods working with the document rely on that it is locked against
* modification so they don't acquire document read/write lock by themselves
* to guarantee the full thread safety of the execution.
* It's the user's task to lock the document appropriately
* before using methods described here.
*
* Most of the methods require org.netbeans.editor.BaseDocument instance
* not just the javax.swing.text.Document.
* The reason for that is to mark that the methods work on BaseDocument
* instances only, not on generic documents. To convert the Document
* to BaseDocument the simple conversion (BaseDocument)target.getDocument()
* can be done or the method getDocument(target) can be called.
* There are also other conversion methods like getEditorUI(), getKit()
* or getKitClass().
*
* @author Miloslav Metelka
* @version 0.10
*/

public class Utilities {

    private static final String WRONG_POSITION_LOCALE = "wrong_position"; // NOI18N

    /** Switch the case to capital letters. Used in changeCase() */
    public static final int CASE_UPPER = 0;

    /** Switch the case to small letters. Used in changeCase() */
    public static final int CASE_LOWER = 1;

    /** Switch the case to reverse. Used in changeCase() */
    public static final int CASE_SWITCH = 2;
    
    /** Fake TextAction for getting the info of the focused component */
    private static TextAction focusedComponentAction;    
    
    private static final boolean isLoggable = ErrorManager.getDefault().isLoggable(ErrorManager.INFORMATIONAL);

    private Utilities() {
        // instantiation has no sense
    }

    /** Get the starting position of the row.
    * @param c text component to operate on
    * @param offset position in document where to start searching
    * @return position of the start of the row or -1 for invalid position
    */
    public static int getRowStart(JTextComponent c, int offset)
    throws BadLocationException {
        return javax.swing.text.Utilities.getRowStart(c, offset);
        //return getRowStart((BaseDocument)c.getDocument(), offset, 0);
    }

    /** Get the starting position of the row.
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @return position of the start of the row or -1 for invalid position
    */
    public static int getRowStart(BaseDocument doc, int offset)
    throws BadLocationException {
        return getRowStart(doc, offset, 0);
    }

    /** Get the starting position of the row while providing relative count
    * of row how the given position should be shifted. This is the most
    * efficient way how to move by lines in the document based on some
    * position. There is no similair getRowEnd() method that would have
    * shifting parameter.
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @param lineShift shift the given offset forward/back relatively
    *  by some amount of lines
    * @return position of the start of the row or -1 for invalid position
    */
    public static int getRowStart(BaseDocument doc, int offset, int lineShift)
    throws BadLocationException {
        
        checkOffsetValid(doc, offset);

        if (lineShift != 0) {
            Element lineRoot = doc.getParagraphElement(0).getParentElement();
            int line = lineRoot.getElementIndex(offset);
            line += lineShift;
            if (line < 0 || line >= lineRoot.getElementCount()) {
                return -1; // invalid line shift
            }
            return lineRoot.getElement(line).getStartOffset();

        } else { // no shift
            return doc.getParagraphElement(offset).getStartOffset();
        }
    }

    /** Get the first non-white character on the line.
    * The document.isWhitespace() is used to test whether the particular
    * character is white space or not.
    * @param doc document to operate on
    * @param offset position in document anywhere on the line
    * @return position of the first non-white char on the line or -1
    *   if there's no non-white character on that line.
    */
    public static int getRowFirstNonWhite(BaseDocument doc, int offset)
    throws BadLocationException {
        
        checkOffsetValid(doc, offset);

        Element lineElement = doc.getParagraphElement(offset);
        return getFirstNonWhiteFwd(doc,
            lineElement.getStartOffset(),
            lineElement.getEndOffset() - 1
        );
    }

    /** Get the last non-white character on the line.
    * The document.isWhitespace() is used to test whether the particular
    * character is white space or not.
    * @param doc document to operate on
    * @param offset position in document anywhere on the line
    * @return position of the last non-white char on the line or -1
    *   if there's no non-white character on that line.
    */
    public static int getRowLastNonWhite(BaseDocument doc, int offset)
    throws BadLocationException {
        
        checkOffsetValid(doc, offset);

        Element lineElement = doc.getParagraphElement(offset);
        return getFirstNonWhiteBwd(doc,
            lineElement.getEndOffset() - 1,
            lineElement.getStartOffset()
        );
    }

    /** Get indentation on the current line. If this line is white then
    * return -1.
    * @param doc document to operate on
    * @param offset position in document anywhere on the line
    * @return indentation or -1 if the line is white
    */
    public static int getRowIndent(BaseDocument doc, int offset)
    throws BadLocationException {
        offset = getRowFirstNonWhite(doc, offset);
        if (offset == -1) {
            return -1;
        }
        return doc.getVisColFromPos(offset);
    }

    /** Get indentation on the current line. If this line is white then
    * go either up or down an return indentation of the first non-white row.
    * The getRowFirstNonWhite() is used to find the indentation
    * on particular line.
    * @param doc document to operate on
    * @param offset position in document anywhere on the line
    * @param downDir if this flag is set to true then if the row is white
    *   then the indentation of the next first non-white row is returned. If it's
    *   false then the indentation of the previous first non-white row is returned.
    * @return indentation or -1 if there's no non-white line in the specified direction
    */
    public static int getRowIndent(BaseDocument doc, int offset, boolean downDir)
    throws BadLocationException {
        int p = getRowFirstNonWhite(doc, offset);
        if (p == -1) {
            p = getFirstNonWhiteRow(doc, offset, downDir);
            if (p == -1) {
                return -1; // non-white line not found
            }
            p = getRowFirstNonWhite(doc, p);
            if (p == -1) {
                return -1; // non-white line not found
            }
        }
        return doc.getVisColFromPos(p);
    }

    /** Get the end position of the row right before the new-line character.
    * @param c text component to operate on
    * @param offset position in document where to start searching
    * @param relLine shift offset forward/back by some amount of lines
    * @return position of the end of the row or -1 for invalid position
    */
    public static int getRowEnd(JTextComponent c, int offset)
    throws BadLocationException {
        return javax.swing.text.Utilities.getRowEnd(c, offset);
        //return getRowEnd((BaseDocument)c.getDocument(), offset);
    }

    public static int getRowEnd(BaseDocument doc, int offset)
    throws BadLocationException {
        checkOffsetValid(doc, offset);

        return doc.getParagraphElement(offset).getEndOffset() - 1;
    }

    /** Get the position that is one line above and visually at some
    * x-coordinate value.
    * @param doc document to operate on
    * @param offset position in document from which the current line is determined
    * @param x float x-coordinate value
    * @return position of the character that is at the one line above at
    *   the required x-coordinate value
    */
    public static int getPositionAbove(JTextComponent c, int offset, int x)
    throws BadLocationException {
        int offs = offset;
	int lastOffs = getRowStart(c, offs) - 1;
	if (lastOffs < 0) {
	    return -1;
	}
	int bestSpan = Short.MAX_VALUE;
	int y = 0;
        int width = 0;
	Rectangle r = null;
	if (lastOffs >= 0) {
	    r = c.modelToView(lastOffs);
	    y = r.y;
            width = r.width;
	}
        
        Rectangle lastR = r;
        int span = -1;
	while ((r != null) && (y == r.y) && r.x >= x) {
	    span = Math.abs(r.x - x);
	    if (span < bestSpan) {
		offs = lastOffs;
		bestSpan = span;
	    }
	    lastOffs -= 1;
            lastR = r;            
	    r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
	}

        if (r!=null && y == r.y && span>width){
            offs = c.viewToModel(new java.awt.Point(r.x, r.y));
        } else {
            offs = c.viewToModel(new java.awt.Point(lastR.x, lastR.y));
        }
        
        return offs;

//        return javax.swing.text.Utilities.getPositionAbove(c, offset, x);
    }

    /** Get the position that is one line above and visually at some
    * x-coordinate value.
    * @param c text component to operate on
    * @param offset position in document from which the current line is determined
    * @param x float x-coordinate value
    * @return position of the character that is at the one line above at
    *   the required x-coordinate value
    */
    public static int getPositionBelow(JTextComponent c, int offset, int x)
    throws BadLocationException {
        return javax.swing.text.Utilities.getPositionBelow(c, offset, x);
        /*
        BaseDocument doc = (BaseDocument)c.getDocument();
        BaseTextUI ui = (BaseTextUI)c.getUI();
        offset = ui.viewToModel(c, x, ui.getYFromPos(offset) + ui.getEditorUI().getLineHeight());
        return offset;
        */
    }

    /** Get start of the current word. If there are no more words till
    * the begining of the document, this method returns -1.
    * @param c text component to operate on
    * @param offset position in document from which the current line is determined
    */
    public static int getWordStart(JTextComponent c, int offset)
    throws BadLocationException {
        return getWordStart((BaseDocument)c.getDocument(), offset);
    }

    public static int getWordStart(BaseDocument doc, int offset)
    throws BadLocationException {
        return doc.find(new FinderFactory.PreviousWordBwdFinder(doc, false, true),
                        offset, 0);
    }

    public static int getWordEnd(JTextComponent c, int offset)
    throws BadLocationException {
        return getWordEnd((BaseDocument)c.getDocument(), offset);
    }

    public static int getWordEnd(BaseDocument doc, int offset)
    throws BadLocationException {
        int ret = doc.find(new FinderFactory.NextWordFwdFinder(doc, false, true),
                        offset, -1);
        return (ret > 0) ? ret : doc.getLength();
    }

    public static int getNextWord(JTextComponent c, int offset)
    throws BadLocationException {
        int nextWordOffset = getNextWord((BaseDocument)c.getDocument(), offset);
        int nextVisualPosition = c.getUI().getNextVisualPositionFrom(c,
                              nextWordOffset, null, javax.swing.SwingConstants.EAST, null);
        return (nextVisualPosition - 1 == nextWordOffset) ?  nextWordOffset : nextVisualPosition - 1;
    }

    public static int getNextWord(BaseDocument doc, int offset)
    throws BadLocationException {
        Finder nextWordFinder = (Finder)doc.getProperty(SettingsNames.NEXT_WORD_FINDER);
        offset = doc.find(nextWordFinder, offset, -1);
        if (offset < 0) {
            offset = doc.getLength();
        }
        return offset;
    }

    public static int getPreviousWord(JTextComponent c, int offset)
    throws BadLocationException {
        int prevWordOffset = getPreviousWord((BaseDocument)c.getDocument(), offset);
        int nextVisualPosition = c.getUI().getNextVisualPositionFrom(c,
                              prevWordOffset, null, javax.swing.SwingConstants.WEST, null);
        return (nextVisualPosition + 1 == prevWordOffset) ?  prevWordOffset : nextVisualPosition + 1;
    }

    public static int getPreviousWord(BaseDocument doc, int offset)
    throws BadLocationException {
        Finder prevWordFinder = (Finder)doc.getProperty(SettingsNames.PREVIOUS_WORD_FINDER);
        offset = doc.find(prevWordFinder, offset, 0);
        if (offset < 0) {
            offset = 0;
        }
        return offset;
    }

    /** Get first white character in document in forward direction
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @return position of the first white character or -1
    */
    public static int getFirstWhiteFwd(BaseDocument doc, int offset)
    throws BadLocationException {
        return getFirstWhiteFwd(doc, offset, -1);
    }

    /** Get first white character in document in forward direction
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @param limitPos position in document (greater or equal than offset) where
    *   the search will stop reporting unsuccessful search by returning -1
    * @return position of the first non-white character or -1
    */
    public static int getFirstWhiteFwd(BaseDocument doc, int offset, int limitPos)
    throws BadLocationException {
        return doc.find(new FinderFactory.WhiteFwdFinder(doc), offset, limitPos);
    }

    /** Get first non-white character in document in forward direction
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @return position of the first non-white character or -1
    */
    public static int getFirstNonWhiteFwd(BaseDocument doc, int offset)
    throws BadLocationException {
        return getFirstNonWhiteFwd(doc, offset, -1);
    }

    /** Get first non-white character in document in forward direction
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @param limitPos position in document (greater or equal than offset) where
    *   the search will stop reporting unsuccessful search by returning -1
    * @return position of the first non-white character or -1
    */
    public static int getFirstNonWhiteFwd(BaseDocument doc, int offset, int limitPos)
    throws BadLocationException {
        return doc.find(new FinderFactory.NonWhiteFwdFinder(doc), offset, limitPos);
    }

    /** Get first white character in document in backward direction.
    * The character right before the character at position offset will
    * be searched as first.
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @return position of the first white character or -1
    */
    public static int getFirstWhiteBwd(BaseDocument doc, int offset)
    throws BadLocationException {
        return getFirstWhiteBwd(doc, offset, 0);
    }

    /** Get first white character in document in backward direction.
    * The character right before the character at position offset will
    * be searched as first.
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @param limitPos position in document (lower or equal than offset) where
    *   the search will stop reporting unsuccessful search by returning -1
    * @return position of the first white character or -1
    */
    public static int getFirstWhiteBwd(BaseDocument doc, int offset, int limitPos)
    throws BadLocationException {
        return doc.find(new FinderFactory.WhiteBwdFinder(doc), offset, limitPos);
    }

    /** Get first non-white character in document in backward direction.
    * The character right before the character at position offset will
    * be searched as first.
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @return position of the first non-white character or -1
    */
    public static int getFirstNonWhiteBwd(BaseDocument doc, int offset)
    throws BadLocationException {
        return getFirstNonWhiteBwd(doc, offset, 0);
    }

    /** Get first non-white character in document in backward direction.
    * The character right before the character at position offset will
    * be searched as first.
    * @param doc document to operate on
    * @param offset position in document where to start searching
    * @param limitPos position in document (lower or equal than offset) where
    *   the search will stop reporting unsuccessful search by returning -1
    * @return position of the first non-white character or -1
    */
    public static int getFirstNonWhiteBwd(BaseDocument doc, int offset, int limitPos)
    throws BadLocationException {
        return doc.find(new FinderFactory.NonWhiteBwdFinder(doc), offset, limitPos);
    }

    /** Return line offset (line number - 1) for some position in the document
    * @param doc document to operate on
    * @param offset position in document where to start searching
    */
    public static int getLineOffset(BaseDocument doc, int offset)
    throws BadLocationException {
        
        checkOffsetValid(offset, doc.getLength() + 1);

        Element lineRoot = doc.getParagraphElement(0).getParentElement();
        return lineRoot.getElementIndex(offset);
    }

    /** Return start offset of the line
    * @param lineIndex line index starting from 0
    * @return start position of the line or -1 if lineIndex was invalid
    */
    public static int getRowStartFromLineOffset(BaseDocument doc, int lineIndex) {
        Element lineRoot = doc.getParagraphElement(0).getParentElement();
        if (lineIndex < 0 || lineIndex >= lineRoot.getElementCount()) {
            return -1; // invalid line number

        } else {
            return lineRoot.getElement(lineIndex).getStartOffset();
        }
    }

    /** Return visual column (with expanded tabs) on the line.
    * @param doc document to operate on
    * @param offset position in document for which the visual column should be found
    * @return visual column on the line determined by position
    */
    public static int getVisualColumn(BaseDocument doc, int offset)
    throws BadLocationException {
        
        int docLen = doc.getLength();
        if (offset == docLen + 1) { // at ending extra '\n' => make docLen to proceed without BLE
            offset = docLen;
        }

        return doc.getVisColFromPos(offset);
    }

    /** Get the identifier around the given position or null if there's no identifier
    * @see getIdentifierBlock()
    */
    public static String getIdentifier(BaseDocument doc, int offset)
    throws BadLocationException {
        int[] blk = getIdentifierBlock(doc, offset);
        return (blk != null) ? doc.getText(blk[0], blk[1] - blk[0]) : null;
    }


    /** Get the identifier around the given position or null if there's no identifier
     * around the given position. The identifier is not verified against SyntaxSupport.isIdentifier().
     * @param c JTextComponent to work on
     * @param offset position in document - usually the caret.getDot()
     * @return the block (starting and ending position) enclosing the identifier
     * or null if no identifier was found
     */
    public static int[] getIdentifierBlock(JTextComponent c, int offset)
    throws BadLocationException {
        String id = null;
        int[] ret = null;
        Document doc = c.getDocument();
        int idStart = javax.swing.text.Utilities.getWordStart(c, offset);
        if (idStart >= 0) {
            int idEnd = javax.swing.text.Utilities.getWordEnd(c, idStart);
            if (idEnd >= 0) {
                id = doc.getText(idStart, idEnd - idStart);
                ret = new int[] { idStart, idEnd };
                String trim = id.trim();
                if (trim.length() == 0 || (trim.length() == 1 && !Character.isJavaIdentifierPart(trim.charAt(0)))) {
                    int prevWordStart = javax.swing.text.Utilities.getPreviousWord(c, offset);
                    if (offset == javax.swing.text.Utilities.getWordEnd(c,prevWordStart )){
                        ret = new int[] { prevWordStart, offset };
                    }
                } else if ((id != null) && (id.length() != 0)  && (id.indexOf(".") != -1)){ //NOI18N
                    int index = offset - idStart;
                    int begin = id.substring(0, index).lastIndexOf("."); //NOI18N
                    begin = (begin == -1) ? 0 : begin + 1; //first index after the dot, if exists
                    int end = id.indexOf(".", index); //NOI18N
                    end = (end == -1) ? id.length() : end;
                    ret = new int[] { idStart+begin, idStart+end };
                }
            }
        }
        return ret;
    }
    
    
    
    /** Get the identifier around the given position or null if there's no identifier
    * around the given position. The identifier must be
    * accepted by SyntaxSupport.isIdnetifier() otherwise null is returned.
    * @param doc document to work on
    * @param offset position in document - usually the caret.getDot()
    * @return the block (starting and ending position) enclosing the identifier
    *   or null if no identifier was found
    */
    public static int[] getIdentifierBlock(BaseDocument doc, int offset)
    throws BadLocationException {
        int[] ret = null;
        int idStart = getWordStart(doc, offset);
        if (idStart >= 0) {
            int idEnd = getWordEnd(doc, idStart);
            if (idEnd >= 0) {
                String id = doc.getText(idStart, idEnd - idStart);
                if (doc.getSyntaxSupport().isIdentifier(id)) {
                    ret = new int[] { idStart, idEnd };
                } else { // not identifier by syntax support
                    id = getWord(doc, offset); // try right at offset
                    if (doc.getSyntaxSupport().isIdentifier(id)) {
                        ret = new int[] { offset, offset + id.length() };
                    }
                }
            }
        }
        return ret;
    }

    
    /** Get the word around the given position .
     * @param c component to work with
     * @param offset position in document - usually the caret.getDot()
     * @return the word.
     */
    public static String getWord(JTextComponent c, int offset)
    throws BadLocationException {
        int[] blk = getIdentifierBlock(c, offset);
        Document doc = c.getDocument();
        return (blk != null) ? doc.getText(blk[0], blk[1] - blk[0]) : null;
    }
    
    
    /** Get the selection if there's any or get the identifier around
    * the position if there's no selection.
    * @param c component to work with
    * @param offset position in document - usually the caret.getDot()
    * @return the block (starting and ending position) enclosing the identifier
    *   or null if no identifier was found
    */
    public static int[] getSelectionOrIdentifierBlock(JTextComponent c, int offset)
    throws BadLocationException {
        Document doc = c.getDocument();
        Caret caret = c.getCaret();
        int[] ret;
        if (caret.isSelectionVisible()) {
            ret = new int[] { c.getSelectionStart(), c.getSelectionEnd() }; 
        } else if (doc instanceof BaseDocument){
            ret = getIdentifierBlock((BaseDocument)doc, caret.getDot());
        } else {
            ret = getIdentifierBlock(c, offset);
        }
        return ret;
    }

    /** Get the selection or identifier at the current caret position
     * @see getSelectionOrIdentifierBlock(JTextComponent, int)
     */
    public static int[] getSelectionOrIdentifierBlock(JTextComponent c) {
        try {
            return getSelectionOrIdentifierBlock(c, c.getCaret().getDot());
        } catch (BadLocationException e) {
            return null;
        }
    }

    /** Get the identifier before the given position (ending at given offset)
    * or null if there's no identifier
    */
    public static String getIdentifierBefore(BaseDocument doc, int offset)
    throws BadLocationException {
        int wordStart = getWordStart(doc, offset);
        if (wordStart != -1) {
            String word = new String(doc.getChars(wordStart,
                                                  offset - wordStart), 0, offset - wordStart);
            if (doc.getSyntaxSupport().isIdentifier(word)) {
                return word;
            }
        }
        return null;
    }

    /** Get the selection if there's any or get the identifier around
    * the position if there's no selection.
    */
    public static String getSelectionOrIdentifier(JTextComponent c, int offset)
    throws BadLocationException {
        Document doc = c.getDocument();
        Caret caret = c.getCaret();
        String ret;
        if (caret.isSelectionVisible()) {
            ret = c.getSelectedText();
	    if (ret != null) return ret;
        } 
	if (doc instanceof BaseDocument){
	    ret = getIdentifier((BaseDocument) doc, caret.getDot());
        } else {
	    ret = getWord(c, offset);
	}
        return ret;
    }

    /** Get the selection or identifier at the current caret position */
    public static String getSelectionOrIdentifier(JTextComponent c) {
        try {
            return getSelectionOrIdentifier(c, c.getCaret().getDot());
        } catch (BadLocationException e) {
            return null;
        }
    }

    /** Get the word at given position.
    */
    public static String getWord(BaseDocument doc, int offset)
    throws BadLocationException {
        int wordEnd = getWordEnd(doc, offset);
        if (wordEnd != -1) {
            return new String(doc.getChars(offset, wordEnd - offset), 0,
                              wordEnd - offset);
        }
        return null;
    }


    /** Change the case for specified part of document
    * @param doc document to operate on
    * @param offset position in document determines the changed area begining
    * @param len number of chars to change
    * @param type either CASE_CAPITAL, CASE_SMALL or CASE_SWITCH
    */
    public static boolean changeCase(BaseDocument doc, int offset, int len, int type)
    throws BadLocationException {
        char[] orig = doc.getChars(offset, len);
        char[] changed = (char[])orig.clone();
        for (int i = 0; i < orig.length; i++) {
            switch (type) {
            case CASE_UPPER:
                changed[i] = Character.toUpperCase(orig[i]);
                break;
            case CASE_LOWER:
                changed[i] = Character.toLowerCase(orig[i]);
                break;
            case CASE_SWITCH:
                if (Character.isUpperCase(orig[i])) {
                    changed[i] = Character.toLowerCase(orig[i]);
                } else if (Character.isLowerCase(orig[i])) {
                    changed[i] = Character.toUpperCase(orig[i]);
                }
                break;
            }
        }
        // check chars for difference and possibly change document
        for (int i = 0; i < orig.length; i++) {
            if (orig[i] != changed[i]) {
                doc.atomicLock();
                try {
                    doc.remove(offset, orig.length);
                    doc.insertString(offset, new String(changed), null);
                } finally {
                    doc.atomicUnlock();
                }
                return true; // changed
            }
        }
        return false;
    }

    /** Tests whether the line contains no characters except the ending new-line.
    * @param doc document to operate on
    * @param offset position anywhere on the tested line
    * @return whether the line is empty or not
    */
    public static boolean isRowEmpty(BaseDocument doc, int offset)
    throws BadLocationException {
        Element lineElement = doc.getParagraphElement(offset);
        return (lineElement.getStartOffset() + 1 == lineElement.getEndOffset());
    }

    public static int getFirstNonEmptyRow(BaseDocument doc, int offset, boolean downDir)
    throws BadLocationException {
        while (offset != -1 && isRowEmpty(doc, offset)) {
            offset = getRowStart(doc, offset, downDir ? +1 : -1);
        }
        return offset;
    }

    /** Tests whether the line contains only whitespace characters.
    * @param doc document to operate on
    * @param offset position anywhere on the tested line
    * @return whether the line is empty or not
    */
    public static boolean isRowWhite(BaseDocument doc, int offset)
    throws BadLocationException {
        Element lineElement = doc.getParagraphElement(offset);
        offset = doc.find(new FinderFactory.NonWhiteFwdFinder(doc),
              lineElement.getStartOffset(), lineElement.getEndOffset() - 1);
        return (offset == -1);
    }

    public static int getFirstNonWhiteRow(BaseDocument doc, int offset, boolean downDir)
    throws BadLocationException {
        if (isRowWhite(doc, offset)) {
            if (downDir) { // search down for non-white line
                offset = getFirstNonWhiteFwd(doc, offset);
            } else { // search up for non-white line
                offset = getFirstNonWhiteBwd(doc, offset);
            }
        }
        return offset;
    }

    /** Reformat a block of code.
    * @param doc document to work with
    * @param startOffset offset at which the formatting starts
    * @param endOffset offset at which the formatting ends
    * @return length of the reformatted code
    */
    public static int reformat(BaseDocument doc, int startOffset, int endOffset)
    throws BadLocationException {
        return doc.getFormatter().reformat(doc, startOffset, endOffset);
    }

    /** Reformat the line around the given position. */
    public static void reformatLine(BaseDocument doc, int pos)
    throws BadLocationException {
        int lineStart = getRowStart(doc, pos);
        int lineEnd = getRowEnd(doc, pos);
        reformat(doc, lineStart, lineEnd);
    }

    /** Count of rows between these two positions */
    public static int getRowCount(BaseDocument doc, int startPos, int endPos)
    throws BadLocationException {
        if (startPos > endPos) {
            return 0;
        }
        Element lineRoot = doc.getParagraphElement(0).getParentElement();
        return lineRoot.getElementIndex(endPos) - lineRoot.getElementIndex(startPos) + 1;
    }

    /** Get the total count of lines in the document */
    public static int getRowCount(BaseDocument doc) {
        return doc.getParagraphElement(0).getParentElement().getElementCount();
    }

    /** @deprecated
     * @see Formatter.insertTabString()
     */
    public static String getTabInsertString(BaseDocument doc, int offset)
    throws BadLocationException {
        int col = getVisualColumn(doc, offset);
        Formatter f = doc.getFormatter();
        boolean expandTabs = f.expandTabs();
        if (expandTabs) {
            int spacesPerTab = f.getSpacesPerTab();
            int len = (col + spacesPerTab) / spacesPerTab * spacesPerTab - col;
            return new String(Analyzer.getSpacesBuffer(len), 0, len);
        } else { // insert pure tab
            return "\t"; // NOI18N
        }
    }

    /** Get the visual column corresponding to the position after pressing
     * the TAB key.
     * @param doc document to work with
     * @param offset position at which the TAB was pressed
     */
    public static int getNextTabColumn(BaseDocument doc, int offset)
    throws BadLocationException {
        int col = getVisualColumn(doc, offset);
        int tabSize = doc.getFormatter().getSpacesPerTab();
        return (col + tabSize) / tabSize * tabSize;
    }

    public static void setStatusText(JTextComponent c, String text) {
        StatusBar sb = getEditorUI(c).getStatusBar();
        if (sb != null) {
            sb.setText(StatusBar.CELL_MAIN, text);
        }
    }

    public static void setStatusText(JTextComponent c, String text,
                                     Coloring extraColoring) {
        StatusBar sb = getEditorUI(c).getStatusBar();
        if (sb != null) {
            sb.setText(StatusBar.CELL_MAIN, text, extraColoring);
        }
    }

    public static void setStatusBoldText(JTextComponent c, String text) {
        StatusBar sb = getEditorUI(c).getStatusBar();
        if (sb != null) {
            sb.setBoldText(StatusBar.CELL_MAIN, text);
        }
    }

    public static String getStatusText(JTextComponent c) {
        StatusBar sb = getEditorUI(c).getStatusBar();
        return (sb != null) ? sb.getText(StatusBar.CELL_MAIN) : null;
    }

    public static void clearStatusText(JTextComponent c) {
        setStatusText(c, ""); // NOI18N
    }

    public static void insertMark(BaseDocument doc, Mark mark, int offset)
    throws BadLocationException, InvalidMarkException {
        mark.insert(doc, offset);
    }

    public static void moveMark(BaseDocument doc, Mark mark, int newOffset)
    throws BadLocationException, InvalidMarkException {
        mark.move(doc, newOffset);
    }

    public static void returnFocus() {
         JTextComponent c = getLastActiveComponent();
         if (c != null) {
             requestFocus(c);
         }
    }

    public static void requestFocus(JTextComponent c) {
        if (c != null) {
            if (!ImplementationProvider.getDefault().activateComponent(c)) {
                Frame f = EditorUI.getParentFrame(c);
                if (f != null) {
                    f.requestFocus();
                }
                c.requestFocus();
            }
        }
    }

    public static void runInEventDispatchThread(Runnable r) {
        if (SwingUtilities.isEventDispatchThread()) {
            r.run();
        } else {
            SwingUtilities.invokeLater(r);
        }
    }

    public static String debugPosition(BaseDocument doc, int offset) {
        String ret;

        if (offset >= 0) {
            try {
                int line = getLineOffset(doc, offset) + 1;
                int col = getVisualColumn(doc, offset) + 1;
                ret = String.valueOf(line) + ":" + String.valueOf(col); // NOI18N
            } catch (BadLocationException e) {
                ret = LocaleSupport.getString( WRONG_POSITION_LOCALE )
                      + ' ' + offset + " > " + doc.getLength(); // NOI18N
            }
        } else {
            ret = String.valueOf(offset);
        }

        return ret;
    }
    
    public static String offsetToLineColumnString(BaseDocument doc, int offset) {
        return String.valueOf(offset) + "[" + debugPosition(doc, offset) + "]"; // NOI18N
    }

    /** Display the identity of the document together with the title property
     * and stream-description property.
     */
    public static String debugDocument(Document doc) {
        return "<" + System.identityHashCode(doc) // NOI18N
            + ", title='" + doc.getProperty(Document.TitleProperty)
            + "', stream='" + doc.getProperty(Document.StreamDescriptionProperty)
            + ", " + doc.toString() + ">"; // NOI18N
    }

    public static void performAction(Action a, ActionEvent evt, JTextComponent target) {
        if (a instanceof BaseAction) {
            ((BaseAction)a).actionPerformed(evt, target);
        } else {
            a.actionPerformed(evt);
        }
    }

    /** Returns last activated component. If the component was closed, 
     *  then previous component is returned */
    public static JTextComponent getLastActiveComponent() {
        return Registry.getMostActiveComponent();
    }
    
    /**
     * Fetches the text component that currently has focus. It delegates to 
     * TextAction.getFocusedComponent().
     * @return the component
     */
    public static JTextComponent getFocusedComponent() {
        /** Fake action for getting the focused component */
        class FocusedComponentAction extends TextAction {
            
            FocusedComponentAction() {
                super("focused-component"); // NOI18N
            }
            
            /** adding this method because of protected final getFocusedComponent */
            JTextComponent getFocusedComponent2() {
                return getFocusedComponent();
            }
            
            public void actionPerformed(ActionEvent evt){}
        };
        
        if (focusedComponentAction == null) {
            focusedComponentAction = new FocusedComponentAction();
        }
        
        return ((FocusedComponentAction)focusedComponentAction).getFocusedComponent2();
    }

    /** Helper method to obtain instance of EditorUI (extended UI)
     * from the existing JTextComponent.
     * It doesn't require any document locking.
     * @param target JTextComponent for which the extended UI should be obtained
     * @return extended ui instance or null if the component.getUI()
     *   does not return BaseTextUI instance.
     */
    public static EditorUI getEditorUI(JTextComponent target) {
        TextUI ui = target.getUI();
        return (ui instanceof BaseTextUI) 
            ? ((BaseTextUI)ui).getEditorUI()
            : null;
    }

    /** Helper method to obtain instance of editor kit from existing JTextComponent.
    * If the kit of the component is not an instance
    * of the org.netbeans.editor.BaseKit the method returns null.
    * The method doesn't require any document locking.
    * @param target JTextComponent for which the editor kit should be obtained
    * @return BaseKit instance or null
    */
    public static BaseKit getKit(JTextComponent target) {
        if (target == null) return null; // #19574
        EditorKit ekit = target.getUI().getEditorKit(target);
        return (ekit instanceof BaseKit) ? (BaseKit)ekit : null;
    }

    /** Helper method to obtain editor kit class from existing JTextComponent.
    * This method is useful for example when dealing with Settings.
    * The method doesn't require any document locking.
    * @param target JTextComponent for which the editor kit should be obtained
    * @return editor kit class
    */
    public static Class getKitClass(JTextComponent target) {
        return (target != null) ? target.getUI().getEditorKit(target).getClass() : null;
    }

    /** Helper method to obtain instance of BaseDocument from JTextComponent.
    * If the document of the component is not an instance
    * of the org.netbeans.editor.BaseDocument the method returns null.
    * The method doesn't require any document locking.
    * @param target JTextComponent for which the document should be obtained
    * @return BaseDocument instance or null
    */
    public static BaseDocument getDocument(JTextComponent target) {
        Document doc = target.getDocument();
        return (doc instanceof BaseDocument) ? (BaseDocument)doc : null;
    }

    /** Get the syntax-support class that belongs to the document of the given
    * component. Besides using directly this method, the SyntaxSupport
    * can be obtained by calling doc.getSyntaxSupport().
    * The method can return null in case the document is not
    * an instance of the BaseDocument.
    * The method doesn't require any document locking.
    * @param target JTextComponent for which the syntax-support should be obtained
    * @return SyntaxSupport instance or null
    */
    public static SyntaxSupport getSyntaxSupport(JTextComponent target) {
        Document doc = target.getDocument();
        return (doc instanceof BaseDocument) ? ((BaseDocument)doc).getSyntaxSupport() : null;
    }

    /**
     * Get first view in the hierarchy that is an instance of the given class.
     * It allows to skip various wrapper-views around the doc-view that holds
     * the child views for the lines.
     *
     * @param component component from which the root view is fetched.
     * @param rootViewClass class of the view to return.
     * @return view being instance of the requested class or null if there
     *  is not one.
     */
    public static View getRootView(JTextComponent component, Class rootViewClass) {
        View view = null;
        TextUI textUI = (TextUI)component.getUI();
        if (textUI != null) {
            view = textUI.getRootView(component);
            while (view != null && !rootViewClass.isInstance(view)
                && view.getViewCount() == 1 // must be wrapper view
            ) {
                view = view.getView(0); // get the only child
            }
        }
        
        return view;
    }
    
    /**
     * Get the view that covers the whole area of the document
     * and holds a child view for each line in the document
     * (or for a bunch of lines in case there is a code folding present).
     */
    public static View getDocumentView(JTextComponent component) {
        return getRootView(component, DrawEngineDocView.class);
    }

    /**
     * Creates nice textual description of sequence of KeyStrokes. Usable for
     * displaying MultiKeyBindings. The keyStrokes are delimited by space.
     * @param Array of KeyStrokes representing the actual sequence.
     * @return String describing the KeyStroke sequence.
     */
    public static String keySequenceToString( KeyStroke[] seq ) {
        StringBuffer sb = new StringBuffer();
        for( int i=0; i0 ) sb.append( ' ' );  // NOI18N
            sb.append( keyStrokeToString( seq[i] ) );
        }
        return sb.toString();
    }

    /**
     * Creates nice textual representation of KeyStroke.
     * Modifiers and an actual key label are concated by plus signs
     * @param the KeyStroke to get description of
     * @return String describing the KeyStroke
     */
    public static String keyStrokeToString( KeyStroke stroke ) {
        String modifText = KeyEvent.getKeyModifiersText( stroke.getModifiers() );
        String keyText = (stroke.getKeyCode() == KeyEvent.VK_UNDEFINED) ? 
            String.valueOf(stroke.getKeyChar()) : getKeyText(stroke.getKeyCode());
        if( modifText.length() > 0 ) return modifText + '+' + keyText;
        else return keyText;
    }
    
    /** @return slight modification of what KeyEvent.getKeyText() returns.
     *  The numpad Left, Right, Down, Up get extra result.
     */
    private static String getKeyText(int keyCode) {
        String ret = KeyEvent.getKeyText(keyCode);
        if (ret != null) {
            switch (keyCode) {
                case KeyEvent.VK_KP_DOWN:
                    ret = prefixNumpad(ret, KeyEvent.VK_DOWN);
                    break;
                case KeyEvent.VK_KP_LEFT:
                    ret = prefixNumpad(ret, KeyEvent.VK_LEFT);
                    break;
                case KeyEvent.VK_KP_RIGHT:
                    ret = prefixNumpad(ret, KeyEvent.VK_RIGHT);
                    break;
                case KeyEvent.VK_KP_UP:
                    ret = prefixNumpad(ret, KeyEvent.VK_UP);
                    break;
            }
        }
        return ret;
    }
    
    private static String prefixNumpad(String key, int testKeyCode) {
        if (key.equals(KeyEvent.getKeyText(testKeyCode))) {
            key = LocaleSupport.getString("key-prefix-numpad") + key;
        }
        return key;
    }

    private static void checkOffsetValid(Document doc, int offset) throws BadLocationException {
        checkOffsetValid(offset, doc.getLength());
    }

    private static void checkOffsetValid(int offset, int limitOffset) throws BadLocationException {
        if (offset < 0 || offset > limitOffset) { 
            throw new BadLocationException("Invalid offset=" + offset // NOI18N
                + " not within <0, " + limitOffset + ">", // NOI18N
                offset);
        }
    }

    /** Annotates a Throwable if ErrorManager.INFORMATIONAL is logbable */
    public static void annotateLoggable(Throwable t){
        if (isLoggable){
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, t);
        }
    }
    
}
... 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.