1 21 22 package org.armedbear.j; 23 24 import gnu.regexp.RE; 25 import gnu.regexp.REException; 26 import gnu.regexp.REMatch; 27 import gnu.regexp.UncheckedRE; 28 import java.awt.event.KeyEvent ; 29 import java.io.BufferedReader ; 30 import java.io.IOException ; 31 import java.io.InputStreamReader ; 32 import java.io.StringReader ; 33 import javax.swing.tree.DefaultMutableTreeNode ; 34 import javax.swing.tree.TreeModel ; 35 import javax.swing.tree.TreeNode ; 36 import javax.swing.tree.TreePath ; 37 import javax.swing.undo.CompoundEdit ; 38 import org.xml.sax.SAXParseException ; 39 40 public final class XmlMode extends AbstractMode implements Constants, Mode 41 { 42 private static final String COMMENT_START = "<!--"; 43 private static final String COMMENT_END = "-->"; 44 private static final String CDATA_START = "<![CDATA["; 45 private static final String CDATA_END = "]]>"; 46 47 private static final XmlMode mode = new XmlMode(); 48 49 private static XmlErrorBuffer errorBuffer; 50 51 private static RE tagNameRE; 52 private static RE attributeNameRE; 53 private static RE quotedValueRE; 54 private static RE unquotedValueRE; 55 56 private XmlMode() 57 { 58 super(XML_MODE, XML_MODE_NAME); 59 setProperty(Property.INDENT_SIZE, 2); 60 } 61 62 public static final XmlMode getMode() 63 { 64 return mode; 65 } 66 67 public static final XmlErrorBuffer getErrorBuffer() 68 { 69 return errorBuffer; 70 } 71 72 public NavigationComponent getSidebarComponent(Editor editor) 73 { 74 Debug.assertTrue(editor.getBuffer().getMode() == getMode()); 75 if (!editor.getBuffer().getBooleanProperty(Property.ENABLE_TREE)) 76 return null; 77 View view = editor.getCurrentView(); 78 if (view == null) 79 return null; if (view.getSidebarComponent() == null) 81 view.setSidebarComponent(new XmlTree(editor, null)); 82 return view.getSidebarComponent(); 83 } 84 85 public String getCommentStart() 86 { 87 return COMMENT_START; 88 } 89 90 public String getCommentEnd() 91 { 92 return COMMENT_END; 93 } 94 95 public Formatter getFormatter(Buffer buffer) 96 { 97 return new XmlFormatter(buffer); 98 } 99 100 protected void setKeyMapDefaults(KeyMap km) 101 { 102 km.mapKey(KeyEvent.VK_TAB, 0, "tab"); 103 km.mapKey(KeyEvent.VK_TAB, CTRL_MASK, "insertTab"); 104 km.mapKey(KeyEvent.VK_ENTER, 0, "newlineAndIndent"); 105 km.mapKey(KeyEvent.VK_ENTER, CTRL_MASK, "newline"); 106 km.mapKey(KeyEvent.VK_M, CTRL_MASK, "xmlFindMatch"); 107 km.mapKey('=', "xmlElectricEquals"); 108 km.mapKey('>', "electricCloseAngleBracket"); 109 km.mapKey(KeyEvent.VK_E, CTRL_MASK, "xmlInsertMatchingEndTag"); 110 km.mapKey('/', "xmlElectricSlash"); 111 km.mapKey(KeyEvent.VK_I, ALT_MASK, "cycleIndentSize"); 112 km.mapKey(KeyEvent.VK_COMMA, CTRL_MASK | SHIFT_MASK, "xmlInsertTag"); 113 km.mapKey(KeyEvent.VK_PERIOD, CTRL_MASK | SHIFT_MASK, 114 "xmlInsertEmptyElementTag"); 115 km.mapKey(KeyEvent.VK_P, CTRL_MASK, "xmlParseBuffer"); 116 km.mapKey(KeyEvent.VK_P, CTRL_MASK | SHIFT_MASK, "xmlValidateBuffer"); 117 km.mapKey(KeyEvent.VK_EQUALS, CTRL_MASK, "xmlFindCurrentNode"); 118 km.mapKey(KeyEvent.VK_OPEN_BRACKET, CTRL_MASK, "fold"); 119 km.mapKey(KeyEvent.VK_CLOSE_BRACKET, CTRL_MASK, "unfold"); 120 121 km.mapKey(KeyEvent.VK_F9, 0, "compile"); 123 km.mapKey(KeyEvent.VK_F9, CTRL_MASK, "recompile"); 124 } 125 126 public void populateModeMenu(Editor editor, Menu menu) 127 { 128 menu.add(editor, "Insert Element", 'I', "xmlInsertTag"); 129 menu.add(editor, "End Current Element", 'E', "xmlInsertMatchingEndTag"); 130 menu.addSeparator(); 131 menu.add(editor, "Parse Buffer", 'P', "xmlParseBuffer"); 132 menu.add(editor, "Validate Buffer", 'V', "xmlValidateBuffer"); 133 boolean enabled = errorBuffer != null; 134 menu.addSeparator(); 135 menu.add(editor, "Next Error", 'N', "nextError", enabled); 136 menu.add(editor, "Previous Error", 'R', "previousError", enabled); 137 menu.add(editor, "Show Error Message", 'M', "showMessage", enabled); 138 } 139 140 public void loadFile(Buffer buffer, File file) 141 { 142 String encoding = null; 143 try { 144 BufferedReader reader = 145 new BufferedReader (new InputStreamReader (file.getInputStream())); 146 String s = reader.readLine(); 147 reader.close(); 148 if (s != null && s.toLowerCase().startsWith("<?xml")) { 149 int end = s.indexOf("?>"); 150 if (end >= 0) { 151 s = s.substring(5, end); 152 RE re = new UncheckedRE("encoding[ \t]*=[ \t]*"); 153 REMatch match = re.getMatch(s); 154 if (match != null) { 155 s = s.substring(match.getEndIndex()); 158 if (s.length() > 0) { 159 char quoteChar = s.charAt(0); 160 end = s.indexOf(quoteChar, 1); 162 if (end >= 0) { 163 encoding = s.substring(1, end); 164 if (Utilities.isSupportedEncoding(encoding)) 165 file.setEncoding(encoding); 166 else 167 Log.error("unsupported encoding \"" + 168 encoding + '"'); 169 } 170 } 171 } 172 } 173 } 174 } 175 catch (IOException e) { 176 Log.error(e); 177 } 178 if (encoding == null) 179 encoding = "UTF8"; try { 181 buffer.load(file.getInputStream(), file.getEncoding()); 182 } 183 catch (IOException e) { 184 Log.error(e); 185 } 186 } 187 188 public boolean canIndent() 189 { 190 return true; 191 } 192 193 public int getCorrectIndentation(Line line, Buffer buffer) 194 { 195 final Line model = getModel(line); 196 if (model == null) 197 return 0; 198 int indent = buffer.getIndentation(model); 199 if (line.flags() == STATE_QUOTE && model.flags() != STATE_QUOTE) 200 return indent + buffer.getIndentSize(); 201 final String text = line.trim(); 202 if (text.equals("/>")) { 203 Position pos = new Position(line, line.length()); 204 while (pos.prev()) { 205 if (pos.getChar() == '<') 206 break; 207 } 208 return buffer.getIndentation(pos.getLine()); 209 } 210 if (text.startsWith("</")) { 211 Position pos = findMatchingStartTag(line); 212 if (pos != null) 213 return buffer.getIndentation(pos.getLine()); 214 indent -= buffer.getIndentSize(); 215 return indent < 0 ? 0 : indent; 216 } 217 final String modelText = model.trim(); 218 if (modelText.startsWith("<") && !modelText.startsWith("</") && 219 !modelText.startsWith("<!")) 220 { 221 String tag = getTag(modelText); 222 if (isEmptyElementTag(tag)) 223 return indent; 224 if (isProcessingInstruction(tag)) 225 return indent; 226 String tagName = Utilities.getTagName(modelText); 228 String startTag = "<" + tagName; 229 String endTag = "</" + tagName; 230 int count = 1; 231 final int limit = modelText.length(); 232 for (int i = startTag.length(); i < limit; i++) { 233 if (modelText.charAt(i) == '<') { 234 if (lookingAt(modelText, i, endTag)) { 235 int end = i + endTag.length(); 236 if (end < limit) { 237 char c = modelText.charAt(end); 238 if (c == ' ' || c == '\t' || c == '>') 239 --count; 240 } 241 } else if (lookingAt(modelText, i, startTag)) { 242 int end = i + startTag.length(); 243 if (end < limit) { 244 char c = modelText.charAt(end); 245 if (c == ' ' || c == '\t' || c == '>') 246 ++count; 247 } 248 } 249 } 250 } 251 if (count > 0) 252 indent += buffer.getIndentSize(); 253 } else if (modelText.endsWith("/>")) { 254 Position pos = new Position(model, model.length()); 255 while (pos.prev()) { 256 if (pos.getChar() == '<') 257 break; 258 } 259 indent = buffer.getIndentation(pos.getLine()); 260 } 261 return indent; 262 } 263 264 private Position findMatchingStartTag(Line line) 266 { 267 String s = line.trim(); 268 if (!s.startsWith("</")) 269 return null; 270 FastStringBuffer sb = new FastStringBuffer(); 271 for (int i = 2; i < s.length(); i++) { 272 char c = s.charAt(i); 273 if (c <= ' ') 274 break; 275 if (c == '>') 276 break; 277 sb.append(c); 278 } 279 Position pos = new Position(line, 0); 280 String name = sb.toString(); 281 return findMatchingStartTag(name, pos); 282 } 283 284 private static Position findMatchingStartTag(String name, Position start) 285 { 286 Position pos = start.copy(); 287 String endTagToBeMatched = "</" + name + ">"; 288 String lookFor = "<" + name; 289 int count = 1; 290 boolean succeeded = false; 291 if (pos.lookingAt(endTagToBeMatched)) 292 pos.prev(); 293 while (!pos.atStart()) { 295 if (pos.lookingAt(COMMENT_END)) { 296 do { 297 pos.prev(); 298 } while (!pos.atStart() && !pos.lookingAt(COMMENT_START)); 299 } else if (pos.lookingAt(CDATA_END)) { 300 do { 301 pos.prev(); 302 } while (!pos.atStart() && !pos.lookingAt(CDATA_START)); 303 } else if (pos.lookingAt(endTagToBeMatched)) { 304 ++count; 305 } else if (pos.lookingAt(lookFor)) { 306 String tag = getTag(pos.copy()); 309 if (Utilities.getTagName(tag).equals(name)) { 310 if (!tag.endsWith("/>")) { 311 --count; 312 if (count == 0) { 313 succeeded = true; 314 break; 315 } 316 } 317 } 318 } 319 pos.prev(); 320 } 321 if (succeeded) 322 return pos; 323 return null; 325 } 326 327 private static Position findMatchingEndTag(String name, Position start) 328 { 329 Position pos = start.copy(); 330 String startTagToBeMatched = "<" + name; 331 String lookFor = "</" + name + ">"; 332 int count = 1; 333 if (pos.lookingAt(startTagToBeMatched)) 334 pos.skip(startTagToBeMatched.length()); 335 while (!pos.atEnd()) { 337 if (pos.lookingAt(COMMENT_START)) { 338 do { 339 pos.next(); 340 } while (!pos.atEnd() && !pos.lookingAt(COMMENT_END)); 341 if (pos.atEnd()) { 342 break; 343 } else { 344 pos.skip(COMMENT_END.length()); 345 continue; 346 } 347 } 348 if (pos.lookingAt(CDATA_START)) { 349 do { 350 pos.next(); 351 } while (!pos.atEnd() && !pos.lookingAt(CDATA_END)); 352 if (pos.atEnd()) { 353 break; 354 } else { 355 pos.skip(CDATA_END.length()); 356 continue; 357 } 358 } 359 if (pos.lookingAt(startTagToBeMatched)) { 360 String tag = getTag(pos); if (Utilities.getTagName(tag).equals(name)) { 362 if (!tag.endsWith("/>")) 363 ++count; 364 } 365 continue; 366 } 367 if (pos.lookingAt(lookFor)) { 368 --count; 369 if (count == 0) { 370 return pos; 371 } else { 372 pos.skip(lookFor.length()); 373 continue; 374 } 375 } 376 pos.next(); 378 } 379 return null; 381 } 382 383 private static String getUnmatchedStartTag(Position start) 384 { 385 Position pos = start.copy(); 386 if (isInComment(pos)) 387 return null; 388 if (isInTag(pos)) 389 return null; 390 if (isInCDataSection(pos)) 391 return null; 392 int count = 1; 393 if (!pos.lookingAt("<") || pos.prev()) { 394 do { 395 if (pos.lookingAt(COMMENT_END)) { 396 do { 397 pos.prev(); 398 } while (!pos.atStart() && !pos.lookingAt(COMMENT_START)); 399 } else if (pos.lookingAt(CDATA_END)) { 400 do { 401 pos.prev(); 402 } while (!pos.atStart() && !pos.lookingAt(CDATA_START)); 403 } else if (pos.getChar() == '<') { 404 if (pos.lookingAt("</")) 405 ++count; 406 else if (pos.lookingAt("<?")) 407 ; 408 else { 409 String tag = getTag(pos.copy()); 412 if (!tag.endsWith("/>")) { 413 --count; 414 if (count == 0) 415 return tag; 416 } 417 } 418 } 419 } while (pos.prev()); 420 } 421 return null; 423 } 424 425 private static boolean isInTag(Position position) 426 { 427 Position pos = position.copy(); 428 while (pos.prev()) { 429 if (pos.lookingAt(COMMENT_END)) { 430 do { 431 pos.prev(); 432 } while (!pos.atStart() && !pos.lookingAt(COMMENT_START)); 433 continue; 434 } 435 char c = pos.getChar(); 436 if (c == '<') 437 return true; 438 else if (c == '>') 439 return false; 440 } 441 return false; 442 } 443 444 private static boolean isInComment(Position position) 445 { 446 Position pos = position.copy(); 447 boolean inComment = pos.getLine().flags() == STATE_COMMENT; 448 pos.setOffset(0); 449 final int limit = position.getOffset(); 450 while (pos.getOffset() < limit) { 451 if (inComment) { 452 if (pos.lookingAt(COMMENT_END)) { 453 pos.skip(COMMENT_END.length()); 454 if (pos.getOffset() > limit) 455 break; 456 inComment = false; 457 continue; 458 } 459 } else if (pos.lookingAt(COMMENT_START)) { 460 inComment = true; 461 pos.skip(COMMENT_START.length()); 462 continue; 463 } 464 pos.next(); 465 } 466 return inComment; 467 } 468 469 private static boolean isInCDataSection(Position position) 470 { 471 Position pos = position.copy(); 472 boolean inCDataSection = pos.getLine().flags() == STATE_CDATA; 473 pos.setOffset(0); 474 final int limit = position.getOffset(); 475 while (pos.getOffset() < limit) { 476 if (inCDataSection) { 477 if (pos.lookingAt(CDATA_END)) { 478 pos.skip(CDATA_END.length()); 479 if (pos.getOffset() > limit) 480 break; 481 inCDataSection = false; 482 continue; 483 } 484 } else if (pos.lookingAt(CDATA_START)) { 485 inCDataSection = true; 486 pos.skip(CDATA_START.length()); 487 continue; 488 } 489 pos.next(); 490 } 491 return inCDataSection; 492 } 493 494 private static String getTag(String s) 495 { 496 if (s == null || s.length() == 0 || s.charAt(0) != '<') 497 return null; 498 FastStringBuffer sb = new FastStringBuffer(); 499 final int limit = s.length(); 500 char quoteChar = 0; 501 for (int i = 0; i < limit; i++) { 502 char c = s.charAt(i); 503 sb.append(c); 504 if (quoteChar != 0) { 505 if (c == quoteChar) 507 quoteChar = 0; 508 } else { 509 if (c == '\'' || c == '"') 511 quoteChar = c; 512 else if (c == '>') 513 break; 514 } 515 } 516 return sb.toString(); 517 } 518 519 private static String getTag(Position pos) 521 { 522 if (pos == null || pos.getChar() != '<') 523 return null; 524 FastStringBuffer sb = new FastStringBuffer('<'); 525 char quoteChar = 0; 526 while (pos.next()) { 527 char c = pos.getChar(); 528 sb.append(c); 529 if (quoteChar != 0) { 530 if (c == quoteChar) 532 quoteChar = 0; 533 } else { 534 if (c == '\'' || c == '"') 536 quoteChar = c; 537 else if (c == '>') { 538 pos.next(); 539 break; 540 } 541 } 542 } 543 return sb.toString(); 544 } 545 546 private static boolean isProcessingInstruction(String tag) 547 { 548 if (tag.startsWith("<?") && tag.endsWith("?>")) 549 return true; 550 return false; 551 } 552 553 private static boolean isEmptyElementTag(String tag) 554 { 555 if (tag == null) 556 return false; 557 return tag.endsWith("/>"); 558 } 559 560 private static final boolean lookingAt(String s, int i, String pattern) 561 { 562 return s.regionMatches(i, pattern, 0, pattern.length()); 563 } 564 565 private static Line getModel(Line line) 566 { 567 Line model = line; 568 while ((model = model.previous()) != null) { 569 int flags = model.flags(); 570 if (flags == STATE_COMMENT || flags == STATE_QUOTE) 571 continue; 572 else if (model.trim().startsWith(COMMENT_START)) 573 continue; 574 else if (model.isBlank()) 575 continue; 576 else 577 break; 578 } 579 return model; 580 } 581 582 public char fixCase(Editor editor, char c) 583 { 584 if (!Character.isUpperCase(c) && !Character.isLowerCase(c)) 585 return c; 586 final Buffer buffer = editor.getBuffer(); 587 if (!buffer.getBooleanProperty(Property.FIX_CASE)) 588 return c; 589 if (!initRegExps()) 590 return c; 591 Position pos = findStartOfTag(editor.getDot()); 592 if (pos != null) { 593 int index = pos.getOffset(); 594 String text = pos.getLine().getText(); 595 REMatch match = tagNameRE.getMatch(text, index); 596 if (match == null) 597 return c; 598 if (match.getEndIndex() >= editor.getDotOffset()) { 599 if (buffer.getBooleanProperty(Property.UPPER_CASE_TAG_NAMES)) 601 return Character.toUpperCase(c); 602 else 603 return Character.toLowerCase(c); 604 } 605 while (true) { 606 index = match.getEndIndex(); 607 match = attributeNameRE.getMatch(text, index); 608 if (match == null) 609 return c; 610 if (match.getEndIndex() >= editor.getDotOffset()) { 611 if (buffer.getBooleanProperty(Property.UPPER_CASE_ATTRIBUTE_NAMES)) 613 return Character.toUpperCase(c); 614 else 615 return Character.toLowerCase(c); 616 } 617 index = match.getEndIndex(); 618 match = quotedValueRE.getMatch(text, index); 619 if (match == null) { 620 match = unquotedValueRE.getMatch(text, index); 621 if (match == null) 622 return c; 623 } 624 if (match.getEndIndex() >= editor.getDotOffset()) { 625 return c; 627 } 628 } 629 } 630 return c; 631 } 632 633 private static boolean checkElectricEquals(Editor editor) 634 { 635 Position pos = findStartOfTag(editor.getDot()); 636 if (pos == null) 637 return false; 638 char c = editor.getDotChar(); 639 if (c == '>' || c == '/' || Character.isWhitespace(c)) 640 return true; 641 return false; 642 } 643 644 private static Position findStartOfTag(Position pos) 646 { 647 final String text = pos.getLine().getText(); 648 int offset = pos.getOffset(); 649 if (offset >= pos.getLine().length()) 650 offset = pos.getLine().length()-1; 651 else if (text.charAt(offset) == '>') 652 --offset; 653 while (offset >= 0) { 654 char c = text.charAt(offset); 655 if (c == '<') 656 return new Position(pos.getLine(), offset); 657 if (c == '>') 658 return null; 659 --offset; 660 } 661 return null; 662 } 663 664 private static boolean initRegExps() 665 { 666 if (tagNameRE == null) { 667 try { 668 tagNameRE = new RE("</?[A-Za-z0-9]*"); 669 attributeNameRE = new RE("\\s+[A-Za-z0-9]*"); 670 quotedValueRE = new RE("\\s*=\\s*\"[^\"]*"); 671 unquotedValueRE = new RE("\\s*=\\s*\\S*"); 672 } 673 catch (REException e) { 674 tagNameRE = null; 675 return false; 676 } 677 } 678 return true; 679 } 680 681 public static void xmlFindCurrentNode() 682 { 683 final Editor editor = Editor.currentEditor(); 684 if (editor.getModeId() == XML_MODE) { 685 final Sidebar sidebar = editor.getSidebar(); 686 if (sidebar != null) { 687 XmlTree tree = (XmlTree) sidebar.getBottomComponent(); 688 if (tree != null) 689 ensureCurrentNodeIsVisible(editor, tree); 690 } 691 } 692 } 693 694 public static void xmlParseBuffer() 695 { 696 final Editor editor = Editor.currentEditor(); 697 if (editor.getModeId() == XML_MODE) { 698 XmlTree tree = null; 699 final Sidebar sidebar = editor.getSidebar(); 700 if (sidebar != null) { 701 tree = (XmlTree) sidebar.getBottomComponent(); 702 703 if (tree == null) { 705 sidebar.setUpdateFlag(SIDEBAR_NAVIGATION_COMPONENT_ALL); 706 sidebar.refreshSidebar(); 707 tree = (XmlTree) sidebar.getBottomComponent(); 708 if (tree != null) 709 ensureCurrentNodeIsVisible(editor, tree); 710 return; 711 } 712 } 713 final Buffer buffer = editor.getBuffer(); 714 XmlParserImpl parser = new XmlParserImpl(buffer); 715 if (parser.initialize()) { 716 editor.setWaitCursor(); 717 try { 718 parser.setReader(new StringReader (buffer.getText())); 719 parser.run(); 720 } 721 catch (OutOfMemoryError e) { 722 outOfMemory(); 723 return; 724 } 725 finally { 726 editor.setDefaultCursor(); 727 } 728 String output = parser.getOutput(); 729 if (output != null && output.length() > 0) { 732 if (errorBuffer == null) { 733 errorBuffer = 734 new XmlErrorBuffer(buffer.getFile(), output); 735 } else 736 errorBuffer.recycle(buffer.getFile(), output); 737 Editor otherEditor = editor.getOtherEditor(); 738 if (otherEditor != null) { 739 errorBuffer.setUnsplitOnClose( 740 otherEditor.getBuffer().unsplitOnClose()); 741 otherEditor.makeNext(errorBuffer); 742 } else 743 errorBuffer.setUnsplitOnClose(true); 744 editor.displayInOtherWindow(errorBuffer); 745 } 746 if (parser.getException() == null) { 747 if (tree != null) { 748 TreeModel treeModel = parser.getTreeModel(); 749 if (treeModel != null) { 750 tree.setParserClassName(parser.getParserClassName()); 751 tree.setModel(treeModel); 752 Debug.assertTrue(tree.getModel() != null); 753 Debug.assertTrue(tree.getModel().getRoot() != null); 754 ensureCurrentNodeIsVisible(editor, tree); 755 } 756 } 757 editor.status("No errors"); 758 } 759 } 760 } 761 } 762 763 public static void xmlValidateBuffer() 764 { 765 final Editor editor = Editor.currentEditor(); 766 if (editor.getModeId() == XML_MODE) { 767 final Buffer buffer = editor.getBuffer(); 768 XmlParserImpl parser = new XmlParserImpl(buffer); 769 if (parser.initialize()) { 770 try { 771 editor.setWaitCursor(); 772 parser.enableValidation(true); 773 parser.setReader(new StringReader (buffer.getText())); 774 parser.run(); 775 } 776 catch (OutOfMemoryError e) { 777 outOfMemory(); 778 return; 779 } 780 finally { 781 editor.setDefaultCursor(); 782 } 783 String output = parser.getOutput(); 784 if (output != null && output.length() > 0) { 785 if (errorBuffer == null) { 786 errorBuffer = 787 new XmlErrorBuffer(buffer.getFile(), output); 788 } else 789 errorBuffer.recycle(buffer.getFile(), output); 790 Editor otherEditor = editor.getOtherEditor(); 791 if (otherEditor != null) { 792 errorBuffer.setUnsplitOnClose( 793 otherEditor.getBuffer().unsplitOnClose()); 794 otherEditor.makeNext(errorBuffer); 795 } else 796 errorBuffer.setUnsplitOnClose(true); 797 editor.displayInOtherWindow(errorBuffer); 798 } else 799 editor.status("No errors"); 800 } 801 } 802 } 803 804 private static void outOfMemory() 805 { 806 MessageDialog.showMessageDialog( 807 "Not enough memory to run parser", 808 "XML Mode"); 809 } 810 811 public static void xmlFindError(Editor editor, SAXParseException e) 812 { 813 Line line = editor.getBuffer().getLine(e.getLineNumber()-1); 814 if (line != null) { 815 int offset = e.getColumnNumber()-1; 816 if (offset < 0) 817 offset = 0; 818 if (offset > line.length()) 819 offset = line.length(); 820 if (line != editor.getDotLine() || offset != editor.getDotOffset()) { 821 editor.addUndo(SimpleEdit.MOVE); 822 editor.updateDotLine(); 823 editor.setDot(line, offset); 824 editor.updateDotLine(); 825 editor.moveCaretToDotCol(); 826 editor.updateDisplay(); 827 } 828 } 829 } 830 831 public static void ensureCurrentNodeIsVisible(Editor editor, XmlTree tree) 832 { 833 if (tree == null) 834 return; 835 DefaultMutableTreeNode currentNode = tree.getNodeAtPos(editor.getDot()); 836 if (currentNode != null) { 837 tree.scrollPathToVisible(new TreePath (currentNode.getPath())); 838 tree.updatePosition(); 839 } 840 } 841 842 public static void copyXPath() 843 { 844 final Editor editor = Editor.currentEditor(); 845 if (editor.getModeId() == XML_MODE) { 846 final Sidebar sidebar = editor.getSidebar(); 847 if (sidebar != null) { 848 XmlTree tree = (XmlTree) sidebar.getBottomComponent(); 849 if (tree != null) { 850 DefaultMutableTreeNode currentNode = 851 tree.getNodeAtPos(editor.getDot()); 852 if (currentNode != null) { 853 TreeNode [] array = currentNode.getPath(); 854 if (array != null) { 855 FastStringBuffer sb = new FastStringBuffer(); 856 for (int i = 0; i < array.length; i++) { 857 DefaultMutableTreeNode node = 858 (DefaultMutableTreeNode ) array[i]; 859 XmlTreeElement element = 860 (XmlTreeElement) node.getUserObject(); 861 sb.append('/'); 862 sb.append(element.getName()); 863 } 864 if (sb.length() > 0) { 865 KillRing killRing = editor.getKillRing(); 866 killRing.appendNew(sb.toString()); 867 killRing.copyLastKillToSystemClipboard(); 868 editor.status("XPath copied to clipboard"); 869 } 870 } 871 } 872 } 873 } 874 } 875 } 876 877 public static void xmlElectricEquals() 878 { 879 final Editor editor = Editor.currentEditor(); 880 if (!editor.checkReadOnly()) 881 return; 882 boolean ok = false; 883 if (editor.getModeId() == XML_MODE) { 884 if (checkElectricEquals(editor)) 886 ok = true; 887 } 888 if (ok) { 889 CompoundEdit compoundEdit = editor.beginCompoundEdit(); 890 editor.fillToCaret(); 891 editor.addUndo(SimpleEdit.INSERT_STRING); 892 editor.insertStringInternal("=\"\""); 893 editor.addUndo(SimpleEdit.MOVE); 894 editor.getDot().moveLeft(); 895 editor.moveCaretToDotCol(); 896 editor.endCompoundEdit(compoundEdit); 897 } else 898 editor.insertNormalChar('='); 899 } 900 901 public static void xmlInsertTag() 902 { 903 final Editor editor = Editor.currentEditor(); 904 if (!editor.checkReadOnly()) 905 return; 906 InsertTagDialog d = new InsertTagDialog(editor); 907 editor.centerDialog(d); 908 d.show(); 909 _xmlInsertTag(editor, d.getInput()); 910 } 911 912 public static void xmlInsertTag(String input) 913 { 914 final Editor editor = Editor.currentEditor(); 915 if (!editor.checkReadOnly()) 916 return; 917 _xmlInsertTag(editor, input); 918 } 919 920 private static void _xmlInsertTag(Editor editor, String input) 921 { 922 if (input != null) { 923 final String tagName, extra; 924 int index = input.indexOf(' '); 925 if (index >= 0) { 926 tagName = input.substring(0, index); 927 extra = input.substring(index); 928 } else { 929 tagName = input; 930 extra = ""; 931 } 932 InsertTagDialog.insertTag(editor, tagName, extra, true); 934 } 935 } 936 937 public static void xmlInsertEmptyElementTag() 938 { 939 final Editor editor = Editor.currentEditor(); 940 if (!editor.checkReadOnly()) 941 return; 942 InputDialog d = 943 new InputDialog(editor, "Tag:", "Insert Empty Element Tag", null); 944 d.setHistory(new History("xmlInsertEmptyElementTag")); 945 editor.centerDialog(d); 946 d.show(); 947 String input = d.getInput(); 948 if (input == null) 949 return; 950 String tagName; 951 String extra; 952 int index = input.indexOf(' '); 953 if (index >= 0) { 954 tagName = input.substring(0, index); 955 extra = input.substring(index); 956 } else { 957 tagName = input; 958 extra = ""; 959 } 960 final Buffer buffer = editor.getBuffer(); 961 if (buffer.getBooleanProperty(Property.FIX_CASE)) { 962 if (buffer.getBooleanProperty(Property.UPPER_CASE_TAG_NAMES)) 963 tagName = tagName.toUpperCase(); 964 else 965 tagName = tagName.toLowerCase(); 966 } 967 CompoundEdit compoundEdit = editor.beginCompoundEdit(); 968 editor.fillToCaret(); 969 final int offset = editor.getDotOffset(); 970 editor.addUndo(SimpleEdit.INSERT_STRING); 971 FastStringBuffer sb = new FastStringBuffer('<'); 972 sb.append(tagName); 973 sb.append(extra); 974 sb.append("/>"); 975 editor.insertStringInternal(sb.toString()); 976 Editor.updateInAllEditors(editor.getDotLine()); 977 editor.addUndo(SimpleEdit.MOVE); 978 editor.getDot().setOffset(offset + 1 + input.length()); 979 editor.moveCaretToDotCol(); 980 editor.endCompoundEdit(compoundEdit); 981 } 982 983 public static void xmlFindMatch() 984 { 985 final Editor editor = Editor.currentEditor(); 986 final Position dot = editor.getDot(); 987 if (isInComment(dot)) { 988 editor.status("In comment"); 989 return; 990 } 991 if (isInCDataSection(dot)) { 992 editor.status("In CDATA section"); 993 return; 994 } 995 Position pos = findStartOfTag(dot); 996 if (pos == null) { 997 final Line dotLine = dot.getLine(); 998 int offset = dot.getOffset(); 999 if (dotLine.substring(0, offset).trim().length() == 0) { 1000 while (Character.isWhitespace(dotLine.charAt(offset)) && 1003 offset < dotLine.length()) 1004 ++offset; 1005 if (dotLine.charAt(offset) == '<') 1006 pos = new Position(dotLine, offset); 1007 } 1008 if (pos == null) { 1009 offset = 1010 dotLine.getText().lastIndexOf(COMMENT_END, dot.getOffset()); 1011 if (offset >= 0 && dot.getOffset() >= offset && 1012 dot.getOffset() < offset + COMMENT_END.length()) 1013 pos = new Position(dotLine, offset); 1014 else if (dotLine.trim().equals(COMMENT_END)) 1015 pos = new Position(dotLine, 1016 dotLine.getText().indexOf(COMMENT_END)); 1017 } 1018 } 1019 1020 if (pos == null) { 1021 editor.status("Nothing to match"); 1022 return; 1023 } 1024 1025 Position match = null; 1026 if (pos.lookingAt(COMMENT_START)) { 1027 match = findCommentEnd(pos); 1028 } else if (pos.lookingAt(COMMENT_END)) { 1029 match = findCommentStart(pos); 1030 } else if (pos.lookingAt("</")) { 1031 String name = 1033 Utilities.getTagName(pos.getLine().substring(pos.getOffset())); 1034 name = name.substring(1); match = findMatchingStartTag(name, pos); 1036 } else if (pos.lookingAt("<")) { 1037 String tag = getTag(pos.copy()); 1039 if (tag.endsWith("/>")) { 1040 editor.status("Nothing to match (empty-element tag)"); 1041 return; 1042 } 1043 String name = Utilities.getTagName(tag); 1044 match = findMatchingEndTag(name, pos); 1045 } 1046 1047 if (match != null) { 1048 editor.updateDotLine(); 1049 editor.addUndo(SimpleEdit.MOVE); 1050 dot.moveTo(match); 1051 editor.updateDotLine(); 1052 editor.moveCaretToDotCol(); 1053 } else 1054 editor.status("No match"); 1055 } 1056 1057 public static void xmlInsertMatchingEndTag() 1058 { 1059 final Editor editor = Editor.currentEditor(); 1060 if (!editor.checkReadOnly()) 1061 return; 1062 final Buffer buffer = editor.getBuffer(); 1063 if (buffer.needsRenumbering()) 1064 buffer.renumber(); 1065 if (buffer.needsParsing()) 1066 buffer.getFormatter().parseBuffer(); 1067 final Position dot = editor.getDot(); 1068 String tag = getUnmatchedStartTag(dot); 1069 if (tag != null) { 1070 final String name = Utilities.getTagName(tag); 1071 final String endTag = "</" + name + ">"; 1072 try { 1073 buffer.lockWrite(); 1074 } 1075 catch (InterruptedException e) { 1076 Log.error(e); 1077 return; 1078 } 1079 try { 1080 CompoundEdit compoundEdit = editor.beginCompoundEdit(); 1081 editor.fillToCaret(); 1082 editor.addUndo(SimpleEdit.INSERT_STRING); 1083 editor.insertStringInternal(endTag); 1084 buffer.modified(); 1085 editor.addUndo(SimpleEdit.MOVE); 1086 editor.moveCaretToDotCol(); 1087 editor.endCompoundEdit(compoundEdit); 1088 } 1089 finally { 1090 buffer.unlockWrite(); 1091 } 1092 } 1093 } 1094 1095 public static void xmlElectricSlash() 1096 { 1097 final Editor editor = Editor.currentEditor(); 1098 if (!editor.checkReadOnly()) 1099 return; 1100 final Buffer buffer = editor.getBuffer(); 1101 if (buffer.needsRenumbering()) 1102 buffer.renumber(); 1103 final Position dot = editor.getDotCopy(); 1104 final int offset = dot.getOffset(); 1105 if (offset > 0 && dot.getLine().charAt(offset-1) == '<') { 1106 dot.setOffset(offset-1); 1107 String tag = getUnmatchedStartTag(dot); 1108 if (tag != null) { 1109 final String name = Utilities.getTagName(tag); 1110 final String endTag = "/" + name + ">"; 1111 try { 1112 buffer.lockWrite(); 1113 } 1114 catch (InterruptedException e) { 1115 Log.error(e); 1116 return; 1117 } 1118 try { 1119 editor.fillToCaret(); 1122 editor.addUndo(SimpleEdit.LINE_EDIT); 1123 editor.insertStringInternal(endTag); 1124 buffer.modified(); 1125 editor.moveCaretToDotCol(); 1126 if (buffer.getBooleanProperty(Property.AUTO_INDENT)) 1127 editor.indentLine(); 1128 } 1129 finally { 1130 buffer.unlockWrite(); 1131 } 1132 return; 1133 } 1134 1135 } 1136 editor.insertNormalChar('/'); 1138 } 1139 1140 private static Position findCommentStart(Position start) 1142 { 1143 Position pos = start.copy(); 1144 do { 1145 if (pos.lookingAt(COMMENT_START)) 1146 return pos; 1147 } while (pos.prev()); 1148 return null; 1150 } 1151 1152 private static Position findCommentEnd(Position start) 1154 { 1155 Position pos = start.copy(); 1156 do { 1157 if (pos.lookingAt(COMMENT_END)) 1158 return pos; 1159 } while (pos.next()); 1160 return null; 1162 } 1163} 1164 | Popular Tags |