PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/TextUtilities.java

#
Java | 1044 lines | 632 code | 81 blank | 331 comment | 173 complexity | 27f2c63324ac9d98398c1fdaee8df77f MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * TextUtilities.java - Various text functions
  3. * Copyright (C) 1998, 2005 Slava Pestov
  4. * :tabSize=8:indentSize=8:noTabs=false:
  5. * :folding=explicit:collapseFolds=1:
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. */
  21. package org.gjt.sp.jedit;
  22. //{{{ Imports
  23. import java.util.*;
  24. import javax.swing.text.Segment;
  25. import org.gjt.sp.jedit.buffer.JEditBuffer;
  26. import org.gjt.sp.jedit.syntax.*;
  27. import org.gjt.sp.util.StandardUtilities;
  28. //}}}
  29. /**
  30. * Contains several text manipulation methods.
  31. *
  32. * <ul>
  33. * <li>Bracket matching
  34. * <li>Word start and end offset calculation
  35. * <li>String comparison
  36. * <li>Converting tabs to spaces and vice versa
  37. * <li>Wrapping text
  38. * <li>String case conversion
  39. * </ul>
  40. *
  41. * @author Slava Pestov
  42. * @version $Id: TextUtilities.java 19412 2011-03-01 15:07:08Z kpouer $
  43. */
  44. public class TextUtilities
  45. {
  46. // to avoid slowdown with large files; only scan 10000 lines either way
  47. public static final int BRACKET_MATCH_LIMIT = 10000;
  48. public static final int WHITESPACE = 0;
  49. public static final int WORD_CHAR = 1;
  50. public static final int SYMBOL = 2;
  51. //{{{ getTokenAtOffset() method
  52. /**
  53. * Returns the token that contains the specified offset.
  54. * @param tokens The token list
  55. * @param offset The offset
  56. * @since jEdit 4.0pre3
  57. */
  58. public static Token getTokenAtOffset(Token tokens, int offset)
  59. {
  60. if(offset == 0 && tokens.id == Token.END)
  61. return tokens;
  62. for(;;)
  63. {
  64. if(tokens.id == Token.END)
  65. throw new ArrayIndexOutOfBoundsException("offset > line length");
  66. if(tokens.offset + tokens.length > offset)
  67. return tokens;
  68. else
  69. tokens = tokens.next;
  70. }
  71. } //}}}
  72. //{{{ getComplementaryBracket() method
  73. /**
  74. * Given an opening bracket, return the corresponding closing bracket
  75. * and store true in <code>direction[0]</code>. Given a closing bracket,
  76. * return the corresponding opening bracket and store false in
  77. * <code>direction[0]</code>. Otherwise, return <code>\0</code>.
  78. * @since jEdit 4.3pre2
  79. */
  80. public static char getComplementaryBracket(char ch, boolean[] direction)
  81. {
  82. switch(ch)
  83. {
  84. case '(': if (direction != null) direction[0] = true; return ')';
  85. case ')': if (direction != null) direction[0] = false; return '(';
  86. case '[': if (direction != null) direction[0] = true; return ']';
  87. case ']': if (direction != null) direction[0] = false; return '[';
  88. case '{': if (direction != null) direction[0] = true; return '}';
  89. case '}': if (direction != null) direction[0] = false; return '{';
  90. case '<': if (direction != null) direction[0] = true; return '>';
  91. case '>': if (direction != null) direction[0] = false; return '<';
  92. default: return '\0';
  93. }
  94. } //}}}
  95. //{{{ findMatchingBracket() method
  96. /**
  97. * Returns the offset of the bracket matching the one at the
  98. * specified offset of the buffer, or -1 if the bracket is
  99. * unmatched (or if the character is not a bracket).
  100. * @param buffer The buffer
  101. * @param line The line
  102. * @param offset The offset within that line
  103. * @since jEdit 2.6pre1
  104. */
  105. public static int findMatchingBracket(JEditBuffer buffer, int line, int offset)
  106. {
  107. if(offset < 0 || offset >= buffer.getLineLength(line))
  108. {
  109. throw new ArrayIndexOutOfBoundsException(offset + ":"
  110. + buffer.getLineLength(line));
  111. }
  112. Segment lineText = new Segment();
  113. buffer.getLineText(line,lineText);
  114. char c = lineText.array[lineText.offset + offset];
  115. // false - backwards, true - forwards
  116. boolean[] direction = new boolean[1];
  117. // corresponding character
  118. char cprime = getComplementaryBracket(c,direction);
  119. if( cprime == '\0' )
  120. { // c is no bracket
  121. return -1;
  122. }
  123. // 1 because we've already 'seen' the first bracket
  124. int count = 1;
  125. DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
  126. buffer.markTokens(line,tokenHandler);
  127. // Get the syntax token at 'offset'
  128. // only tokens with the same type will be checked for
  129. // the corresponding bracket
  130. byte idOfBracket = getTokenAtOffset(tokenHandler.getTokens(),offset).id;
  131. boolean haveTokens = true;
  132. int startLine = line;
  133. //{{{ Forward search
  134. if(direction[0])
  135. {
  136. offset++;
  137. for(;;)
  138. {
  139. for(int i = offset; i < lineText.count; i++)
  140. {
  141. char ch = lineText.array[lineText.offset + i];
  142. if(ch == c)
  143. {
  144. if(!haveTokens)
  145. {
  146. tokenHandler.init();
  147. buffer.markTokens(line,tokenHandler);
  148. haveTokens = true;
  149. }
  150. if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket)
  151. count++;
  152. }
  153. else if(ch == cprime)
  154. {
  155. if(!haveTokens)
  156. {
  157. tokenHandler.init();
  158. buffer.markTokens(line,tokenHandler);
  159. haveTokens = true;
  160. }
  161. if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket)
  162. {
  163. count--;
  164. if(count == 0)
  165. return buffer.getLineStartOffset(line) + i;
  166. }
  167. }
  168. }
  169. //{{{ Go on to next line
  170. line++;
  171. if(line >= buffer.getLineCount() || (line - startLine) > BRACKET_MATCH_LIMIT)
  172. break;
  173. buffer.getLineText(line,lineText);
  174. offset = 0;
  175. haveTokens = false;
  176. //}}}
  177. }
  178. } //}}}
  179. //{{{ Backward search
  180. else
  181. {
  182. offset--;
  183. for(;;)
  184. {
  185. for(int i = offset; i >= 0; i--)
  186. {
  187. char ch = lineText.array[lineText.offset + i];
  188. if(ch == c)
  189. {
  190. if(!haveTokens)
  191. {
  192. tokenHandler.init();
  193. buffer.markTokens(line,tokenHandler);
  194. haveTokens = true;
  195. }
  196. if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket)
  197. count++;
  198. }
  199. else if(ch == cprime)
  200. {
  201. if(!haveTokens)
  202. {
  203. tokenHandler.init();
  204. buffer.markTokens(line,tokenHandler);
  205. haveTokens = true;
  206. }
  207. if(getTokenAtOffset(tokenHandler.getTokens(),i).id == idOfBracket)
  208. {
  209. count--;
  210. if(count == 0)
  211. return buffer.getLineStartOffset(line) + i;
  212. }
  213. }
  214. }
  215. //{{{ Go on to previous line
  216. line--;
  217. if(line < 0 || (startLine - line) > BRACKET_MATCH_LIMIT)
  218. break;
  219. buffer.getLineText(line,lineText);
  220. offset = lineText.count - 1;
  221. haveTokens = false;
  222. //}}}
  223. }
  224. } //}}}
  225. // Nothing found
  226. return -1;
  227. } //}}}
  228. //{{{ join() method
  229. /** Similar to perl's join() method on lists,
  230. * but works with all collections.
  231. *
  232. * @param c An iterable collection of Objects
  233. * @param delim a string to put between each object
  234. * @return a joined toString() representation of the collection
  235. *
  236. * @since jedit 4.3pre3
  237. */
  238. public static String join(Collection<String> c, String delim)
  239. {
  240. StringBuilder retval = new StringBuilder();
  241. Iterator<String> itr = c.iterator();
  242. if (itr.hasNext())
  243. retval.append( itr.next() );
  244. else
  245. return "";
  246. while (itr.hasNext())
  247. {
  248. retval.append(delim);
  249. retval.append(itr.next());
  250. }
  251. return retval.toString();
  252. } //}}}
  253. //{{{ findWordStart() methods
  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, String noWordSep)
  262. {
  263. return findWordStart(line, pos, noWordSep, true, false);
  264. }
  265. /**
  266. * Locates the start of the word at the specified position.
  267. * @param line The text
  268. * @param pos The position
  269. * @param noWordSep Characters that are non-alphanumeric, but
  270. * should be treated as word characters anyway
  271. * @since jEdit 4.3pre15
  272. */
  273. public static int findWordStart(CharSequence line,
  274. int pos,
  275. String noWordSep)
  276. {
  277. return findWordStart(line, pos, noWordSep, true, false, false);
  278. }
  279. /**
  280. * Locates the start of the word at the specified position.
  281. * @param line The text
  282. * @param pos The position
  283. * @param noWordSep Characters that are non-alphanumeric, but
  284. * should be treated as word characters anyway
  285. * @param joinNonWordChars Treat consecutive non-alphanumeric
  286. * characters as one word
  287. * @since jEdit 4.2pre5
  288. */
  289. public static int findWordStart(String line, int pos, String noWordSep,
  290. boolean joinNonWordChars)
  291. {
  292. return findWordStart(line,pos,noWordSep,joinNonWordChars,false);
  293. }
  294. /**
  295. * Locates the start of the word at the specified position.
  296. * @param line The text
  297. * @param pos The position
  298. * @param noWordSep Characters that are non-alphanumeric, but
  299. * should be treated as word characters anyway
  300. * @param joinNonWordChars Treat consecutive non-alphanumeric
  301. * characters as one word
  302. * @param eatWhitespace Include whitespace at start of word
  303. * @since jEdit 4.1pre2
  304. */
  305. public static int findWordStart(String line, int pos, String noWordSep,
  306. boolean joinNonWordChars, boolean eatWhitespace)
  307. {
  308. return findWordStart(line, pos, noWordSep, joinNonWordChars,
  309. false, eatWhitespace);
  310. }
  311. /**
  312. * Locates the start of the word at the specified position.
  313. * @param line The text
  314. * @param pos The position
  315. * @param noWordSep Characters that are non-alphanumeric, but
  316. * should be treated as word characters anyway
  317. * @param joinNonWordChars Treat consecutive non-alphanumeric
  318. * characters as one word
  319. * @param camelCasedWords Treat "camelCased" parts as words
  320. * @param eatWhitespace Include whitespace at start of word
  321. * @since jEdit 4.3pre10
  322. */
  323. public static int findWordStart(String line, int pos, String noWordSep,
  324. boolean joinNonWordChars, boolean camelCasedWords,
  325. boolean eatWhitespace)
  326. {
  327. return findWordStart((CharSequence) line, pos, noWordSep,
  328. joinNonWordChars, camelCasedWords,
  329. eatWhitespace);
  330. }
  331. /**
  332. * Locates the start of the word at the specified position.
  333. * @param line The text
  334. * @param pos The position
  335. * @param noWordSep Characters that are non-alphanumeric, but
  336. * should be treated as word characters anyway
  337. * @param joinNonWordChars Treat consecutive non-alphanumeric
  338. * characters as one word
  339. * @param camelCasedWords Treat "camelCased" parts as words
  340. * @param eatWhitespace Include whitespace at start of word
  341. * @since jEdit 4.3pre15
  342. */
  343. public static int findWordStart(CharSequence line,
  344. int pos,
  345. String noWordSep,
  346. boolean joinNonWordChars,
  347. boolean camelCasedWords,
  348. boolean eatWhitespace)
  349. {
  350. return findWordStart(line, pos, noWordSep, joinNonWordChars, camelCasedWords, eatWhitespace, false);
  351. }
  352. /**
  353. * Locates the start of the word at the specified position.
  354. * @param line The text
  355. * @param pos The position
  356. * @param noWordSep Characters that are non-alphanumeric, but
  357. * should be treated as word characters anyway
  358. * @param joinNonWordChars Treat consecutive non-alphanumeric
  359. * characters as one word
  360. * @param camelCasedWords Treat "camelCased" parts as words
  361. * @param eatWhitespace Include whitespace at start of word
  362. * @param eatOnlyAfterWord Eat only whitespace after a word,
  363. * in effect this finds actual word starts even if eating
  364. * @since jEdit 4.4pre1
  365. */
  366. public static int findWordStart(CharSequence line, int pos, String noWordSep,
  367. boolean joinNonWordChars, boolean camelCasedWords,
  368. boolean eatWhitespace, boolean eatOnlyAfterWord)
  369. {
  370. char ch = line.charAt(pos);
  371. if(noWordSep == null)
  372. noWordSep = "";
  373. //{{{ the character under the cursor changes how we behave.
  374. int type = getCharType(ch, noWordSep);
  375. //}}}
  376. for(int i = pos; i >= 0; i--)
  377. {
  378. char lastCh = ch;
  379. ch = line.charAt(i);
  380. switch(type)
  381. {
  382. //{{{ Whitespace...
  383. case WHITESPACE:
  384. // only select other whitespace in this case, unless eating only after words
  385. if(Character.isWhitespace(ch))
  386. break;
  387. // word char or symbol; stop, unless eating only after words
  388. else if (!eatOnlyAfterWord)
  389. {
  390. return i + 1;
  391. }
  392. // we have eaten after-word-whitespace and now continue until word start
  393. else if (Character.isLetterOrDigit(ch) || noWordSep.indexOf(ch) != -1)
  394. {
  395. type = WORD_CHAR;
  396. }
  397. else
  398. type = SYMBOL;
  399. break; //}}}
  400. //{{{ Word character...
  401. case WORD_CHAR:
  402. // stop at next last (in writing direction) upper case char if camel cased
  403. // (don't stop at every upper case char, don't treat noWordSep as word chars)
  404. if (camelCasedWords && Character.isUpperCase(ch) && !Character.isUpperCase(lastCh)
  405. && Character.isLetterOrDigit(lastCh))
  406. {
  407. return i;
  408. }
  409. // stop at next first (in writing direction) upper case char if camel cased
  410. // (don't stop at every upper case char)
  411. else if (camelCasedWords && !Character.isUpperCase(ch) && Character.isUpperCase(lastCh))
  412. {
  413. return i + 1;
  414. }
  415. // word char; keep going
  416. else if(Character.isLetterOrDigit(ch) ||
  417. noWordSep.indexOf(ch) != -1)
  418. {
  419. break;
  420. }
  421. // whitespace; include in word if eating, but not if only eating after word
  422. else if(Character.isWhitespace(ch)
  423. && eatWhitespace && !eatOnlyAfterWord)
  424. {
  425. type = WHITESPACE;
  426. break;
  427. }
  428. else
  429. return i + 1; //}}}
  430. //{{{ Symbol...
  431. case SYMBOL:
  432. if(!joinNonWordChars && pos != i)
  433. return i + 1;
  434. // whitespace; include in word if eating, but not if only eating after word
  435. if(Character.isWhitespace(ch))
  436. {
  437. if(eatWhitespace && !eatOnlyAfterWord)
  438. {
  439. type = WHITESPACE;
  440. break;
  441. }
  442. else
  443. return i + 1;
  444. }
  445. else if(Character.isLetterOrDigit(ch) ||
  446. noWordSep.indexOf(ch) != -1)
  447. {
  448. return i + 1;
  449. }
  450. else
  451. {
  452. break;
  453. } //}}}
  454. }
  455. }
  456. return 0;
  457. } //}}}
  458. //{{{ findWordEnd() methods
  459. /**
  460. * Locates the end of the word at the specified position.
  461. * @param line The text
  462. * @param pos The position
  463. * @param noWordSep Characters that are non-alphanumeric, but
  464. * should be treated as word characters anyway
  465. */
  466. public static int findWordEnd(String line, int pos, String noWordSep)
  467. {
  468. return findWordEnd(line, pos, noWordSep, true);
  469. }
  470. /**
  471. * Locates the end of the word at the specified position.
  472. * @param line The text
  473. * @param pos The position
  474. * @param noWordSep Characters that are non-alphanumeric, but
  475. * should be treated as word characters anyway
  476. * @since jEdit 4.3pre15
  477. */
  478. public static int findWordEnd(CharSequence line,
  479. int pos,
  480. String noWordSep)
  481. {
  482. return findWordEnd(line, pos, noWordSep, true, false, false);
  483. }
  484. /**
  485. * Locates the end of the word at the specified position.
  486. * @param line The text
  487. * @param pos The position
  488. * @param noWordSep Characters that are non-alphanumeric, but
  489. * should be treated as word characters anyway
  490. * @param joinNonWordChars Treat consecutive non-alphanumeric
  491. * characters as one word
  492. * @since jEdit 4.1pre2
  493. */
  494. public static int findWordEnd(String line, int pos, String noWordSep,
  495. boolean joinNonWordChars)
  496. {
  497. return findWordEnd(line,pos,noWordSep,joinNonWordChars,false);
  498. }
  499. /**
  500. * Locates the end of the word at the specified position.
  501. * @param line The text
  502. * @param pos The position
  503. * @param noWordSep Characters that are non-alphanumeric, but
  504. * should be treated as word characters anyway
  505. * @param joinNonWordChars Treat consecutive non-alphanumeric
  506. * characters as one word
  507. * @param eatWhitespace Include whitespace at end of word
  508. * @since jEdit 4.2pre5
  509. */
  510. public static int findWordEnd(String line, int pos, String noWordSep,
  511. boolean joinNonWordChars, boolean eatWhitespace)
  512. {
  513. return findWordEnd(line, pos, noWordSep, joinNonWordChars,
  514. false, eatWhitespace);
  515. }
  516. /**
  517. * Locates the end of the word at the specified position.
  518. * @param line The text
  519. * @param pos The position
  520. * @param noWordSep Characters that are non-alphanumeric, but
  521. * should be treated as word characters anyway
  522. * @param joinNonWordChars Treat consecutive non-alphanumeric
  523. * characters as one word
  524. * @param camelCasedWords Treat "camelCased" parts as words
  525. * @param eatWhitespace Include whitespace at end of word
  526. * @since jEdit 4.3pre10
  527. */
  528. public static int findWordEnd(String line, int pos, String noWordSep,
  529. boolean joinNonWordChars, boolean camelCasedWords,
  530. boolean eatWhitespace)
  531. {
  532. return findWordEnd((CharSequence)line, pos, noWordSep,
  533. joinNonWordChars, camelCasedWords,
  534. eatWhitespace);
  535. }
  536. /**
  537. * Locates the end of the word at the specified position.
  538. * @param line The text
  539. * @param pos The position
  540. * @param noWordSep Characters that are non-alphanumeric, but
  541. * should be treated as word characters anyway
  542. * @param joinNonWordChars Treat consecutive non-alphanumeric
  543. * characters as one word
  544. * @param camelCasedWords Treat "camelCased" parts as words
  545. * @param eatWhitespace Include whitespace at end of word
  546. * @since jEdit 4.3pre15
  547. */
  548. public static int findWordEnd(CharSequence line,
  549. int pos,
  550. String noWordSep,
  551. boolean joinNonWordChars,
  552. boolean camelCasedWords,
  553. boolean eatWhitespace)
  554. {
  555. if(pos != 0)
  556. pos--;
  557. char ch = line.charAt(pos);
  558. if(noWordSep == null)
  559. noWordSep = "";
  560. //{{{ the character under the cursor changes how we behave.
  561. int type = getCharType(ch, noWordSep);
  562. //}}}
  563. for(int i = pos; i < line.length(); i++)
  564. {
  565. char lastCh = ch;
  566. ch = line.charAt(i);
  567. switch(type)
  568. {
  569. //{{{ Whitespace...
  570. case WHITESPACE:
  571. // only select other whitespace in this case
  572. if(Character.isWhitespace(ch))
  573. break;
  574. else
  575. return i; //}}}
  576. //{{{ Word character...
  577. case WORD_CHAR:
  578. // stop at next last upper case char if camel cased
  579. // (don't stop at every upper case char, don't treat noWordSep as word chars)
  580. if (camelCasedWords && i > pos + 1 && !Character.isUpperCase(ch) && Character.isLetterOrDigit(ch)
  581. && Character.isUpperCase(lastCh))
  582. {
  583. return i - 1;
  584. }
  585. // stop at next first upper case char if camel caseg (don't stop at every upper case char)
  586. else if (camelCasedWords && Character.isUpperCase(ch) && !Character.isUpperCase(lastCh))
  587. {
  588. return i;
  589. }
  590. else if(Character.isLetterOrDigit(ch) ||
  591. noWordSep.indexOf(ch) != -1)
  592. {
  593. break;
  594. }
  595. // whitespace; include in word if eating
  596. else if(Character.isWhitespace(ch)
  597. && eatWhitespace)
  598. {
  599. type = WHITESPACE;
  600. break;
  601. }
  602. else
  603. return i; //}}}
  604. //{{{ Symbol...
  605. case SYMBOL:
  606. if(!joinNonWordChars && i != pos)
  607. return i;
  608. // if we see whitespace, set flag.
  609. if(Character.isWhitespace(ch))
  610. {
  611. if(eatWhitespace)
  612. {
  613. type = WHITESPACE;
  614. break;
  615. }
  616. else
  617. return i;
  618. }
  619. else if(Character.isLetterOrDigit(ch) ||
  620. noWordSep.indexOf(ch) != -1)
  621. {
  622. return i;
  623. }
  624. else
  625. {
  626. break;
  627. } //}}}
  628. }
  629. }
  630. return line.length();
  631. } //}}}
  632. //{{{ getCharType() method
  633. /**
  634. * Returns the type of the char.
  635. *
  636. * @param ch the character
  637. * @param noWordSep Characters that are non-alphanumeric, but
  638. * should be treated as word characters anyway, it must not be null
  639. * @return the type of the char : {@link #WHITESPACE},
  640. * {@link #WORD_CHAR}, {@link #SYMBOL}
  641. * @since jEdit 4.4pre1
  642. */
  643. public static int getCharType(char ch, String noWordSep)
  644. {
  645. int type;
  646. if(Character.isWhitespace(ch))
  647. type = WHITESPACE;
  648. else if(Character.isLetterOrDigit(ch)
  649. || noWordSep.indexOf(ch) != -1)
  650. type = WORD_CHAR;
  651. else
  652. type = SYMBOL;
  653. return type;
  654. } //}}}
  655. //{{{ spacesToTabs() method
  656. /**
  657. * Converts consecutive spaces to tabs in the specified string.
  658. * @param in The string
  659. * @param tabSize The tab size
  660. */
  661. public static String spacesToTabs(String in, int tabSize)
  662. {
  663. StringBuilder buf = new StringBuilder();
  664. int width = 0;
  665. int whitespace = 0;
  666. for(int i = 0; i < in.length(); i++)
  667. {
  668. switch(in.charAt(i))
  669. {
  670. case ' ':
  671. whitespace++;
  672. width++;
  673. break;
  674. case '\t':
  675. int tab = tabSize - (width % tabSize);
  676. width += tab;
  677. whitespace += tab;
  678. break;
  679. case '\n':
  680. if(whitespace != 0)
  681. {
  682. buf.append(StandardUtilities
  683. .createWhiteSpace(whitespace,tabSize,
  684. width - whitespace));
  685. }
  686. whitespace = 0;
  687. width = 0;
  688. buf.append('\n');
  689. break;
  690. default:
  691. if(whitespace != 0)
  692. {
  693. buf.append(StandardUtilities
  694. .createWhiteSpace(whitespace,tabSize,
  695. width - whitespace));
  696. whitespace = 0;
  697. }
  698. buf.append(in.charAt(i));
  699. width++;
  700. break;
  701. }
  702. }
  703. if(whitespace != 0)
  704. {
  705. buf.append(StandardUtilities.createWhiteSpace(whitespace,tabSize,
  706. width - whitespace));
  707. }
  708. return buf.toString();
  709. } //}}}
  710. //{{{ tabsToSpaces() method
  711. /**
  712. * Converts tabs to consecutive spaces in the specified string.
  713. * @param in The string
  714. * @param tabSize The tab size
  715. */
  716. public static String tabsToSpaces(String in, int tabSize)
  717. {
  718. StringBuilder buf = new StringBuilder();
  719. int width = 0;
  720. for(int i = 0; i < in.length(); i++)
  721. {
  722. switch(in.charAt(i))
  723. {
  724. case '\t':
  725. int count = tabSize - (width % tabSize);
  726. width += count;
  727. while(--count >= 0)
  728. buf.append(' ');
  729. break;
  730. case '\n':
  731. width = 0;
  732. buf.append(in.charAt(i));
  733. break;
  734. default:
  735. width++;
  736. buf.append(in.charAt(i));
  737. break;
  738. }
  739. }
  740. return buf.toString();
  741. } //}}}
  742. //{{{ format() method
  743. /**
  744. * Formats the specified text by merging and breaking lines to the
  745. * specified width.
  746. * @param text The text
  747. * @param maxLineLength The maximum line length
  748. * @param tabSize The tab size
  749. */
  750. public static String format(String text, int maxLineLength, int tabSize)
  751. {
  752. StringBuilder buf = new StringBuilder();
  753. int index = 0;
  754. for(;;)
  755. {
  756. int newIndex = text.indexOf("\n\n",index);
  757. if(newIndex == -1)
  758. break;
  759. formatParagraph(text.substring(index,newIndex),
  760. maxLineLength,tabSize,buf);
  761. buf.append("\n\n");
  762. index = newIndex + 2;
  763. }
  764. if(index != text.length())
  765. {
  766. formatParagraph(text.substring(index),
  767. maxLineLength,tabSize,buf);
  768. }
  769. return buf.toString();
  770. } //}}}
  771. //{{{ indexIgnoringWhitespace() method
  772. /**
  773. * Inverse of <code>ignoringWhitespaceIndex()</code>.
  774. * @param str a string (not an empty string)
  775. * @param index The index
  776. * @return The number of non-whitespace characters that precede the index.
  777. * @since jEdit 4.3pre2
  778. */
  779. public static int indexIgnoringWhitespace(String str, int index)
  780. {
  781. int j = 0;
  782. for(int i = 0; i < index; i++)
  783. if(!Character.isWhitespace(str.charAt(i))) j++;
  784. return j;
  785. } //}}}
  786. //{{{ ignoringWhitespaceIndex() method
  787. /**
  788. * Inverse of <code>indexIgnoringWhitespace()</code>.
  789. * @param str a string (not an empty string)
  790. * @param index The index
  791. * @return The index into the string where the number of non-whitespace
  792. * characters that precede the index is count.
  793. * @since jEdit 4.3pre2
  794. */
  795. public static int ignoringWhitespaceIndex(String str, int index)
  796. {
  797. int j = 0;
  798. for(int i = 0;;i++)
  799. {
  800. if(!Character.isWhitespace(str.charAt(i))) j++;
  801. if(j > index)
  802. return i;
  803. if(i == str.length() - 1)
  804. return i + 1;
  805. }
  806. } //}}}
  807. //{{{ getStringCase() methods
  808. public static final int MIXED = 0;
  809. public static final int LOWER_CASE = 1;
  810. public static final int UPPER_CASE = 2;
  811. public static final int TITLE_CASE = 3;
  812. /**
  813. * Returns if the specified string is all upper case, all lower case,
  814. * or title case (first letter upper case, rest lower case).
  815. * @param str The string
  816. * @since jEdit 4.4pre1
  817. */
  818. public static int getStringCase(CharSequence str)
  819. {
  820. if(str.length() == 0)
  821. return MIXED;
  822. int state = -1;
  823. char ch = str.charAt(0);
  824. if(Character.isLetter(ch))
  825. {
  826. if(Character.isUpperCase(ch))
  827. state = UPPER_CASE;
  828. else
  829. state = LOWER_CASE;
  830. }
  831. for(int i = 1; i < str.length(); i++)
  832. {
  833. ch = str.charAt(i);
  834. if(!Character.isLetter(ch))
  835. continue;
  836. switch(state)
  837. {
  838. case UPPER_CASE:
  839. if(Character.isLowerCase(ch))
  840. {
  841. if(i == 1)
  842. state = TITLE_CASE;
  843. else
  844. return MIXED;
  845. }
  846. break;
  847. case LOWER_CASE:
  848. case TITLE_CASE:
  849. if(Character.isUpperCase(ch))
  850. return MIXED;
  851. break;
  852. }
  853. }
  854. return state;
  855. }
  856. /**
  857. * Returns if the specified string is all upper case, all lower case,
  858. * or title case (first letter upper case, rest lower case).
  859. * @param str The string
  860. * @since jEdit 4.0pre1
  861. */
  862. public static int getStringCase(String str)
  863. {
  864. return getStringCase((CharSequence) str);
  865. } //}}}
  866. //{{{ toTitleCase() method
  867. /**
  868. * Converts the specified string to title case, by capitalizing the
  869. * first letter.
  870. * @param str The string
  871. * @since jEdit 4.0pre1
  872. */
  873. public static String toTitleCase(String str)
  874. {
  875. if(str.length() == 0)
  876. return str;
  877. else
  878. {
  879. return Character.toUpperCase(str.charAt(0))
  880. + str.substring(1).toLowerCase();
  881. }
  882. } //}}}
  883. //{{{ escapeText() method
  884. /**
  885. * Escapes a given string for use in a java.util.regex pattern.
  886. * @since jEdit 4.5pre1
  887. */
  888. public static String escapeText(String text)
  889. {
  890. // Make sure that every "\E" appearing in the text is escaped, and then
  891. // surround it with the quotation tags \Q and \E.
  892. String result = text.replace("\\E", "\\\\E");
  893. return "\\Q" + result + "\\E";
  894. } //}}}
  895. //{{{ Private members
  896. //{{{ formatParagraph() method
  897. private static void formatParagraph(String text, int maxLineLength,
  898. int tabSize, StringBuilder buf)
  899. {
  900. // align everything to paragraph's leading indent
  901. int leadingWhitespaceCount = StandardUtilities.getLeadingWhiteSpace(text);
  902. String leadingWhitespace = text.substring(0,leadingWhitespaceCount);
  903. int leadingWhitespaceWidth = StandardUtilities.getLeadingWhiteSpaceWidth(text,tabSize);
  904. buf.append(leadingWhitespace);
  905. int lineLength = leadingWhitespaceWidth;
  906. StringTokenizer st = new StringTokenizer(text);
  907. while(st.hasMoreTokens())
  908. {
  909. String word = st.nextToken();
  910. if(lineLength == leadingWhitespaceWidth)
  911. {
  912. // do nothing
  913. }
  914. else if(lineLength + word.length() + 1 > maxLineLength)
  915. {
  916. buf.append('\n');
  917. buf.append(leadingWhitespace);
  918. lineLength = leadingWhitespaceWidth;
  919. }
  920. else
  921. {
  922. buf.append(' ');
  923. lineLength++;
  924. }
  925. buf.append(word);
  926. lineLength += word.length();
  927. }
  928. } //}}}
  929. //{{{ indexIgnoringWhitespace() method
  930. public static void indexIgnoringWhitespace(String text, int maxLineLength,
  931. int tabSize, StringBuffer buf)
  932. {
  933. // align everything to paragraph's leading indent
  934. int leadingWhitespaceCount = StandardUtilities.getLeadingWhiteSpace(text);
  935. String leadingWhitespace = text.substring(0,leadingWhitespaceCount);
  936. int leadingWhitespaceWidth = StandardUtilities.getLeadingWhiteSpaceWidth(text,tabSize);
  937. buf.append(leadingWhitespace);
  938. int lineLength = leadingWhitespaceWidth;
  939. StringTokenizer st = new StringTokenizer(text);
  940. while(st.hasMoreTokens())
  941. {
  942. String word = st.nextToken();
  943. if(lineLength == leadingWhitespaceWidth)
  944. {
  945. // do nothing
  946. }
  947. else if(lineLength + word.length() + 1 > maxLineLength)
  948. {
  949. buf.append('\n');
  950. buf.append(leadingWhitespace);
  951. lineLength = leadingWhitespaceWidth;
  952. }
  953. else
  954. {
  955. buf.append(' ');
  956. lineLength++;
  957. }
  958. buf.append(word);
  959. lineLength += word.length();
  960. }
  961. } //}}}
  962. //}}}
  963. }