001: /*
002: * TextUtilities.java - Various text functions
003: * Copyright (C) 1998, 2005 Slava Pestov
004: * :tabSize=8:indentSize=8:noTabs=false:
005: * :folding=explicit:collapseFolds=1:
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.gjt.sp.jedit;
023:
024: //{{{ Imports
025: import java.util.*;
026: import javax.swing.text.Segment;
027: import org.gjt.sp.jedit.buffer.JEditBuffer;
028: import org.gjt.sp.jedit.syntax.*;
029: import org.gjt.sp.util.StandardUtilities;
030:
031: //}}}
032:
033: /**
034: * Contains several text manipulation methods.
035: *
036: * <ul>
037: * <li>Bracket matching
038: * <li>Word start and end offset calculation
039: * <li>String comparison
040: * <li>Converting tabs to spaces and vice versa
041: * <li>Wrapping text
042: * <li>String case conversion
043: * </ul>
044: *
045: * @author Slava Pestov
046: * @version $Id: TextUtilities.java 9134 2007-03-13 05:30:18Z vanza $
047: */
048: public class TextUtilities {
049: // to avoid slowdown with large files; only scan 10000 lines either way
050: public static final int BRACKET_MATCH_LIMIT = 10000;
051:
052: //{{{ getTokenAtOffset() method
053: /**
054: * Returns the token that contains the specified offset.
055: * @param tokens The token list
056: * @param offset The offset
057: * @since jEdit 4.0pre3
058: */
059: public static Token getTokenAtOffset(Token tokens, int offset) {
060: if (offset == 0 && tokens.id == Token.END)
061: return tokens;
062:
063: for (;;) {
064: if (tokens.id == Token.END)
065: throw new ArrayIndexOutOfBoundsException(
066: "offset > line length");
067:
068: if (tokens.offset + tokens.length > offset)
069: return tokens;
070: else
071: tokens = tokens.next;
072: }
073: } //}}}
074:
075: //{{{ getComplementaryBracket() method
076: /**
077: * Given an opening bracket, return the corresponding closing bracket
078: * and store true in <code>direction[0]</code>. Given a closing bracket,
079: * return the corresponding opening bracket and store false in
080: * <code>direction[0]</code>. Otherwise, return <code>\0</code>.
081: * @since jEdit 4.3pre2
082: */
083: public static char getComplementaryBracket(char ch,
084: boolean[] direction) {
085: switch (ch) {
086: case '(':
087: if (direction != null)
088: direction[0] = true;
089: return ')';
090: case ')':
091: if (direction != null)
092: direction[0] = false;
093: return '(';
094: case '[':
095: if (direction != null)
096: direction[0] = true;
097: return ']';
098: case ']':
099: if (direction != null)
100: direction[0] = false;
101: return '[';
102: case '{':
103: if (direction != null)
104: direction[0] = true;
105: return '}';
106: case '}':
107: if (direction != null)
108: direction[0] = false;
109: return '{';
110: default:
111: return '\0';
112: }
113: } //}}}
114:
115: //{{{ findMatchingBracket() method
116: /**
117: * Returns the offset of the bracket matching the one at the
118: * specified offset of the buffer, or -1 if the bracket is
119: * unmatched (or if the character is not a bracket).
120: * @param buffer The buffer
121: * @param line The line
122: * @param offset The offset within that line
123: * @since jEdit 2.6pre1
124: */
125: public static int findMatchingBracket(JEditBuffer buffer, int line,
126: int offset) {
127: if (offset < 0 || offset >= buffer.getLineLength(line)) {
128: throw new ArrayIndexOutOfBoundsException(offset + ":"
129: + buffer.getLineLength(line));
130: }
131:
132: Segment lineText = new Segment();
133: buffer.getLineText(line, lineText);
134:
135: char c = lineText.array[lineText.offset + offset];
136: // false - backwards, true - forwards
137: boolean[] direction = new boolean[1];
138:
139: // corresponding character
140: char cprime = getComplementaryBracket(c, direction);
141:
142: if (cprime == '\0') { // c is no bracket
143: return -1;
144: }
145:
146: // 1 because we've already 'seen' the first bracket
147: int count = 1;
148:
149: DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
150: buffer.markTokens(line, tokenHandler);
151:
152: // Get the syntax token at 'offset'
153: // only tokens with the same type will be checked for
154: // the corresponding bracket
155: byte idOfBracket = getTokenAtOffset(tokenHandler.getTokens(),
156: offset).id;
157:
158: boolean haveTokens = true;
159:
160: int startLine = line;
161:
162: //{{{ Forward search
163: if (direction[0]) {
164: offset++;
165:
166: for (;;) {
167: for (int i = offset; i < lineText.count; i++) {
168: char ch = lineText.array[lineText.offset + i];
169: if (ch == c) {
170: if (!haveTokens) {
171: tokenHandler.init();
172: buffer.markTokens(line, tokenHandler);
173: haveTokens = true;
174: }
175: if (getTokenAtOffset(tokenHandler.getTokens(),
176: i).id == idOfBracket)
177: count++;
178: } else if (ch == cprime) {
179: if (!haveTokens) {
180: tokenHandler.init();
181: buffer.markTokens(line, tokenHandler);
182: haveTokens = true;
183: }
184: if (getTokenAtOffset(tokenHandler.getTokens(),
185: i).id == idOfBracket) {
186: count--;
187: if (count == 0)
188: return buffer.getLineStartOffset(line)
189: + i;
190: }
191: }
192: }
193:
194: //{{{ Go on to next line
195: line++;
196: if (line >= buffer.getLineCount()
197: || (line - startLine) > BRACKET_MATCH_LIMIT)
198: break;
199: buffer.getLineText(line, lineText);
200: offset = 0;
201: haveTokens = false;
202: //}}}
203: }
204: } //}}}
205: //{{{ Backward search
206: else {
207: offset--;
208:
209: for (;;) {
210: for (int i = offset; i >= 0; i--) {
211: char ch = lineText.array[lineText.offset + i];
212: if (ch == c) {
213: if (!haveTokens) {
214: tokenHandler.init();
215: buffer.markTokens(line, tokenHandler);
216: haveTokens = true;
217: }
218: if (getTokenAtOffset(tokenHandler.getTokens(),
219: i).id == idOfBracket)
220: count++;
221: } else if (ch == cprime) {
222: if (!haveTokens) {
223: tokenHandler.init();
224: buffer.markTokens(line, tokenHandler);
225: haveTokens = true;
226: }
227: if (getTokenAtOffset(tokenHandler.getTokens(),
228: i).id == idOfBracket) {
229: count--;
230: if (count == 0)
231: return buffer.getLineStartOffset(line)
232: + i;
233: }
234: }
235: }
236:
237: //{{{ Go on to previous line
238: line--;
239: if (line < 0
240: || (startLine - line) > BRACKET_MATCH_LIMIT)
241: break;
242: buffer.getLineText(line, lineText);
243: offset = lineText.count - 1;
244: haveTokens = false;
245: //}}}
246: }
247: } //}}}
248:
249: // Nothing found
250: return -1;
251: } //}}}
252:
253: //{{{ findWordStart() method
254: /**
255: * Locates the start of the word at the specified position.
256: * @param line The text
257: * @param pos The position
258: * @param noWordSep Characters that are non-alphanumeric, but
259: * should be treated as word characters anyway
260: */
261: public static int findWordStart(String line, int pos,
262: String noWordSep) {
263: return findWordStart(line, pos, noWordSep, true, false);
264: } //}}}
265:
266: /** Similar to perl's join() method on lists,
267: * but works with all collections.
268: *
269: * @param c An iterable collection of Objects
270: * @param delim a string to put between each object
271: * @return a joined toString() representation of the collection
272: *
273: * @since jedit 4.3pre3
274: */
275: public static String join(Collection c, String delim) {
276: StringBuilder retval = new StringBuilder();
277: Iterator itr = c.iterator();
278: if (itr.hasNext()) {
279: retval.append(itr.next().toString());
280: } else
281: return "";
282: while (itr.hasNext()) {
283: retval.append(delim);
284: retval.append(itr.next().toString());
285: }
286: return retval.toString();
287: }
288:
289: //{{{ findWordStart() method
290: /**
291: * Locates the start of the word at the specified position.
292: * @param line The text
293: * @param pos The position
294: * @param noWordSep Characters that are non-alphanumeric, but
295: * should be treated as word characters anyway
296: * @param joinNonWordChars Treat consecutive non-alphanumeric
297: * characters as one word
298: * @since jEdit 4.2pre5
299: */
300: public static int findWordStart(String line, int pos,
301: String noWordSep, boolean joinNonWordChars) {
302: return findWordStart(line, pos, noWordSep, joinNonWordChars,
303: false);
304: } //}}}
305:
306: //{{{ findWordStart() method
307: /**
308: * Locates the start of the word at the specified position.
309: * @param line The text
310: * @param pos The position
311: * @param noWordSep Characters that are non-alphanumeric, but
312: * should be treated as word characters anyway
313: * @param joinNonWordChars Treat consecutive non-alphanumeric
314: * characters as one word
315: * @param eatWhitespace Include whitespace at start of word
316: * @since jEdit 4.1pre2
317: */
318: public static int findWordStart(String line, int pos,
319: String noWordSep, boolean joinNonWordChars,
320: boolean eatWhitespace) {
321: return findWordStart(line, pos, noWordSep, joinNonWordChars,
322: false, eatWhitespace);
323: } //}}}
324:
325: //{{{ findWordStart() method
326: /**
327: * Locates the start of the word at the specified position.
328: * @param line The text
329: * @param pos The position
330: * @param noWordSep Characters that are non-alphanumeric, but
331: * should be treated as word characters anyway
332: * @param joinNonWordChars Treat consecutive non-alphanumeric
333: * characters as one word
334: * @param camelCasedWords Treat "camelCased" parts as words
335: * @param eatWhitespace Include whitespace at start of word
336: * @since jEdit 4.3pre10
337: */
338: public static int findWordStart(String line, int pos,
339: String noWordSep, boolean joinNonWordChars,
340: boolean camelCasedWords, boolean eatWhitespace) {
341: char ch = line.charAt(pos);
342:
343: if (noWordSep == null)
344: noWordSep = "";
345:
346: //{{{ the character under the cursor changes how we behave.
347: int type = getCharType(ch, noWordSep);
348: //}}}
349:
350: loop: for (int i = pos; i >= 0; i--) {
351: char lastCh = ch;
352: ch = line.charAt(i);
353: switch (type) {
354: //{{{ Whitespace...
355: case WHITESPACE:
356: // only select other whitespace in this case
357: if (Character.isWhitespace(ch))
358: break;
359: // word char or symbol; stop
360: else
361: return i + 1; //}}}
362: //{{{ Word character...
363: case WORD_CHAR:
364: // stop at next last (in writing direction) upper case char if camel cased
365: // (don't stop at every upper case char, don't treat noWordSep as word chars)
366: if (camelCasedWords && Character.isUpperCase(ch)
367: && !Character.isUpperCase(lastCh)
368: && Character.isLetterOrDigit(lastCh)) {
369: return i;
370: }
371: // stop at next first (in writing direction) upper case char if camel cased
372: // (don't stop at every upper case char)
373: else if (camelCasedWords && !Character.isUpperCase(ch)
374: && Character.isUpperCase(lastCh)) {
375: return i + 1;
376: }
377: // word char; keep going
378: else if (Character.isLetterOrDigit(ch)
379: || noWordSep.indexOf(ch) != -1) {
380: break;
381: }
382: // whitespace; include in word if eating
383: else if (Character.isWhitespace(ch) && eatWhitespace) {
384: type = WHITESPACE;
385: break;
386: } else
387: return i + 1; //}}}
388: //{{{ Symbol...
389: case SYMBOL:
390: if (!joinNonWordChars && pos != i)
391: return i + 1;
392:
393: // whitespace; include in word if eating
394: if (Character.isWhitespace(ch)) {
395: if (eatWhitespace) {
396: type = WHITESPACE;
397: break;
398: } else
399: return i + 1;
400: } else if (Character.isLetterOrDigit(ch)
401: || noWordSep.indexOf(ch) != -1) {
402: return i + 1;
403: } else {
404: break;
405: } //}}}
406: }
407: }
408:
409: return 0;
410: } //}}}
411:
412: //{{{ findWordEnd() method
413: /**
414: * Locates the end of the word at the specified position.
415: * @param line The text
416: * @param pos The position
417: * @param noWordSep Characters that are non-alphanumeric, but
418: * should be treated as word characters anyway
419: */
420: public static int findWordEnd(String line, int pos, String noWordSep) {
421: return findWordEnd(line, pos, noWordSep, true);
422: } //}}}
423:
424: //{{{ findWordEnd() method
425: /**
426: * Locates the end of the word at the specified position.
427: * @param line The text
428: * @param pos The position
429: * @param noWordSep Characters that are non-alphanumeric, but
430: * should be treated as word characters anyway
431: * @param joinNonWordChars Treat consecutive non-alphanumeric
432: * characters as one word
433: * @since jEdit 4.1pre2
434: */
435: public static int findWordEnd(String line, int pos,
436: String noWordSep, boolean joinNonWordChars) {
437: return findWordEnd(line, pos, noWordSep, joinNonWordChars,
438: false);
439: } //}}}
440:
441: //{{{ findWordEnd() method
442: /**
443: * Locates the end of the word at the specified position.
444: * @param line The text
445: * @param pos The position
446: * @param noWordSep Characters that are non-alphanumeric, but
447: * should be treated as word characters anyway
448: * @param joinNonWordChars Treat consecutive non-alphanumeric
449: * characters as one word
450: * @param eatWhitespace Include whitespace at end of word
451: * @since jEdit 4.2pre5
452: */
453: public static int findWordEnd(String line, int pos,
454: String noWordSep, boolean joinNonWordChars,
455: boolean eatWhitespace) {
456: return findWordEnd(line, pos, noWordSep, joinNonWordChars,
457: false, eatWhitespace);
458: } //}}}
459:
460: //{{{ findWordEnd() method
461: /**
462: * Locates the end of the word at the specified position.
463: * @param line The text
464: * @param pos The position
465: * @param noWordSep Characters that are non-alphanumeric, but
466: * should be treated as word characters anyway
467: * @param joinNonWordChars Treat consecutive non-alphanumeric
468: * characters as one word
469: * @param camelCasedWords Treat "camelCased" parts as words
470: * @param eatWhitespace Include whitespace at end of word
471: * @since jEdit 4.3pre10
472: */
473: public static int findWordEnd(String line, int pos,
474: String noWordSep, boolean joinNonWordChars,
475: boolean camelCasedWords, boolean eatWhitespace) {
476: if (pos != 0)
477: pos--;
478:
479: char ch = line.charAt(pos);
480:
481: if (noWordSep == null)
482: noWordSep = "";
483:
484: //{{{ the character under the cursor changes how we behave.
485: int type = getCharType(ch, noWordSep);
486: //}}}
487:
488: loop: for (int i = pos; i < line.length(); i++) {
489: char lastCh = ch;
490: ch = line.charAt(i);
491: switch (type) {
492: //{{{ Whitespace...
493: case WHITESPACE:
494: // only select other whitespace in this case
495: if (Character.isWhitespace(ch))
496: break;
497: else
498: return i; //}}}
499: //{{{ Word character...
500: case WORD_CHAR:
501: // stop at next last upper case char if camel cased
502: // (don't stop at every upper case char, don't treat noWordSep as word chars)
503: if (camelCasedWords && i > pos + 1
504: && !Character.isUpperCase(ch)
505: && Character.isLetterOrDigit(ch)
506: && Character.isUpperCase(lastCh)) {
507: return i - 1;
508: }
509: // stop at next first upper case char if camel caseg (don't stop at every upper case char)
510: else if (camelCasedWords && Character.isUpperCase(ch)
511: && !Character.isUpperCase(lastCh)) {
512: return i;
513: } else if (Character.isLetterOrDigit(ch)
514: || noWordSep.indexOf(ch) != -1) {
515: break;
516: }
517: // whitespace; include in word if eating
518: else if (Character.isWhitespace(ch) && eatWhitespace) {
519: type = WHITESPACE;
520: break;
521: } else
522: return i; //}}}
523: //{{{ Symbol...
524: case SYMBOL:
525: if (!joinNonWordChars && i != pos)
526: return i;
527:
528: // if we see whitespace, set flag.
529: if (Character.isWhitespace(ch)) {
530: if (eatWhitespace) {
531: type = WHITESPACE;
532: break;
533: } else
534: return i;
535: } else if (Character.isLetterOrDigit(ch)
536: || noWordSep.indexOf(ch) != -1) {
537: return i;
538: } else {
539: break;
540: } //}}}
541: }
542: }
543:
544: return line.length();
545: } //}}}
546:
547: /**
548: * Returns the type of the char.
549: *
550: * @param ch the character
551: * @param noWordSep Characters that are non-alphanumeric, but
552: * should be treated as word characters anyway
553: * @return the type of the char : {@link #WHITESPACE}, {@link #WORD_CHAR}, {@link #SYMBOL}
554: */
555: private static int getCharType(char ch, String noWordSep) {
556: int type;
557: if (Character.isWhitespace(ch))
558: type = WHITESPACE;
559: else if (Character.isLetterOrDigit(ch)
560: || noWordSep.indexOf(ch) != -1)
561: type = WORD_CHAR;
562: else
563: type = SYMBOL;
564: return type;
565: }
566:
567: //{{{ spacesToTabs() method
568: /**
569: * Converts consecutive spaces to tabs in the specified string.
570: * @param in The string
571: * @param tabSize The tab size
572: */
573: public static String spacesToTabs(String in, int tabSize) {
574: StringBuilder buf = new StringBuilder();
575: int width = 0;
576: int whitespace = 0;
577: for (int i = 0; i < in.length(); i++) {
578: switch (in.charAt(i)) {
579: case ' ':
580: whitespace++;
581: width++;
582: break;
583: case '\t':
584: int tab = tabSize - (width % tabSize);
585: width += tab;
586: whitespace += tab;
587: break;
588: case '\n':
589: if (whitespace != 0) {
590: buf.append(StandardUtilities.createWhiteSpace(
591: whitespace, tabSize, width - whitespace));
592: }
593: whitespace = 0;
594: width = 0;
595: buf.append('\n');
596: break;
597: default:
598: if (whitespace != 0) {
599: buf.append(StandardUtilities.createWhiteSpace(
600: whitespace, tabSize, width - whitespace));
601: whitespace = 0;
602: }
603: buf.append(in.charAt(i));
604: width++;
605: break;
606: }
607: }
608:
609: if (whitespace != 0) {
610: buf.append(StandardUtilities.createWhiteSpace(whitespace,
611: tabSize, width - whitespace));
612: }
613:
614: return buf.toString();
615: } //}}}
616:
617: //{{{ tabsToSpaces() method
618: /**
619: * Converts tabs to consecutive spaces in the specified string.
620: * @param in The string
621: * @param tabSize The tab size
622: */
623: public static String tabsToSpaces(String in, int tabSize) {
624: StringBuilder buf = new StringBuilder();
625: int width = 0;
626: for (int i = 0; i < in.length(); i++) {
627: switch (in.charAt(i)) {
628: case '\t':
629: int count = tabSize - (width % tabSize);
630: width += count;
631: while (--count >= 0)
632: buf.append(' ');
633: break;
634: case '\n':
635: width = 0;
636: buf.append(in.charAt(i));
637: break;
638: default:
639: width++;
640: buf.append(in.charAt(i));
641: break;
642: }
643: }
644: return buf.toString();
645: } //}}}
646:
647: //{{{ format() method
648: /**
649: * Formats the specified text by merging and breaking lines to the
650: * specified width.
651: * @param text The text
652: * @param maxLineLength The maximum line length
653: * @param tabSize The tab size
654: */
655: public static String format(String text, int maxLineLength,
656: int tabSize) {
657: StringBuilder buf = new StringBuilder();
658:
659: int index = 0;
660:
661: for (;;) {
662: int newIndex = text.indexOf("\n\n", index);
663: if (newIndex == -1)
664: break;
665:
666: formatParagraph(text.substring(index, newIndex),
667: maxLineLength, tabSize, buf);
668: buf.append("\n\n");
669: index = newIndex + 2;
670: }
671:
672: if (index != text.length()) {
673: formatParagraph(text.substring(index), maxLineLength,
674: tabSize, buf);
675: }
676:
677: return buf.toString();
678: } //}}}
679:
680: //{{{ indexIgnoringWhitespace() method
681: /**
682: * Inverse of <code>ignoringWhitespaceIndex()</code>.
683: * @param str a string (not an empty string)
684: * @param index The index
685: * @return The number of non-whitespace characters that precede the index.
686: * @since jEdit 4.3pre2
687: */
688: public static int indexIgnoringWhitespace(String str, int index) {
689: int j = 0;
690: for (int i = 0; i < index; i++)
691: if (!Character.isWhitespace(str.charAt(i)))
692: j++;
693: return j;
694: } //}}}
695:
696: //{{{ ignoringWhitespaceIndex() method
697: /**
698: * Inverse of <code>indexIgnoringWhitespace()</code>.
699: * @param str a string (not an empty string)
700: * @param index The index
701: * @return The index into the string where the number of non-whitespace
702: * characters that precede the index is count.
703: * @since jEdit 4.3pre2
704: */
705: public static int ignoringWhitespaceIndex(String str, int index) {
706: int j = 0;
707: for (int i = 0;; i++) {
708: if (!Character.isWhitespace(str.charAt(i)))
709: j++;
710:
711: if (j > index)
712: return i;
713: if (i == str.length() - 1)
714: return i + 1;
715: }
716: } //}}}
717:
718: //{{{ getStringCase() method
719: public static final int MIXED = 0;
720: public static final int LOWER_CASE = 1;
721: public static final int UPPER_CASE = 2;
722: public static final int TITLE_CASE = 3;
723:
724: /**
725: * Returns if the specified string is all upper case, all lower case,
726: * or title case (first letter upper case, rest lower case).
727: * @param str The string
728: * @since jEdit 4.0pre1
729: */
730: public static int getStringCase(String str) {
731: if (str.length() == 0)
732: return MIXED;
733:
734: int state = -1;
735:
736: char ch = str.charAt(0);
737: if (Character.isLetter(ch)) {
738: if (Character.isUpperCase(ch))
739: state = UPPER_CASE;
740: else
741: state = LOWER_CASE;
742: }
743:
744: for (int i = 1; i < str.length(); i++) {
745: ch = str.charAt(i);
746: if (!Character.isLetter(ch))
747: continue;
748:
749: switch (state) {
750: case UPPER_CASE:
751: if (Character.isLowerCase(ch)) {
752: if (i == 1)
753: state = TITLE_CASE;
754: else
755: return MIXED;
756: }
757: break;
758: case LOWER_CASE:
759: case TITLE_CASE:
760: if (Character.isUpperCase(ch))
761: return MIXED;
762: break;
763: }
764: }
765:
766: return state;
767: } //}}}
768:
769: //{{{ toTitleCase() method
770: /**
771: * Converts the specified string to title case, by capitalizing the
772: * first letter.
773: * @param str The string
774: * @since jEdit 4.0pre1
775: */
776: public static String toTitleCase(String str) {
777: if (str.length() == 0)
778: return str;
779: else {
780: return Character.toUpperCase(str.charAt(0))
781: + str.substring(1).toLowerCase();
782: }
783: } //}}}
784:
785: //{{{ Private members
786: private static final int WHITESPACE = 0;
787: private static final int WORD_CHAR = 1;
788: private static final int SYMBOL = 2;
789:
790: //{{{ formatParagraph() method
791: private static void formatParagraph(String text, int maxLineLength,
792: int tabSize, StringBuilder buf) {
793: // align everything to paragraph's leading indent
794: int leadingWhitespaceCount = StandardUtilities
795: .getLeadingWhiteSpace(text);
796: String leadingWhitespace = text.substring(0,
797: leadingWhitespaceCount);
798: int leadingWhitespaceWidth = StandardUtilities
799: .getLeadingWhiteSpaceWidth(text, tabSize);
800:
801: buf.append(leadingWhitespace);
802:
803: int lineLength = leadingWhitespaceWidth;
804: StringTokenizer st = new StringTokenizer(text);
805: while (st.hasMoreTokens()) {
806: String word = st.nextToken();
807: if (lineLength == leadingWhitespaceWidth) {
808: // do nothing
809: } else if (lineLength + word.length() + 1 > maxLineLength) {
810: buf.append('\n');
811: buf.append(leadingWhitespace);
812: lineLength = leadingWhitespaceWidth;
813: } else {
814: buf.append(' ');
815: lineLength++;
816: }
817: buf.append(word);
818: lineLength += word.length();
819: }
820: } //}}}
821:
822: //{{{ indexIgnoringWhitespace() method
823: public static void indexIgnoringWhitespace(String text,
824: int maxLineLength, int tabSize, StringBuffer buf) {
825: // align everything to paragraph's leading indent
826: int leadingWhitespaceCount = StandardUtilities
827: .getLeadingWhiteSpace(text);
828: String leadingWhitespace = text.substring(0,
829: leadingWhitespaceCount);
830: int leadingWhitespaceWidth = StandardUtilities
831: .getLeadingWhiteSpaceWidth(text, tabSize);
832:
833: buf.append(leadingWhitespace);
834:
835: int lineLength = leadingWhitespaceWidth;
836: StringTokenizer st = new StringTokenizer(text);
837: while (st.hasMoreTokens()) {
838: String word = st.nextToken();
839: if (lineLength == leadingWhitespaceWidth) {
840: // do nothing
841: } else if (lineLength + word.length() + 1 > maxLineLength) {
842: buf.append('\n');
843: buf.append(leadingWhitespace);
844: lineLength = leadingWhitespaceWidth;
845: } else {
846: buf.append(' ');
847: lineLength++;
848: }
849: buf.append(word);
850: lineLength += word.length();
851: }
852: } //}}}
853:
854: //}}}
855: }
|