1 19 20 package org.netbeans.modules.java.codegen; 21 22 import java.beans.*; 23 import java.lang.reflect.Method ; 24 import java.io.Writer ; 25 import java.io.PrintWriter ; 26 import java.io.StringWriter ; 27 import java.util.*; 28 29 import javax.swing.text.*; 30 31 import org.openide.src.*; 32 import org.openide.src.Element; 33 import org.openide.text.*; 34 35 import org.netbeans.modules.java.bridge.Binding; 36 37 42 abstract class ElementBinding implements TextBinding, ElementProperties { 43 private Element el; 44 45 47 protected PositionBounds wholeBounds; 48 49 52 protected PositionBounds docBounds; 53 54 56 protected PositionBounds headerBounds; 57 58 60 protected PositionBounds bodyBounds; 61 62 64 protected SourceText source; 65 66 protected ContainerImpl containerRef; 67 68 71 static Map setterCache; 72 73 private static final boolean DEBUG = false; 74 75 79 private static final String lineSeparator; 80 81 84 private static final int lineSeparatorLength; 85 86 89 private static final String LINEFEED = "\n"; 91 static { 92 String sep = System.getProperty("line.separator"); if (sep == null || LINEFEED.equals(sep)) { 94 lineSeparator = null; 95 lineSeparatorLength = -1; 96 } else { 97 lineSeparator = sep; 98 lineSeparatorLength = sep.length(); 99 } 100 } 101 102 103 public ElementBinding(Element el, SourceText s) { 104 this.el = el; 105 this.source = s; 106 s.registerBinding(el, this); 107 } 108 109 public TextBinding.Container getContainer(String type) { 110 return containerRef; 111 } 112 113 public void linkAfter(TextBinding b) { 114 } 115 116 public Element getElement() { 117 return this.el; 118 } 119 120 123 public PositionBounds getElementRange(boolean defRange) { 124 if (!defRange || headerBounds == null) { 125 return wholeBounds; 126 } else { 127 return new PositionBounds(headerBounds.getBegin(), 128 wholeBounds.getEnd()); 129 } 130 } 131 132 135 protected abstract Element cloneElement(); 136 137 142 public void create(PositionBounds bounds) throws SourceException { 143 wholeBounds = bounds; 144 if (DEBUG) { 145 System.err.println("Pre-create dump:\n----------------------------------------------------"); System.err.println("element: " + el); System.err.println("creating at " + bounds); source.dumpDocument(); 149 } 150 regenerateWhole(this.el, true); 151 if (DEBUG) { 152 System.err.println("after create wholeBounds = " + wholeBounds); } 154 } 155 156 161 public PositionRef prepareInsert(ElementBinding tbi, boolean after) 162 throws SourceException { 163 return after ? wholeBounds.getEnd() : wholeBounds.getBegin(); 164 } 165 166 171 public void insertAfter(ElementBinding b) throws SourceException { 172 b.createAt(wholeBounds.getEnd()); 173 } 174 175 183 public void create(ContainerImpl container, 184 ElementBinding previous, ElementBinding next, 185 PositionBounds containerBounds, boolean emptyBefore, boolean emptyAfter) 186 throws SourceException { 187 188 PositionRef beginPos, top; 189 if (previous == null) { 190 beginPos = containerBounds.getBegin(); 191 } else { 192 beginPos = previous.prepareInsert(this, true); 193 } 194 195 if (next == null) { 196 top = containerBounds.getEnd(); 197 } else { 198 top = next.prepareInsert(this, false); 199 } 200 PositionBounds gap = new PositionBounds( 201 beginPos, top); 202 PositionRef pos = source.findFreePosition(gap); 203 if (pos == null) 204 throw new SourceException("No room for element"); createAt(pos, emptyBefore, emptyAfter); 206 containerRef = container; 207 } 208 209 private void createAt(PositionRef r, boolean sepBefore, boolean sepAfter) 210 throws SourceException { 211 PositionBounds newBounds = CodeGenerator.createNewLineBounds(r, 212 CodeGenerator.BOUNDS_ALL, sepBefore, sepAfter); 213 create(newBounds); 214 } 215 216 private void createAt(PositionRef r) throws SourceException { 217 PositionBounds newBounds = CodeGenerator.createNewLineBounds(r, 218 CodeGenerator.BOUNDS_ALL); 219 create(newBounds); 220 } 221 222 public void applyPropertyChange(Object bean, String property, Object value) 223 throws SourceException { 224 java.lang.reflect.Method setter; 225 226 if (setterCache == null) { 227 setterCache = new HashMap(29); 228 setter = null; 229 } else { 230 setter = (java.lang.reflect.Method )setterCache.get(property); 231 } 232 if (setter == null) { 233 try { 234 BeanInfo binfo = Introspector.getBeanInfo(bean.getClass()); 235 PropertyDescriptor[] desc = binfo.getPropertyDescriptors(); 236 for (int i = 0; i < desc.length; i++) { 237 if (desc[i].getName().equals(property)) { 238 setter = desc[i].getWriteMethod(); 239 } 240 } 241 } catch (IntrospectionException ex) { 242 } 243 if (setter == null) { 244 throw new SourceException("Cannot find setter for " + property); } 246 setterCache.put(property, setter); 247 } 248 try { 249 setter.invoke(bean, new Object [] { value }); 250 return; 251 } catch (IllegalAccessException ex) { 252 } catch (java.lang.reflect.InvocationTargetException ex) { 253 } 254 } 255 256 public void changeElementProperty(final PropertyChangeEvent evt) throws SourceException { 257 if (!source.isGeneratorEnabled()) 258 return; 259 source.runAtomic(el, new ExceptionRunnable() { 260 public void run() throws Exception { 261 doChangeProperty(evt.getPropertyName(), 262 evt.getOldValue(), evt.getNewValue()); 263 } 264 }); 265 } 266 267 static final int CLASS_IGNORE = 0; 268 static final int CLASS_HEADER = 1; 269 static final int CLASS_BODY = 2; 270 static final int CLASS_JAVADOC = 3; 271 272 protected void doChangeProperty(String property, Object old, Object now) 273 throws Exception { 274 int propClass = classifyProperty(property); 275 if (propClass == CLASS_IGNORE) 276 return; 277 Element clonedBean = cloneElement(); 278 applyPropertyChange(clonedBean, property, now); 279 switch (propClass) { 280 case CLASS_HEADER: 281 regenerateHeader(clonedBean); 282 break; 283 case CLASS_BODY: 284 regenerateBody(clonedBean); 285 break; 286 case CLASS_JAVADOC: 287 regenerateJavaDoc(clonedBean, getJavaDoc()); 288 break; 289 } 290 } 291 292 protected JavaDoc getJavaDoc() { 293 throw new UnsupportedOperationException ("Generic javadoc not supported!"); } 295 296 protected int classifyProperty(String propName) { 297 return CLASS_IGNORE; 298 } 299 300 public ElementBinding findBindingAt(int off) { 301 if (wholeBounds.getBegin().getOffset() <= off && 302 wholeBounds.getEnd().getOffset() > off) 303 return this; 304 else 305 return null; 306 } 307 308 313 protected void remove(boolean collapseBefore, boolean collapseAfter) throws SourceException, IllegalStateException { 314 try { 316 CodeGenerator.clearBounds(wholeBounds, collapseBefore); 317 } catch (Exception ex) { 318 SourceText.rethrowException(el,ex); 319 } 320 } 321 322 protected void moveTo(ElementBinding before, ElementBinding after) throws SourceException { 323 PositionBounds oldWholeBounds = wholeBounds; 325 containerRef.insertChild(this, before, after); 326 try { 327 CodeGenerator.clearBounds(oldWholeBounds, false); 328 } catch (BadLocationException ex) { 329 SourceText.rethrowException(getElement(),ex); 330 } 331 } 332 333 334 335 341 protected StyledDocument findDocument() throws SourceException { 342 return source.getDocument(); 343 } 344 345 349 protected void regenerateHeader(Element el) throws SourceException { 350 Document doc = findDocument(); 351 352 source.runAtomic(this.el, new PartialGenerator(doc, el, headerBounds, ElementPrinter.HEADER_BEGIN, 353 ElementPrinter.HEADER_END)); 354 } 355 356 protected void regenerateWhole(Element el, boolean updatePositions) throws SourceException { 357 Document doc = findDocument(); 358 source.runAtomic(this.el, new PartialGenerator(doc, el, updatePositions)); 359 } 360 361 protected void regenerateBody(Element el) throws SourceException { 362 Document doc = findDocument(); 363 source.runAtomic(this.el, new PartialGenerator(doc, el, bodyBounds, 364 ElementPrinter.BODY_BEGIN, ElementPrinter.BODY_END)); 365 } 366 367 370 protected void regenerateJavaDoc(Element el, JavaDoc javadoc) throws SourceException { 371 Document doc = findDocument(); 372 if (docBounds != null) 373 source.runAtomic(this.el, new PartialGenerator(doc, el, docBounds, 374 ElementPrinter.JAVADOC_BEGIN, ElementPrinter.JAVADOC_END)); 375 else 376 source.runAtomic(this.el, new JavaDocGenerator(doc, el, javadoc)); 377 } 378 379 public boolean canInsertAfter() { 380 return true; 381 } 382 383 protected class RemovingRunnable implements ExceptionRunnable { 384 public void run() throws Exception { 385 CodeGenerator.clearBounds(wholeBounds, true); 386 } 387 } 388 389 public void changeJavaDoc(JavaDoc content) throws SourceException { 390 if (!source.isGeneratorEnabled()) 391 return; 392 regenerateJavaDoc(this.el, content); 393 } 394 395 public void updateBounds(int kind, PositionBounds bounds) { 396 switch (kind) { 397 case BOUNDS_ALL: 398 wholeBounds = bounds; 399 break; 400 case BOUNDS_JAVADOC: 401 docBounds = bounds; 402 break; 403 case BOUNDS_BODY: 404 bodyBounds = bounds; 405 break; 406 case BOUNDS_HEADER: 407 headerBounds = bounds; 408 } 409 } 410 411 protected class JavaDocGenerator extends PartialGenerator { 412 JavaDoc content; 413 414 JavaDocGenerator(Document doc, Element el, JavaDoc javadoc) { 415 super(doc, el, null, ElementPrinter.JAVADOC_BEGIN, ElementPrinter.JAVADOC_END); 416 content = javadoc; 417 } 418 419 public void run() throws Exception { 420 String raw = content.getRawText(); 421 422 if (content.isEmpty()) { 423 if (docBounds != null) { 425 CodeGenerator.clearBounds(docBounds, false); 426 docBounds = null; 427 } 428 } else { 429 posBounds = CodeGenerator.createNewLineBounds( 430 wholeBounds.getBegin(), CodeGenerator.BOUNDS_JAVADOC); 431 super.run(); 432 docBounds = posBounds; 433 wholeBounds = new PositionBounds(posBounds.getBegin(), 434 wholeBounds.getEnd()); 435 } 436 } 437 } 438 439 442 protected class PartialGenerator implements ExceptionRunnable { 443 PositionBounds posBounds; 444 int begin; 445 int end; 446 Document doc; 447 Element el; 448 boolean update; 449 450 PartialGenerator(Document doc, Element el, boolean updateBounds) { 451 update = updateBounds; 452 this.el = el; 453 this.doc = doc; 454 begin = ElementPrinter.ELEMENT_BEGIN; 455 end = ElementPrinter.ELEMENT_END; 456 posBounds = wholeBounds; 457 } 458 459 PartialGenerator(Document doc, Element el, PositionBounds pos, int begin, int end) { 460 this.posBounds = pos; 461 this.begin = begin; 462 this.end = end; 463 this.doc = doc; 464 this.el = el; 465 } 466 467 public void run() throws Exception { 468 PositionRef headerBegin = posBounds.getBegin(); 469 StyledDocument doc = findDocument(); 470 StringWriter wr = new StringWriter (); 471 Writer indentWriter = CodeGenerator.findIndentWriter(doc, headerBegin.getOffset(), wr); 472 ElementPrinterImpl printer = update ? 473 new WholeElementPrinter(indentWriter, wr) : 474 new ElementPrinterImpl(indentWriter, el, begin, end); 475 try { 476 el.print(printer); 477 } 478 catch (ElementPrinterInterruptException e) { 479 } 480 try { 481 indentWriter.close(); 482 } catch (java.io.IOException ex) { 483 throw new InternalError (ex.getMessage()); 484 } 485 486 CodeGenerator.fillTextBounds(posBounds, wr.toString()); 487 if (update) { 488 ((WholeElementPrinter)printer).finish(); 489 } 490 } 491 } 492 493 PositionRef getEndPosition() { 494 StyledDocument doc = source.getEditorSupport().getDocument(); 495 return source.createPos(doc.getLength(), Position.Bias.Backward); 496 } 497 498 PositionBounds findContainerBounds(TextBinding.Container cont) { 499 throw new UnsupportedOperationException (this.toString()); 500 } 501 502 class WholeElementPrinter extends ElementPrinterImpl { 503 StringWriter stringWriter; 504 CloneableEditorSupport editor; 505 506 int[] positions; 507 508 WholeElementPrinter(Writer writer, StringWriter stringWriter 509 ) { 512 super(writer); 513 this.stringWriter = stringWriter; 514 519 this.editor = source.getEditorSupport(); 520 this.positions = new int[8]; 521 java.util.Arrays.fill(positions, -1); 522 } 523 524 public void markNotify(Element el, int what) { 525 if (lastText!=null && lastText.endsWith(" ")) { String writerText=stringWriter.getBuffer().toString(); 527 int len=lastText.length(); 528 529 if (len>0 && !writerText.endsWith(" ")) { char ch=0; int i; 532 533 for (i=len-1;i>=0;i--) { 534 ch=lastText.charAt(i); 535 if (ch!=' ') 536 break; 537 } 538 if (ch!='\n') { 539 i++; 540 stringWriter.write(lastText,i,len-i); 541 } 542 } 543 } 544 positions[what] = stringWriter.getBuffer().length(); 545 if (what==ELEMENT_END && positions[BODY_END]!=-1 && 546 (el instanceof ConstructorElement || el instanceof InitializerElement)) 547 positions[BODY_END]=positions[ELEMENT_END]-1; 548 } 549 550 void finish() { 551 int offset = wholeBounds.getBegin().getOffset(); 552 553 if (positions[ELEMENT_BEGIN] != -1 && positions[ELEMENT_END] != -1) { 554 wholeBounds = createStableBounds(positions[ELEMENT_BEGIN] + offset, 555 positions[ELEMENT_END] + offset); 556 } 557 if (positions[JAVADOC_BEGIN] != -1 && positions[JAVADOC_END] != -1) { 560 docBounds = createStableBounds(positions[JAVADOC_BEGIN] + offset, 561 positions[JAVADOC_END] + offset); 562 } 563 if (positions[HEADER_BEGIN] != -1 && positions[HEADER_END] != -1) { 565 headerBounds = createStableBounds(positions[HEADER_BEGIN] + offset, 566 positions[HEADER_END] + offset); 567 } 568 if (positions[BODY_BEGIN] != -1 && positions[BODY_END] != -1) { 570 bodyBounds = createBounds(positions[BODY_BEGIN] + offset, 571 positions[BODY_END] + offset); 572 } 573 } 575 576 private PositionBounds createStableBounds(int begin, int end) { 577 if ((begin == -1) || (end == -1)) 578 return null; 579 580 PositionRef posBegin = editor.createPositionRef(begin, Position.Bias.Backward); 581 PositionRef posEnd = editor.createPositionRef(end, Position.Bias.Forward); 582 return new PositionBounds(posBegin, posEnd); 583 } 584 585 private PositionBounds createBounds(int begin, int end) { 586 if ((begin == -1) || (end == -1)) 587 return null; 588 589 PositionRef posBegin = editor.createPositionRef(begin, Position.Bias.Backward); 590 PositionRef posEnd = editor.createPositionRef(end, Position.Bias.Forward); 591 return new PositionBounds(posBegin, posEnd); 592 } 593 } 594 595 public String toString() { 596 return "Binding for " + getElement(); } 598 599 static String convertNewlines(String input) { 600 if (lineSeparator == null) 601 return input; 602 603 int firstIndex; 604 610 firstIndex = input.indexOf(lineSeparator); 612 if (firstIndex == -1) 613 return input; 614 StringBuffer result = new StringBuffer (); 617 char[] contents = input.toCharArray(); 618 if (firstIndex > 0) 619 result.append(contents, 0, firstIndex); 620 result.append(LINEFEED); 621 622 firstIndex += lineSeparatorLength; 623 int lastPos = firstIndex; 624 while (firstIndex < contents.length) { 625 firstIndex = input.indexOf(lineSeparator, firstIndex); 626 if (firstIndex == -1) { 627 result.append(contents, lastPos, contents.length - lastPos); 629 return result.toString(); 630 } 631 result.append(contents, lastPos, firstIndex - lastPos); 633 result.append(LINEFEED); 634 firstIndex += lineSeparatorLength; 635 lastPos = firstIndex; 636 } 637 result.append(contents, lastPos, firstIndex - lastPos); 640 return result.toString(); 642 } 643 644 646 static class ElementPrinterImpl implements ElementPrinter { 647 PrintWriter writer; 648 String lastText=null; 649 650 Element printedElement; 651 int beginMark; 652 int endMark; 653 int status; 654 655 ElementPrinterImpl(Writer writer) { 656 this(writer, null, 0, 0); 657 status = 1; 658 } 659 660 ElementPrinterImpl(Writer writer, Element printedElement, int beginMark, int endMark) { 661 this.writer = new PrintWriter (writer); 662 this.printedElement = printedElement; 663 this.beginMark = beginMark; 664 this.endMark = endMark; 665 status = 0; 666 } 667 668 public boolean isBegin(Element element, int what) { 669 return (printedElement == null) || 670 ((element == printedElement) && (what == beginMark)); 671 } 672 673 public boolean isEnd(Element element, int what) { 674 return (printedElement == element) && (what == endMark); 675 } 676 677 public void markNotify(Element element, int what) { 678 } 679 680 public String getString() { 681 return writer.toString(); 682 } 683 684 687 public void print(String text) throws ElementPrinterInterruptException { 688 switch (status) { 689 case 0: 690 lastText=null; 691 return; 692 case 1: 693 text = convertNewlines(text); 694 lastText=text; 695 writer.print(text); 696 break; 697 case 2: 698 throw new ElementPrinterInterruptException(); 699 } 700 } 701 702 705 public void println(String text) throws ElementPrinterInterruptException { 706 print(text); 707 print("\n"); } 709 710 715 private void mark(Element element, int what) throws ElementPrinterInterruptException { 716 switch (status) { 717 case 0: 718 if (isBegin(element, what)) { 719 markNotify(element, what); 720 status = 1; 721 } 722 break; 723 case 1: 724 writer.flush(); 725 markNotify(element, what); 726 if (isEnd(element, what)) { 727 status = 2; 728 writer.close(); 729 throw new ElementPrinterInterruptException(); 730 } 731 break; 732 case 2: 733 throw new ElementPrinterInterruptException(); 734 } 735 } 736 737 public void markClass(ClassElement element, int what) throws ElementPrinterInterruptException { 738 mark(element, what); 739 } 740 741 public void markInitializer(InitializerElement element, int what) throws ElementPrinterInterruptException { 742 mark(element, what); 743 } 744 745 public void markField(FieldElement element, int what) throws ElementPrinterInterruptException { 746 mark(element, what); 747 } 748 749 public void markConstructor(ConstructorElement element, int what) throws ElementPrinterInterruptException { 750 mark(element, what); 751 } 752 753 public void markMethod(MethodElement element, int what) throws ElementPrinterInterruptException { 754 mark(element, what); 755 } 756 } 757 } 758 | Popular Tags |