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

/*
 * TextUtilities.java - Various text functions
 * Copyright (C) 1998, 2003 Slava Pestov
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package org.gjt.sp.jedit;

//{{{ Imports
import java.util.*;
import javax.swing.text.Segment;
import org.gjt.sp.jedit.syntax.*;
//}}}

/**
 * Contains several text manipulation methods.
 *
 * 
    *
  • Bracket matching *
  • Word start and end offset calculation *
  • String comparison *
  • Converting tabs to spaces and vice versa *
  • Wrapping text *
  • String case conversion *
* * @author Slava Pestov * @version $Id: TextUtilities.java,v 1.47 2004/03/28 00:07:26 spestov Exp $ */ public class TextUtilities { // to avoid slowdown with large files; only scan 10000 lines either way public static final int BRACKET_MATCH_LIMIT = 10000; //{{{ getTokenAtOffset() method /** * Returns the token that contains the specified offset. * @param tokens The token list * @param offset The offset * @since jEdit 4.0pre3 */ public static Token getTokenAtOffset(Token tokens, int offset) { if(offset == 0 && tokens.id == Token.END) return tokens; for(;;) { if(tokens.id == Token.END) throw new ArrayIndexOutOfBoundsException("offset > line length"); if(tokens.offset + tokens.length > offset) return tokens; else tokens = tokens.next; } } //}}} //{{{ findMatchingBracket() method /** * Returns the offset of the bracket matching the one at the * specified offset of the buffer, or -1 if the bracket is * unmatched (or if the character is not a bracket). * @param buffer The buffer * @param line The line * @param offset The offset within that line * @since jEdit 2.6pre1 */ public static int findMatchingBracket(Buffer buffer, int line, int offset) { if(offset < 0 || offset >= buffer.getLineLength(line)) { throw new ArrayIndexOutOfBoundsException(offset + ":" + buffer.getLineLength(line)); } Segment lineText = new Segment(); buffer.getLineText(line,lineText); char c = lineText.array[lineText.offset + offset]; char cprime; // corresponding character boolean direction; // false - backwards, true - forwards switch(c) { case '(': cprime = ')'; direction = true; break; case ')': cprime = '('; direction = false; break; case '[': cprime = ']'; direction = true; break; case ']': cprime = '['; direction = false; break; case '{': cprime = '}'; direction = true; break; case '}': cprime = '{'; direction = false; break; default: return -1; } // 1 because we've already 'seen' the first bracket int count = 1; DefaultTokenHandler tokenHandler = new DefaultTokenHandler(); buffer.markTokens(line,tokenHandler); // Get the syntax token at 'offset' // only tokens with the same type will be checked for // the corresponding bracket byte idOfBracket = getTokenAtOffset(tokenHandler.getTokens(),offset).id; boolean haveTokens = true; int startLine = line; //{{{ Forward search if(direction) { offset++; for(;;) { for(int i = offset; i < lineText.count; i++) { char ch = lineText.array[lineText.offset + i]; if(ch == c) { if(!haveTokens) { tokenHandler.init(); buffer.markTokens(line,tokenHandler); haveTokens = true; } if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket) count++; } else if(ch == cprime) { if(!haveTokens) { tokenHandler.init(); buffer.markTokens(line,tokenHandler); haveTokens = true; } if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket) { count--; if(count == 0) return buffer.getLineStartOffset(line) + i; } } } //{{{ Go on to next line line++; if(line >= buffer.getLineCount() || (line - startLine) > BRACKET_MATCH_LIMIT) break; buffer.getLineText(line,lineText); offset = 0; haveTokens = false; //}}} } } //}}} //{{{ Backward search else { offset--; for(;;) { for(int i = offset; i >= 0; i--) { char ch = lineText.array[lineText.offset + i]; if(ch == c) { if(!haveTokens) { tokenHandler.init(); buffer.markTokens(line,tokenHandler); haveTokens = true; } if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket) count++; } else if(ch == cprime) { if(!haveTokens) { tokenHandler.init(); buffer.markTokens(line,tokenHandler); haveTokens = true; } if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket) { count--; if(count == 0) return buffer.getLineStartOffset(line) + i; } } } //{{{ Go on to previous line line--; if(line < 0 || (startLine - line) > BRACKET_MATCH_LIMIT) break; buffer.getLineText(line,lineText); offset = lineText.count - 1; haveTokens = false; //}}} } } //}}} // Nothing found return -1; } //}}} //{{{ findWordStart() method /** * Locates the start of the word at the specified position. * @param line The text * @param pos The position * @param noWordSep Characters that are non-alphanumeric, but * should be treated as word characters anyway */ public static int findWordStart(String line, int pos, String noWordSep) { return findWordStart(line, pos, noWordSep, true, false); } //}}} //{{{ findWordStart() method /** * Locates the start of the word at the specified position. * @param line The text * @param pos The position * @param noWordSep Characters that are non-alphanumeric, but * should be treated as word characters anyway * @param joinNonWordChars Treat consecutive non-alphanumeric * characters as one word * @since jEdit 4.2pre5 */ public static int findWordStart(String line, int pos, String noWordSep, boolean joinNonWordChars) { return findWordStart(line,pos,noWordSep,joinNonWordChars,false); } //}}} //{{{ findWordStart() method /** * Locates the start of the word at the specified position. * @param line The text * @param pos The position * @param noWordSep Characters that are non-alphanumeric, but * should be treated as word characters anyway * @param joinNonWordChars Treat consecutive non-alphanumeric * characters as one word * @param eatWhitespace Include whitespace at start of word * @since jEdit 4.1pre2 */ public static int findWordStart(String line, int pos, String noWordSep, boolean joinNonWordChars, boolean eatWhitespace) { char ch = line.charAt(pos); if(noWordSep == null) noWordSep = ""; //{{{ the character under the cursor changes how we behave. int type; if(Character.isWhitespace(ch)) type = WHITESPACE; else if(Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1) type = WORD_CHAR; else type = SYMBOL; //}}} loop: for(int i = pos; i >= 0; i--) { ch = line.charAt(i); switch(type) { //{{{ Whitespace... case WHITESPACE: // only select other whitespace in this case if(Character.isWhitespace(ch)) break; // word char or symbol; stop else return i + 1; //}}} //{{{ Word character... case WORD_CHAR: // word char; keep going if(Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1) { break; } // whitespace; include in word if eating else if(Character.isWhitespace(ch) && eatWhitespace) { type = WHITESPACE; break; } else return i + 1; //}}} //{{{ Symbol... case SYMBOL: if(!joinNonWordChars && pos != i) return i + 1; // whitespace; include in word if eating if(Character.isWhitespace(ch)) { if(eatWhitespace) { type = WHITESPACE; break; } else return i + 1; } else if(Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1) { return i + 1; } else { break; } //}}} } } return 0; } //}}} //{{{ findWordEnd() method /** * Locates the end of the word at the specified position. * @param line The text * @param pos The position * @param noWordSep Characters that are non-alphanumeric, but * should be treated as word characters anyway */ public static int findWordEnd(String line, int pos, String noWordSep) { return findWordEnd(line, pos, noWordSep, true); } //}}} //{{{ findWordEnd() method /** * Locates the end of the word at the specified position. * @param line The text * @param pos The position * @param noWordSep Characters that are non-alphanumeric, but * should be treated as word characters anyway * @param joinNonWordChars Treat consecutive non-alphanumeric * characters as one word * @since jEdit 4.1pre2 */ public static int findWordEnd(String line, int pos, String noWordSep, boolean joinNonWordChars) { return findWordEnd(line,pos,noWordSep,joinNonWordChars,false); } //}}} //{{{ findWordEnd() method /** * Locates the end of the word at the specified position. * @param line The text * @param pos The position * @param noWordSep Characters that are non-alphanumeric, but * should be treated as word characters anyway * @param joinNonWordChars Treat consecutive non-alphanumeric * characters as one word * @param eatWhitespace Include whitespace at end of word * @since jEdit 4.2pre5 */ public static int findWordEnd(String line, int pos, String noWordSep, boolean joinNonWordChars, boolean eatWhitespace) { if(pos != 0) pos--; char ch = line.charAt(pos); if(noWordSep == null) noWordSep = ""; //{{{ the character under the cursor changes how we behave. int type; if(Character.isWhitespace(ch)) type = WHITESPACE; else if(Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1) type = WORD_CHAR; else type = SYMBOL; //}}} loop: for(int i = pos; i < line.length(); i++) { ch = line.charAt(i); switch(type) { //{{{ Whitespace... case WHITESPACE: // only select other whitespace in this case if(Character.isWhitespace(ch)) break; else return i; //}}} //{{{ Word character... case WORD_CHAR: if(Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1) { break; } // whitespace; include in word if eating else if(Character.isWhitespace(ch) && eatWhitespace) { type = WHITESPACE; break; } else return i; //}}} //{{{ Symbol... case SYMBOL: if(!joinNonWordChars && i != pos) return i; // if we see whitespace, set flag. if(Character.isWhitespace(ch)) { if(eatWhitespace) { type = WHITESPACE; break; } else return i; } else if(Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1) { return i; } else { break; } //}}} } } return line.length(); } //}}} //{{{ spacesToTabs() method /** * Converts consecutive spaces to tabs in the specified string. * @param in The string * @param tabSize The tab size */ public static String spacesToTabs(String in, int tabSize) { StringBuffer buf = new StringBuffer(); int width = 0; int whitespace = 0; for(int i = 0; i < in.length(); i++) { switch(in.charAt(i)) { case ' ': whitespace++; width++; break; case '\t': int tab = tabSize - (width % tabSize); width += tab; whitespace += tab; break; case '\n': if(whitespace != 0) { buf.append(MiscUtilities .createWhiteSpace(whitespace,tabSize, width - whitespace)); } whitespace = 0; width = 0; buf.append('\n'); break; default: if(whitespace != 0) { buf.append(MiscUtilities .createWhiteSpace(whitespace,tabSize, width - whitespace)); whitespace = 0; } buf.append(in.charAt(i)); width++; break; } } if(whitespace != 0) { buf.append(MiscUtilities.createWhiteSpace(whitespace,tabSize, width - whitespace)); } return buf.toString(); } //}}} //{{{ tabsToSpaces() method /** * Converts tabs to consecutive spaces in the specified string. * @param in The string * @param tabSize The tab size */ public static String tabsToSpaces(String in, int tabSize) { StringBuffer buf = new StringBuffer(); int width = 0; for(int i = 0; i < in.length(); i++) { switch(in.charAt(i)) { case '\t': int count = tabSize - (width % tabSize); width += count; while(--count >= 0) buf.append(' '); break; case '\n': width = 0; buf.append(in.charAt(i)); break; default: width++; buf.append(in.charAt(i)); break; } } return buf.toString(); } //}}} //{{{ format() method /** * Formats the specified text by merging and breaking lines to the * specified width. * @param text The text * @param maxLineLength The maximum line length * @param tabSize The tab size */ public static String format(String text, int maxLineLength, int tabSize) { StringBuffer buf = new StringBuffer(); int index = 0; for(;;) { int newIndex = text.indexOf("\n\n",index); if(newIndex == -1) break; formatParagraph(text.substring(index,newIndex), maxLineLength,tabSize,buf); buf.append("\n\n"); index = newIndex + 2; } if(index != text.length()) { formatParagraph(text.substring(index), maxLineLength,tabSize,buf); } return buf.toString(); } //}}} //{{{ getStringCase() method public static final int MIXED = 0; public static final int LOWER_CASE = 1; public static final int UPPER_CASE = 2; public static final int TITLE_CASE = 3; /** * Returns if the specified string is all upper case, all lower case, * or title case (first letter upper case, rest lower case). * @param str The string * @since jEdit 4.0pre1 */ public static int getStringCase(String str) { if(str.length() == 0) return MIXED; int state = -1; char ch = str.charAt(0); if(Character.isLetter(ch)) { if(Character.isUpperCase(ch)) state = UPPER_CASE; else state = LOWER_CASE; } for(int i = 1; i < str.length(); i++) { ch = str.charAt(i); if(!Character.isLetter(ch)) continue; switch(state) { case UPPER_CASE: if(Character.isLowerCase(ch)) { if(i == 1) state = TITLE_CASE; else return MIXED; } break; case LOWER_CASE: case TITLE_CASE: if(Character.isUpperCase(ch)) return MIXED; break; } } return state; } //}}} //{{{ toTitleCase() method /** * Converts the specified string to title case, by capitalizing the * first letter. * @param str The string * @since jEdit 4.0pre1 */ public static String toTitleCase(String str) { if(str.length() == 0) return str; else { return Character.toUpperCase(str.charAt(0)) + str.substring(1).toLowerCase(); } } //}}} //{{{ Private members private static final int WHITESPACE = 0; private static final int WORD_CHAR = 1; private static final int SYMBOL = 2; //{{{ formatParagraph() method private static void formatParagraph(String text, int maxLineLength, int tabSize, StringBuffer buf) { // align everything to paragraph's leading indent int leadingWhitespaceCount = MiscUtilities.getLeadingWhiteSpace(text); String leadingWhitespace = text.substring(0,leadingWhitespaceCount); int leadingWhitespaceWidth = MiscUtilities.getLeadingWhiteSpaceWidth(text,tabSize); buf.append(leadingWhitespace); int lineLength = leadingWhitespaceWidth; StringTokenizer st = new StringTokenizer(text); while(st.hasMoreTokens()) { String word = st.nextToken(); if(lineLength == leadingWhitespaceWidth) { // do nothing } else if(lineLength + word.length() + 1 > maxLineLength) { buf.append('\n'); buf.append(leadingWhitespace); lineLength = leadingWhitespaceWidth; } else { buf.append(' '); lineLength++; } buf.append(word); lineLength += word.length(); } } //}}} //}}} }
... 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.