1 11 package org.eclipse.jdt.internal.core.dom.rewrite; 12 13 import java.util.ArrayList ; 14 import java.util.Collection ; 15 import java.util.Map ; 16 17 import org.eclipse.core.runtime.Assert; 18 19 import org.eclipse.jdt.core.JavaCore; 20 import org.eclipse.jdt.core.ToolFactory; 21 import org.eclipse.jdt.core.dom.ASTNode; 22 import org.eclipse.jdt.core.dom.Block; 23 import org.eclipse.jdt.core.dom.BodyDeclaration; 24 import org.eclipse.jdt.core.dom.Expression; 25 import org.eclipse.jdt.core.dom.Statement; 26 import org.eclipse.jdt.core.formatter.CodeFormatter; 27 import org.eclipse.jdt.core.formatter.IndentManipulation; 28 import org.eclipse.jface.text.BadLocationException; 29 import org.eclipse.jface.text.BadPositionCategoryException; 30 import org.eclipse.jface.text.DefaultPositionUpdater; 31 import org.eclipse.jface.text.Document; 32 import org.eclipse.jface.text.Position; 33 import org.eclipse.text.edits.DeleteEdit; 34 import org.eclipse.text.edits.InsertEdit; 35 import org.eclipse.text.edits.MultiTextEdit; 36 import org.eclipse.text.edits.ReplaceEdit; 37 import org.eclipse.text.edits.TextEdit; 38 39 final class ASTRewriteFormatter { 40 41 public static class NodeMarker extends Position { 42 public Object data; 43 } 44 45 private class ExtendedFlattener extends ASTRewriteFlattener { 46 47 private ArrayList positions; 48 49 public ExtendedFlattener(RewriteEventStore store) { 50 super(store); 51 this.positions= new ArrayList (); 52 } 53 54 57 public void preVisit(ASTNode node) { 58 Object trackData= getEventStore().getTrackedNodeData(node); 59 if (trackData != null) { 60 addMarker(trackData, this.result.length(), 0); 61 } 62 Object placeholderData= getPlaceholders().getPlaceholderData(node); 63 if (placeholderData != null) { 64 addMarker(placeholderData, this.result.length(), 0); 65 } 66 } 67 68 71 public void postVisit(ASTNode node) { 72 Object placeholderData= getPlaceholders().getPlaceholderData(node); 73 if (placeholderData != null) { 74 fixupLength(placeholderData, this.result.length()); 75 } 76 Object trackData= getEventStore().getTrackedNodeData(node); 77 if (trackData != null) { 78 fixupLength(trackData, this.result.length()); 79 } 80 } 81 82 85 public boolean visit(Block node) { 86 if (getPlaceholders().isCollapsed(node)) { 87 visitList(node, Block.STATEMENTS_PROPERTY, null); 88 return false; 89 } 90 return super.visit(node); 91 } 92 93 private NodeMarker addMarker(Object annotation, int startOffset, int length) { 94 NodeMarker marker= new NodeMarker(); 95 marker.offset= startOffset; 96 marker.length= length; 97 marker.data= annotation; 98 this.positions.add(marker); 99 return marker; 100 } 101 102 private void fixupLength(Object data, int endOffset) { 103 for (int i= this.positions.size()-1; i >= 0 ; i--) { 104 NodeMarker marker= (NodeMarker) this.positions.get(i); 105 if (marker.data == data) { 106 marker.length= endOffset - marker.offset; 107 return; 108 } 109 } 110 } 111 112 public NodeMarker[] getMarkers() { 113 return (NodeMarker[]) this.positions.toArray(new NodeMarker[this.positions.size()]); 114 } 115 } 116 117 private final String lineDelimiter; 118 private final int tabWidth; 119 private final int indentWidth; 120 121 private final NodeInfoStore placeholders; 122 private final RewriteEventStore eventStore; 123 124 private final Map options; 125 126 127 public ASTRewriteFormatter(NodeInfoStore placeholders, RewriteEventStore eventStore, Map options, String lineDelimiter) { 128 this.placeholders= placeholders; 129 this.eventStore= eventStore; 130 131 if (options == null) { 132 options= JavaCore.getOptions(); 133 } 134 136 this.options= options; 137 this.lineDelimiter= lineDelimiter; 138 139 this.tabWidth= IndentManipulation.getTabWidth(options); 140 this.indentWidth= IndentManipulation.getIndentWidth(options); 141 } 142 143 144 145 public NodeInfoStore getPlaceholders() { 146 return this.placeholders; 147 } 148 149 public RewriteEventStore getEventStore() { 150 return this.eventStore; 151 } 152 153 public int getTabWidth() { 154 return this.tabWidth; 155 } 156 157 public int getIndentWidth() { 158 return this.indentWidth; 159 } 160 161 public String getLineDelimiter() { 162 return this.lineDelimiter; 163 } 164 165 174 public String getFormattedResult(ASTNode node, int initialIndentationLevel, Collection resultingMarkers) { 175 176 ExtendedFlattener flattener= new ExtendedFlattener(this.eventStore); 177 node.accept(flattener); 178 179 NodeMarker[] markers= flattener.getMarkers(); 180 for (int i= 0; i < markers.length; i++) { 181 resultingMarkers.add(markers[i]); } 183 184 String unformatted= flattener.getResult(); 185 TextEdit edit= formatNode(node, unformatted, initialIndentationLevel); 186 if (edit == null) { 187 if (initialIndentationLevel > 0) { 188 String indentString = createIndentString(initialIndentationLevel); 190 ReplaceEdit[] edits = IndentManipulation.getChangeIndentEdits(unformatted, 0, this.tabWidth, this.indentWidth, indentString); 191 edit= new MultiTextEdit(); 192 edit.addChild(new InsertEdit(0, indentString)); 193 edit.addChildren(edits); 194 } else { 195 return unformatted; 196 } 197 } 198 return evaluateFormatterEdit(unformatted, edit, markers); 199 } 200 201 public String createIndentString(int indentationUnits) { 202 return ToolFactory.createCodeFormatter(this.options).createIndentationString(indentationUnits); 203 } 204 205 public String getIndentString(String currentLine) { 206 return IndentManipulation.extractIndentString(currentLine, this.tabWidth, this.indentWidth); 207 } 208 209 public String changeIndent(String code, int codeIndentLevel, String newIndent) { 210 return IndentManipulation.changeIndent(code, codeIndentLevel, this.tabWidth, this.indentWidth, newIndent, this.lineDelimiter); 211 } 212 213 public int computeIndentUnits(String line) { 214 return IndentManipulation.measureIndentUnits(line, this.tabWidth, this.indentWidth); 215 } 216 217 226 public static String evaluateFormatterEdit(String string, TextEdit edit, Position[] positions) { 227 try { 228 Document doc= createDocument(string, positions); 229 edit.apply(doc, 0); 230 if (positions != null) { 231 for (int i= 0; i < positions.length; i++) { 232 Assert.isTrue(!positions[i].isDeleted, "Position got deleted"); } 234 } 235 return doc.get(); 236 } catch (BadLocationException e) { 237 Assert.isTrue(false, "Fromatter created edits with wrong positions: " + e.getMessage()); } 240 return null; 241 } 242 243 public TextEdit formatString(int kind, String string, int offset, int length, int indentationLevel) { 244 return ToolFactory.createCodeFormatter(this.options).format(kind, string, offset, length, indentationLevel, this.lineDelimiter); 245 } 246 247 256 private TextEdit formatNode(ASTNode node, String str, int indentationLevel) { 257 int code; 258 String prefix= ""; String suffix= ""; if (node instanceof Statement) { 261 code= CodeFormatter.K_STATEMENTS; 262 if (node.getNodeType() == ASTNode.SWITCH_CASE) { 263 prefix= "switch(1) {"; suffix= "}"; code= CodeFormatter.K_STATEMENTS; 266 } 267 } else if (node instanceof Expression && node.getNodeType() != ASTNode.VARIABLE_DECLARATION_EXPRESSION) { 268 code= CodeFormatter.K_EXPRESSION; 269 } else if (node instanceof BodyDeclaration) { 270 code= CodeFormatter.K_CLASS_BODY_DECLARATIONS; 271 } else { 272 switch (node.getNodeType()) { 273 case ASTNode.ARRAY_TYPE: 274 case ASTNode.PARAMETERIZED_TYPE: 275 case ASTNode.PRIMITIVE_TYPE: 276 case ASTNode.QUALIFIED_TYPE: 277 case ASTNode.SIMPLE_TYPE: 278 suffix= " x;"; code= CodeFormatter.K_CLASS_BODY_DECLARATIONS; 280 break; 281 case ASTNode.WILDCARD_TYPE: 282 prefix= "A<"; suffix= "> x;"; code= CodeFormatter.K_CLASS_BODY_DECLARATIONS; 285 break; 286 case ASTNode.COMPILATION_UNIT: 287 code= CodeFormatter.K_COMPILATION_UNIT; 288 break; 289 case ASTNode.VARIABLE_DECLARATION_EXPRESSION: 290 case ASTNode.SINGLE_VARIABLE_DECLARATION: 291 suffix= ";"; code= CodeFormatter.K_STATEMENTS; 293 break; 294 case ASTNode.VARIABLE_DECLARATION_FRAGMENT: 295 prefix= "A "; suffix= ";"; code= CodeFormatter.K_STATEMENTS; 298 break; 299 case ASTNode.PACKAGE_DECLARATION: 300 case ASTNode.IMPORT_DECLARATION: 301 suffix= "\nclass A {}"; code= CodeFormatter.K_COMPILATION_UNIT; 303 break; 304 case ASTNode.JAVADOC: 305 suffix= "\nclass A {}"; code= CodeFormatter.K_COMPILATION_UNIT; 307 break; 308 case ASTNode.CATCH_CLAUSE: 309 prefix= "try {}"; code= CodeFormatter.K_STATEMENTS; 311 break; 312 case ASTNode.ANONYMOUS_CLASS_DECLARATION: 313 prefix= "new A()"; suffix= ";"; code= CodeFormatter.K_STATEMENTS; 316 break; 317 case ASTNode.MEMBER_VALUE_PAIR: 318 prefix= "@Author("; suffix= ") class x {}"; code= CodeFormatter.K_COMPILATION_UNIT; 321 break; 322 case ASTNode.MODIFIER: 323 suffix= " class x {}"; code= CodeFormatter.K_COMPILATION_UNIT; 325 break; 326 case ASTNode.TYPE_PARAMETER: 327 prefix= "class X<"; suffix= "> {}"; code= CodeFormatter.K_COMPILATION_UNIT; 330 break; 331 case ASTNode.MEMBER_REF: 332 case ASTNode.METHOD_REF: 333 case ASTNode.METHOD_REF_PARAMETER: 334 case ASTNode.TAG_ELEMENT: 335 case ASTNode.TEXT_ELEMENT: 336 return null; 338 339 default: 358 return null; 360 } 361 } 362 363 String concatStr= prefix + str + suffix; 364 TextEdit edit= formatString(code, concatStr, prefix.length(), str.length(), indentationLevel); 365 366 if (prefix.length() > 0) { 367 edit= shifEdit(edit, prefix.length()); 368 } 369 return edit; 370 } 371 372 private static TextEdit shifEdit(TextEdit oldEdit, int diff) { 373 TextEdit newEdit; 374 if (oldEdit instanceof ReplaceEdit) { 375 ReplaceEdit edit= (ReplaceEdit) oldEdit; 376 newEdit= new ReplaceEdit(edit.getOffset() - diff, edit.getLength(), edit.getText()); 377 } else if (oldEdit instanceof InsertEdit) { 378 InsertEdit edit= (InsertEdit) oldEdit; 379 newEdit= new InsertEdit(edit.getOffset() - diff, edit.getText()); 380 } else if (oldEdit instanceof DeleteEdit) { 381 DeleteEdit edit= (DeleteEdit) oldEdit; 382 newEdit= new DeleteEdit(edit.getOffset() - diff, edit.getLength()); 383 } else if (oldEdit instanceof MultiTextEdit) { 384 newEdit= new MultiTextEdit(); 385 } else { 386 return null; } 388 TextEdit[] children= oldEdit.getChildren(); 389 for (int i= 0; i < children.length; i++) { 390 TextEdit shifted= shifEdit(children[i], diff); 391 if (shifted != null) { 392 newEdit.addChild(shifted); 393 } 394 } 395 return newEdit; 396 } 397 398 private static Document createDocument(String string, Position[] positions) throws IllegalArgumentException { 399 Document doc= new Document(string); 400 try { 401 if (positions != null) { 402 final String POS_CATEGORY= "myCategory"; 404 doc.addPositionCategory(POS_CATEGORY); 405 doc.addPositionUpdater(new DefaultPositionUpdater(POS_CATEGORY) { 406 protected boolean notDeleted() { 407 int start= this.fOffset; 408 int end= start + this.fLength; 409 if (start < this.fPosition.offset && (this.fPosition.offset + this.fPosition.length < end)) { 410 this.fPosition.offset= end; return false; 412 } 413 return true; 414 } 415 }); 416 for (int i= 0; i < positions.length; i++) { 417 try { 418 doc.addPosition(POS_CATEGORY, positions[i]); 419 } catch (BadLocationException e) { 420 throw new IllegalArgumentException ("Position outside of string. offset: " + positions[i].offset + ", length: " + positions[i].length + ", string size: " + string.length()); } 422 } 423 } 424 } catch (BadPositionCategoryException cannotHappen) { 425 } 427 return doc; 428 } 429 430 431 432 public static interface Prefix { 433 String getPrefix(int indent); 434 } 435 436 public static interface BlockContext { 437 String [] getPrefixAndSuffix(int indent, ASTNode node, RewriteEventStore events); 438 } 439 440 public static class ConstPrefix implements Prefix { 441 private String prefix; 442 443 public ConstPrefix(String prefix) { 444 this.prefix= prefix; 445 } 446 447 public String getPrefix(int indent) { 448 return this.prefix; 449 } 450 } 451 452 private class FormattingPrefix implements Prefix { 453 private int kind; 454 private String string; 455 private int start; 456 private int length; 457 458 public FormattingPrefix(String string, String sub, int kind) { 459 this.start= string.indexOf(sub); 460 this.length= sub.length(); 461 this.string= string; 462 this.kind= kind; 463 } 464 465 public String getPrefix(int indent) { 466 Position pos= new Position(this.start, this.length); 467 String str= this.string; 468 TextEdit res= formatString(this.kind, str, 0, str.length(), indent); 469 if (res != null) { 470 str= evaluateFormatterEdit(str, res, new Position[] { pos }); 471 } 472 return str.substring(pos.offset + 1, pos.offset + pos.length - 1); 473 } 474 } 475 476 private class BlockFormattingPrefix implements BlockContext { 477 private String prefix; 478 private int start; 479 480 public BlockFormattingPrefix(String prefix, int start) { 481 this.start= start; 482 this.prefix= prefix; 483 } 484 485 public String [] getPrefixAndSuffix(int indent, ASTNode node, RewriteEventStore events) { 486 String nodeString= ASTRewriteFlattener.asString(node, events); 487 String str= this.prefix + nodeString; 488 Position pos= new Position(this.start, this.prefix.length() + 1 - this.start); 489 490 TextEdit res= formatString(CodeFormatter.K_STATEMENTS, str, 0, str.length(), indent); 491 if (res != null) { 492 str= evaluateFormatterEdit(str, res, new Position[] { pos }); 493 } 494 return new String [] { str.substring(pos.offset + 1, pos.offset + pos.length - 1), ""}; } 496 } 497 498 private class BlockFormattingPrefixSuffix implements BlockContext { 499 private String prefix; 500 private String suffix; 501 private int start; 502 503 public BlockFormattingPrefixSuffix(String prefix, String suffix, int start) { 504 this.start= start; 505 this.suffix= suffix; 506 this.prefix= prefix; 507 } 508 509 public String [] getPrefixAndSuffix(int indent, ASTNode node, RewriteEventStore events) { 510 String nodeString= ASTRewriteFlattener.asString(node, events); 511 int nodeStart= this.prefix.length(); 512 int nodeEnd= nodeStart + nodeString.length() - 1; 513 514 String str= this.prefix + nodeString + this.suffix; 515 516 Position pos1= new Position(this.start, nodeStart + 1 - this.start); 517 Position pos2= new Position(nodeEnd, 2); 518 519 TextEdit res= formatString(CodeFormatter.K_STATEMENTS, str, 0, str.length(), indent); 520 if (res != null) { 521 str= evaluateFormatterEdit(str, res, new Position[] { pos1, pos2 }); 522 } 523 return new String [] { 524 str.substring(pos1.offset + 1, pos1.offset + pos1.length - 1), 525 str.substring(pos2.offset + 1, pos2.offset + pos2.length - 1) 526 }; 527 } 528 } 529 530 public final static Prefix NONE= new ConstPrefix(""); public final static Prefix SPACE= new ConstPrefix(" "); public final static Prefix ASSERT_COMMENT= new ConstPrefix(" : "); 534 public final Prefix VAR_INITIALIZER= new FormattingPrefix("A a={};", "a={" , CodeFormatter.K_STATEMENTS); public final Prefix METHOD_BODY= new FormattingPrefix("void a() {}", ") {" , CodeFormatter.K_CLASS_BODY_DECLARATIONS); public final Prefix FINALLY_BLOCK= new FormattingPrefix("try {} finally {}", "} finally {", CodeFormatter.K_STATEMENTS); public final Prefix CATCH_BLOCK= new FormattingPrefix("try {} catch(Exception e) {}", "} c" , CodeFormatter.K_STATEMENTS); public final Prefix ANNOT_MEMBER_DEFAULT= new FormattingPrefix("String value() default 1;", ") default 1" , CodeFormatter.K_CLASS_BODY_DECLARATIONS); public final Prefix ENUM_BODY_START= new FormattingPrefix("enum E { A(){void foo(){}} }", "){v" , CodeFormatter.K_COMPILATION_UNIT); public final Prefix ENUM_BODY_END= new FormattingPrefix("enum E { A(){void foo(){ }}, B}", "}}," , CodeFormatter.K_COMPILATION_UNIT); public final Prefix WILDCARD_EXTENDS= new FormattingPrefix("A<? extends B> a;", "? extends B" , CodeFormatter.K_CLASS_BODY_DECLARATIONS); public final Prefix WILDCARD_SUPER= new FormattingPrefix("A<? super B> a;", "? super B" , CodeFormatter.K_CLASS_BODY_DECLARATIONS); 544 public final Prefix FIRST_ENUM_CONST= new FormattingPrefix("enum E { X;}", "{ X" , CodeFormatter.K_COMPILATION_UNIT); public final Prefix ANNOTATION_SEPARATION= new FormattingPrefix("@A @B class C {}", "A @" , CodeFormatter.K_COMPILATION_UNIT); 547 public final BlockContext IF_BLOCK_WITH_ELSE= new BlockFormattingPrefixSuffix("if (true)", "else{}", 8); public final BlockContext IF_BLOCK_NO_ELSE= new BlockFormattingPrefix("if (true)", 8); public final BlockContext ELSE_AFTER_STATEMENT= new BlockFormattingPrefix("if (true) foo(); else ", 15); public final BlockContext ELSE_AFTER_BLOCK= new BlockFormattingPrefix("if (true) {} else ", 11); 552 public final BlockContext FOR_BLOCK= new BlockFormattingPrefix("for (;;) ", 7); public final BlockContext WHILE_BLOCK= new BlockFormattingPrefix("while (true)", 11); public final BlockContext DO_BLOCK= new BlockFormattingPrefixSuffix("do ", "while (true);", 1); 556 } 557 | Popular Tags |