1 19 package org.netbeans.modules.java.source.save; 20 21 import com.sun.source.tree.CompilationUnitTree; 22 import com.sun.source.tree.ExpressionTree; 23 import com.sun.source.tree.ImportTree; 24 import com.sun.source.tree.Tree; 25 import com.sun.source.util.SourcePositions; 26 import java.util.ArrayList ; 27 import java.util.EnumSet ; 28 import java.util.HashMap ; 29 import java.util.List ; 30 import java.util.Map ; 31 import org.netbeans.api.java.lexer.JavaTokenId; 32 import static org.netbeans.api.java.lexer.JavaTokenId.*; 33 import org.netbeans.api.java.source.WorkingCopy; 34 import org.netbeans.api.lexer.Token; 35 import org.netbeans.api.lexer.TokenSequence; 36 import org.netbeans.modules.java.source.save.TreeDiff.LineInsertionType; 37 38 46 abstract class PositionEstimator { 47 48 final List <? extends Tree> oldL; 49 final List <? extends Tree> newL; 50 final WorkingCopy copy; 51 boolean initialized; 52 final TokenSequence<JavaTokenId> seq; 53 54 PositionEstimator(final List <? extends Tree> oldL, final List <? extends Tree> newL, final WorkingCopy copy) { 55 this.oldL = oldL; 56 this.newL = newL; 57 this.copy = copy; 58 this.seq = copy != null ? copy.getTokenHierarchy().tokenSequence() : null; 59 initialized = false; 60 } 61 62 int[][] matrix; 63 64 67 protected abstract void initialize(); 68 69 75 public abstract int getInsertPos(int index); 76 77 84 public abstract int[] getPositions(int index); 85 86 96 abstract int prepare(final int startPos, StringBuilder aHead, StringBuilder aTail); 97 98 107 public abstract int[] sectionRemovalBounds(StringBuilder replacement); 108 109 112 public LineInsertionType lineInsertType() { 113 return LineInsertionType.NONE; 114 } 115 116 public abstract String head(); 117 public abstract String sep(); 118 public abstract String getIndentString(); 119 120 public int[][] getMatrix() { 122 if (!initialized) initialize(); 123 return matrix; 124 } 125 126 static class ImplementsEstimator extends BaseEstimator { 129 ImplementsEstimator(List <? extends Tree> oldL, 130 List <? extends Tree> newL, 131 WorkingCopy copy) 132 { 133 super(IMPLEMENTS, oldL, newL, copy); 134 } 135 } 136 137 static class ExtendsEstimator extends BaseEstimator { 138 ExtendsEstimator(List <? extends Tree> oldL, 139 List <? extends Tree> newL, 140 WorkingCopy copy) 141 { 142 super(EXTENDS, oldL, newL, copy); 143 } 144 } 145 146 static class ThrowsEstimator extends BaseEstimator { 147 ThrowsEstimator(List <? extends ExpressionTree> oldL, 148 List <? extends ExpressionTree> newL, 149 WorkingCopy copy) 150 { 151 super(THROWS, oldL, newL, copy); 152 } 153 } 154 155 159 static class ImportsEstimator extends PositionEstimator { 160 161 public ImportsEstimator(final List <? extends ImportTree> oldL, 162 final List <? extends ImportTree> newL, 163 final WorkingCopy copy) 164 { 165 super(oldL, newL, copy); 166 } 167 168 List <int[]> data; 169 170 @Override () 171 public void initialize() { 172 int size = oldL.size(); 173 data = new ArrayList (size); 174 SourcePositions positions = copy.getTrees().getSourcePositions(); 175 CompilationUnitTree compilationUnit = copy.getCompilationUnit(); 176 177 for (Tree item : oldL) { 178 int treeStart = (int) positions.getStartPosition(compilationUnit, item); 179 int treeEnd = (int) positions.getEndPosition(compilationUnit, item); 180 181 seq.move(treeStart); 182 seq.moveNext(); 183 int wideStart = goAfterLastNewLine(seq); 184 seq.move(treeStart); 185 seq.moveNext(); 186 if (null != moveToSrcRelevant(seq, Direction.BACKWARD)) { 187 seq.moveNext(); 188 } 189 int previousEnd = seq.offset(); 190 Token<JavaTokenId> token; 191 while (nonRelevant.contains((token = seq.token()).id())) { 192 int localResult = -1; 193 switch (token.id()) { 194 case WHITESPACE: 195 int indexOf = token.text().toString().indexOf('\n'); 196 if (indexOf > 0) { 197 localResult = seq.offset() + indexOf + 1; 198 } 199 break; 200 case LINE_COMMENT: 201 previousEnd = seq.offset() + token.text().length(); 202 break; 203 } 204 if (localResult > 0) { 205 previousEnd = localResult; 206 break; 207 } 208 if (!seq.moveNext()) break; 209 } 210 seq.move(treeEnd); 211 int wideEnd = treeEnd; 212 while (seq.moveNext() && nonRelevant.contains((token = seq.token()).id())) { 213 if (JavaTokenId.WHITESPACE == token.id()) { 214 int indexOf = token.text().toString().indexOf('\n'); 215 if (indexOf > -1) { 216 wideEnd = seq.offset() + indexOf + 1; 217 } else { 218 wideEnd = seq.offset(); 219 } 220 } else if (JavaTokenId.LINE_COMMENT == token.id()) { 221 wideEnd = seq.offset() + token.text().length(); 222 break; 223 } else if (JavaTokenId.JAVADOC_COMMENT == token.id()) { 224 break; 225 } 226 } 227 if (wideEnd < treeEnd) wideEnd = treeEnd; 228 data.add(new int[] { wideStart, wideEnd, previousEnd }); 229 } 230 initialized = true; 231 } 232 233 @Override () 234 public int getInsertPos(int index) { 235 if (!initialized) initialize(); 236 if (data.isEmpty()) { 237 return -1; 238 } else { 239 return index == data.size() ? data.get(index-1)[2] : data.get(index)[0]; 240 } 241 } 242 243 @Override () 246 public int prepare(final int startPos, StringBuilder aHead, StringBuilder aTail) { 247 if (!initialized) initialize(); 248 CompilationUnitTree cut = copy.getCompilationUnit(); 249 int resultPos = 0; 250 if (cut.getTypeDecls().isEmpty()) { 251 } else { 252 Tree t = cut.getTypeDecls().get(0); 253 SourcePositions positions = copy.getTrees().getSourcePositions(); 254 int typeDeclStart = (int) positions.getStartPosition(cut, t); 255 seq.move(typeDeclStart); 256 moveToSrcRelevant(seq, Direction.BACKWARD); 257 resultPos = seq.offset() + seq.token().length(); 258 } 259 int counter = 0; 260 while (seq.moveNext() && nonRelevant.contains(seq.token().id()) && counter < 3) { 261 if (JavaTokenId.WHITESPACE == seq.token().id()) { 262 String white = seq.token().text().toString(); 263 int index = 0, pos = 0; 264 while ((pos = white.indexOf('\n', pos)) > -1) { 265 ++counter; 266 ++pos; 267 if (counter < 3) { 268 index = pos; 269 } 270 } 271 resultPos += index; 272 } else if (JavaTokenId.LINE_COMMENT == seq.token().id()) { 273 ++counter; 274 resultPos += seq.token().text().toString().length(); 275 } else if (JavaTokenId.BLOCK_COMMENT == seq.token().id() || 276 JavaTokenId.JAVADOC_COMMENT == seq.token().id()) { 277 break; 279 } 280 } 281 if (counter < 3) { 282 if (counter == 0) { 283 aHead.append("\n\n"); 284 } else if (counter == 1) { 285 aHead.append('\n'); 286 } 287 aTail.append('\n'); 288 } 289 return resultPos; 290 } 291 292 @Override () 293 public int[] getPositions(int index) { 294 if (!initialized) initialize(); 295 return data.get(index); 296 } 297 298 public LineInsertionType lineInsertType() { 299 return LineInsertionType.AFTER; 300 } 301 302 @Override () 303 public String head() { 304 throw new UnsupportedOperationException ("Not applicable for imports!"); 305 } 306 307 @Override () 308 public String sep() { 309 throw new UnsupportedOperationException ("Not applicable for imports!"); 310 } 311 312 @Override () 313 public String getIndentString() { 314 throw new UnsupportedOperationException ("Not applicable for imports!"); 315 } 316 317 @Override 318 public String toString() { 319 String result = ""; 320 for (int i = 0; i < data.size(); i++) { 321 int[] pos = data.get(i); 322 String s = copy.getText().substring(pos[0], pos[1]); 323 result += "\"" + s + "\"\n"; 324 } 325 return result; 326 } 327 328 331 public int[] sectionRemovalBounds(StringBuilder replacement) { 332 assert !oldL.isEmpty() && newL.isEmpty(); SourcePositions positions = copy.getTrees().getSourcePositions(); 335 CompilationUnitTree compilationUnit = copy.getCompilationUnit(); 336 int sectionStart = (int) positions.getStartPosition(compilationUnit, oldL.get(0)); 337 int sectionEnd = (int) positions.getEndPosition(compilationUnit, oldL.get(oldL.size()-1)); 338 340 seq.move(sectionStart); 341 seq.moveNext(); 342 Token<JavaTokenId> token; 343 while (seq.movePrevious() && nonRelevant.contains((token = seq.token()).id())) { 344 if (JavaTokenId.LINE_COMMENT == token.id()) { 345 seq.moveNext(); 346 sectionStart = seq.offset(); 347 break; 348 } else if (JavaTokenId.BLOCK_COMMENT == token.id() || JavaTokenId.JAVADOC_COMMENT == token.id()) { 349 break; 350 } else if (JavaTokenId.WHITESPACE == token.id()) { 351 int indexOf = token.text().toString().indexOf('\n'); 352 if (indexOf > -1) { 353 sectionStart = seq.offset() + indexOf + 1; 354 } else { 355 sectionStart = seq.offset(); 356 } 357 } 358 } 359 seq.move(sectionEnd); 360 seq.movePrevious(); 361 while (seq.moveNext() && nonRelevant.contains((token = seq.token()).id())) { 362 if (JavaTokenId.LINE_COMMENT == token.id()) { 363 sectionEnd = seq.offset(); 364 if (seq.moveNext()) { 365 sectionEnd = seq.offset(); 366 } 367 break; 368 } else if (JavaTokenId.BLOCK_COMMENT == token.id() || JavaTokenId.JAVADOC_COMMENT == token.id()) { 369 break; 370 } else if (JavaTokenId.WHITESPACE == token.id()) { 371 int indexOf = token.text().toString().lastIndexOf('\n'); 372 if (indexOf > -1) { 373 sectionEnd = seq.offset() + indexOf + 1; 374 } else { 375 sectionEnd += seq.offset() + token.text().length(); 376 } 377 } 378 } 379 return new int[] { sectionStart, sectionEnd }; 380 } 381 } 382 383 386 static class MembersEstimator extends PositionEstimator { 387 388 public MembersEstimator(final List <? extends Tree> oldL, 389 final List <? extends Tree> newL, 390 final WorkingCopy copy) 391 { 392 super(oldL, newL, copy); 393 } 394 395 public void initialize() { 396 int size = oldL.size(); 397 matrix = new int[size+1][5]; 398 matrix[size] = new int[] { -1, -1, -1, -1, -1 }; 399 SourcePositions positions = copy.getTrees().getSourcePositions(); 400 CompilationUnitTree compilationUnit = copy.getCompilationUnit(); 401 int i = 0; 402 403 for (Tree item : oldL) { 404 int treeStart = (int) positions.getStartPosition(compilationUnit, item); 405 int treeEnd = (int) positions.getEndPosition(compilationUnit, item); 406 if (treeEnd < 0) { 407 if (Tree.Kind.BLOCK == item.getKind()) { 408 if (i > 0) { 413 seq.moveIndex(matrix[i-1][4]); 414 seq.moveNext(); 415 TokenUtilities.moveFwdToToken(seq, seq.offset(), JavaTokenId.SEMICOLON); 416 treeStart = seq.offset(); 417 treeEnd = treeStart + 1; 418 } else { 419 } 421 } else { 422 continue; 428 } 429 } 430 431 seq.move(treeStart); 432 seq.moveNext(); 433 int startIndex = seq.index(); 434 moveToSrcRelevant(seq, Direction.BACKWARD); 437 seq.moveNext(); 438 int veryBeg = seq.index(); 439 seq.move(treeEnd); 440 matrix[i++] = new int[] { veryBeg, veryBeg, veryBeg, startIndex, seq.index() }; 441 if (i == size) { 442 seq.move(treeEnd); 443 matrix[i][2] = seq.index(); 444 } 445 } 446 initialized = true; 447 } 448 449 @Override () 450 public int getInsertPos(int index) { 451 if (!initialized) initialize(); 452 int tokenIndex = matrix[index][2]; 453 if (tokenIndex == -1) return -1; 457 seq.moveIndex(tokenIndex); 458 seq.moveNext(); 459 return goAfterFirstNewLine(seq); 460 } 461 462 public String head() { return ""; } 463 464 public String sep() { return ""; } 465 466 public String getIndentString() { return ""; } 467 468 public int[] getPositions(int index) { 469 if (!initialized) initialize(); 470 int begin = getInsertPos(index); 471 if (matrix[index][4] != -1) { 472 seq.moveIndex(matrix[index][4]); 473 seq.moveNext(); 474 } 475 int end = goAfterFirstNewLine(seq); 476 return new int [] { begin, end }; 477 } 478 479 public LineInsertionType lineInsertType() { 480 return LineInsertionType.AFTER; 481 } 482 483 public int prepare(int startPos, StringBuilder aHead, 484 StringBuilder aTail) { 485 return startPos; 486 } 487 488 public int[] sectionRemovalBounds(StringBuilder replacement) { 489 throw new UnsupportedOperationException ("Not supported yet."); 490 } 491 492 } 493 494 497 static class TopLevelEstimator extends PositionEstimator { 498 499 public TopLevelEstimator(List <? extends Tree> oldL, 500 List <? extends Tree> newL, 501 WorkingCopy copy) 502 { 503 super(oldL, newL, copy); 504 } 505 506 public void initialize() { 507 int size = oldL.size(); 508 matrix = new int[size+1][5]; 509 matrix[size] = new int[] { -1, -1, -1, -1, -1 }; 510 SourcePositions positions = copy.getTrees().getSourcePositions(); 511 CompilationUnitTree compilationUnit = copy.getCompilationUnit(); 512 int i = 0; 513 514 for (Tree item : oldL) { 515 int treeStart = (int) positions.getStartPosition(compilationUnit, item); 516 int treeEnd = (int) positions.getEndPosition(compilationUnit, item); 517 if (treeEnd < 0) continue; 521 522 seq.move(treeStart); 523 int startIndex = seq.index(); 524 moveToSrcRelevant(seq, Direction.BACKWARD); 527 seq.moveNext(); 528 int veryBeg = seq.index(); 529 seq.move(treeEnd); 530 matrix[i++] = new int[] { veryBeg, veryBeg, veryBeg, startIndex, seq.index() }; 531 if (i == size) { 532 seq.move(treeEnd); 533 matrix[i][2] = seq.index(); 534 } 535 } 536 } 537 538 @Override () 539 public int getInsertPos(int index) { 540 if (!initialized) initialize(); 541 int tokenIndex = matrix[index][2]; 542 if (tokenIndex == -1) return -1; 546 seq.moveIndex(tokenIndex); 547 seq.moveNext(); 548 int off = goAfterFirstNewLine(seq); 549 return off; 550 } 551 552 public String head() { return ""; } 553 554 public String sep() { return ""; } 555 556 public String getIndentString() { return ""; } 557 558 public int[] getPositions(int index) { 559 if (!initialized) initialize(); 560 int begin = getInsertPos(index); 561 if (matrix[index][4] != -1) { 562 seq.moveIndex(matrix[index][4]); 563 seq.moveNext(); 564 } 565 int end = goAfterFirstNewLine(seq); 566 return new int [] { begin, end }; 567 } 568 569 public LineInsertionType lineInsertType() { 570 return LineInsertionType.AFTER; 571 } 572 573 public int prepare(int startPos, StringBuilder aHead, 574 StringBuilder aTail) { 575 return startPos; 576 } 577 578 public int[] sectionRemovalBounds(StringBuilder replacement) { 579 throw new UnsupportedOperationException ("Not supported yet."); 580 } 581 582 } 583 584 private static abstract class BaseEstimator extends PositionEstimator { 585 586 JavaTokenId precToken; 587 private ArrayList <String > separatorList; 588 589 private BaseEstimator(JavaTokenId precToken, 590 List <? extends Tree> oldL, 591 List <? extends Tree> newL, 592 WorkingCopy copy) 593 { 594 super(oldL, newL, copy); 595 this.precToken = precToken; 596 } 597 598 public String head() { return " " + precToken.fixedText() + " "; } 599 public String sep() { return ", "; } 600 601 public void initialize() { 602 separatorList = new ArrayList <String >(oldL.size()); 603 boolean first = true; 604 int size = oldL.size(); 605 matrix = new int[size+1][5]; 606 matrix[size] = new int[] { -1, -1, -1, -1, -1 }; 607 TokenSequence<JavaTokenId> seq = copy.getTokenHierarchy().tokenSequence(); 608 int i = 0; 609 SourcePositions positions = copy.getTrees().getSourcePositions(); 610 CompilationUnitTree compilationUnit = copy.getCompilationUnit(); 611 for (Tree item : oldL) { 612 String separatedText = ""; 613 int treeStart = (int) positions.getStartPosition(compilationUnit, item); 614 int treeEnd = (int) positions.getEndPosition(compilationUnit, item); 615 seq.move(treeStart); 616 int startIndex = seq.index(); 617 int beforer = -1; 618 if (first) { 619 while (seq.movePrevious() && seq.token().id() != precToken) ; 621 int throwsIndex = seq.index(); 622 beforer = throwsIndex+1; 623 moveToSrcRelevant(seq, Direction.BACKWARD); 625 seq.moveNext(); 626 int beg = seq.index(); 627 seq.move(treeEnd); 628 matrix[i++] = new int[] { beg, throwsIndex, beforer, startIndex, seq.index() }; 629 first = false; 630 } else { 631 int afterPrevious = matrix[i-1][4]; 632 while (seq.movePrevious() && (seq.token().id() != COMMA)) 634 if (seq.token().id() == WHITESPACE) 635 separatedText = seq.token().text() + separatedText; 636 else if (seq.token().id() == LINE_COMMENT) 637 separatedText = '\n' + separatedText; 638 separatorList.add(separatedText); 639 int separator = seq.index(); 640 int afterSeparator = separator + 1; if (afterPrevious == separator) { 642 afterPrevious = -1; 643 } 644 seq.move(treeEnd); 645 matrix[i++] = new int[] { afterPrevious, separator, afterSeparator, startIndex, seq.index() }; 646 } 647 if (i == size) { 648 moveToSrcRelevant(seq, Direction.FORWARD); 650 matrix[i][2] = seq.index(); 651 } 652 seq.move(treeEnd); 653 } 654 initialized = true; 655 } 656 657 public String getIndentString() { 658 if (!initialized) initialize(); 659 Map <String , Integer > map = new HashMap <String , Integer >(); 660 for (String item : separatorList) { 661 String s = item; 662 if (s.lastIndexOf("\n") > -1) { 663 s = s.substring(item.lastIndexOf("\n")); 664 } 665 Integer count = map.get(s); 666 if (count != null) { 667 map.put(s, count++); 668 } else { 669 map.put(s, 1); 670 } 671 } 672 int max = -1; 673 String s = null; 674 for (String item : map.keySet()) { 675 if (map.get(item) > max) { 676 s = item; 677 max = map.get(item); 678 } 679 } 680 return s; 681 } 682 683 public int getInsertPos(int index) { 684 throw new UnsupportedOperationException ("Not supported yet."); 685 } 686 687 public int[] getPositions(int index) { 688 throw new UnsupportedOperationException ("Not supported yet."); 689 } 690 691 public int prepare(int startPos, StringBuilder aHead, 692 StringBuilder aTail) { 693 throw new UnsupportedOperationException ("Not supported yet."); 694 } 695 696 public int[] sectionRemovalBounds(StringBuilder replacement) { 697 throw new UnsupportedOperationException ("Not supported yet."); 698 } 699 700 } 701 702 public void tablePrint(int[][] matrix, TokenSequence seq) { 704 for (int i = 0; i < matrix.length; i++) { 705 for (int j = 0; j < matrix[i].length; j++) { 706 int item = matrix[i][j]; 707 String s = "(nothing)"; 708 if (item > -1) { 709 seq.moveIndex(item); 710 seq.moveNext(); 711 s = "'" + seq.token().text(); 712 } 713 s += "' "; 714 System.err.print(item + "\t"); 716 } 717 System.err.println(""); 718 } 719 } 720 721 724 736 protected static JavaTokenId moveToSrcRelevant(TokenSequence<JavaTokenId> seq, 737 Direction dir) 738 { 739 return moveToDifferentThan(seq, dir, nonRelevant); 740 } 741 742 private static JavaTokenId moveToDifferentThan( 743 TokenSequence<JavaTokenId> seq, 744 Direction dir, 745 EnumSet <JavaTokenId> set) 746 { 747 boolean notBound = false; 748 switch (dir) { 749 case BACKWARD: 750 while ((notBound = seq.movePrevious()) && set.contains(seq.token().id())) ; 751 break; 752 case FORWARD: 753 while ((notBound = seq.moveNext()) && set.contains(seq.token().id())) ; 754 break; 755 } 756 return notBound ? seq.token().id() : null; 757 } 758 759 private static int goAfterFirstNewLine(final TokenSequence<JavaTokenId> seq) { 760 if (seq.token() == null) 763 seq.movePrevious(); 764 765 int base = seq.offset(); 766 seq.movePrevious(); 767 while (seq.moveNext() && nonRelevant.contains(seq.token().id())) { 768 switch (seq.token().id()) { 769 case LINE_COMMENT: 770 seq.moveNext(); 771 return seq.offset(); 772 case WHITESPACE: 773 char[] c = seq.token().text().toString().toCharArray(); 774 int index = 0; 775 while (index < c.length) { 776 if (c[index++] == '\n') { 777 while (index < c.length) 778 if (c[index] != ' ' && c[index] != '\t') 779 break; 780 else 781 ++index; 782 return base + index; 783 } 784 } 785 } 786 } 787 return base; 788 } 789 790 private static int goAfterLastNewLine(final TokenSequence<JavaTokenId> seq) { 791 int base = seq.offset(); 792 seq.movePrevious(); 793 while (seq.moveNext() && nonRelevant.contains(seq.token().id())) ; 794 795 while (seq.movePrevious() && nonRelevant.contains(seq.token().id())) { 796 switch (seq.token().id()) { 797 case LINE_COMMENT: 798 seq.moveNext(); 799 return seq.offset(); 800 case WHITESPACE: 801 char[] c = seq.token().text().toString().toCharArray(); 802 for (int i = c.length; i > 0; ) { 803 if (c[--i] == '\n') { 804 return seq.offset() + i + 1; 805 } 806 } 807 } 808 } 809 if ((seq.index() == 0 || seq.moveNext()) && nonRelevant.contains(seq.token().id())) { 810 return seq.offset(); 811 } 812 return base; 813 } 814 815 820 static final EnumSet <JavaTokenId> nonRelevant = EnumSet.<JavaTokenId>of( 821 LINE_COMMENT, 822 BLOCK_COMMENT, 823 JAVADOC_COMMENT, 824 WHITESPACE 825 ); 826 827 830 protected enum Direction { 831 FORWARD, BACKWARD; 832 } 833 } 834 | Popular Tags |