1 23 24 package org.gjt.sp.jedit.syntax; 25 26 import java.util.*; 28 import java.util.regex.Pattern ; 29 import java.util.regex.PatternSyntaxException ; 30 31 import org.xml.sax.Attributes ; 32 import org.xml.sax.InputSource ; 33 import org.xml.sax.helpers.DefaultHandler ; 34 35 import org.gjt.sp.util.Log; 36 import org.gjt.sp.util.XMLUtilities; 37 39 43 public abstract class XModeHandler extends DefaultHandler 44 { 45 public XModeHandler (String modeName) 47 { 48 this.modeName = modeName; 49 marker = new TokenMarker(); 50 marker.addRuleSet(new ParserRuleSet(modeName,"MAIN")); 51 stateStack = new Stack<TagDecl>(); 52 } 54 public InputSource resolveEntity(String publicId, String systemId) 56 { 57 return XMLUtilities.findEntity(systemId, "xmode.dtd", XModeHandler.class); 58 } 60 public void characters(char[] c, int off, int len) 62 { 63 peekElement().setText(c, off, len); 64 } 66 public void startElement(String uri, String localName, 68 String qName, Attributes attrs) 69 { 70 TagDecl tag = pushElement(qName, attrs); 71 72 if (qName.equals("WHITESPACE")) 73 { 74 Log.log(Log.WARNING,this,modeName + ": WHITESPACE rule " 75 + "no longer needed"); 76 } 77 else if (qName.equals("KEYWORDS")) 78 { 79 keywords = new KeywordMap(rules.getIgnoreCase()); 80 } 81 else if (qName.equals("RULES")) 82 { 83 if(tag.lastSetName == null) 84 tag.lastSetName = "MAIN"; 85 rules = marker.getRuleSet(tag.lastSetName); 86 if(rules == null) 87 { 88 rules = new ParserRuleSet(modeName,tag.lastSetName); 89 marker.addRuleSet(rules); 90 } 91 rules.setIgnoreCase(tag.lastIgnoreCase); 92 rules.setHighlightDigits(tag.lastHighlightDigits); 93 if(tag.lastDigitRE != null) 94 { 95 try 96 { 97 rules.setDigitRegexp(Pattern.compile(tag.lastDigitRE, 98 tag.lastIgnoreCase 99 ? Pattern.CASE_INSENSITIVE : 0)); 100 } 101 catch(PatternSyntaxException e) 102 { 103 error("regexp",e); 104 } 105 } 106 107 if(tag.lastEscape != null) 108 rules.setEscapeRule(ParserRule.createEscapeRule(tag.lastEscape)); 109 rules.setDefault(tag.lastDefaultID); 110 rules.setNoWordSep(tag.lastNoWordSep); 111 } 112 } 114 public void endElement(String uri, String localName, String name) 116 { 117 TagDecl tag = popElement(); 118 if (name.equals(tag.tagName)) 119 { 120 if (tag.tagName.equals("PROPERTY")) 122 { 123 props.put(propName,propValue); 124 } else if (tag.tagName.equals("PROPS")) 127 { 128 if(peekElement().tagName.equals("RULES")) 129 rules.setProperties(props); 130 else 131 modeProps = props; 132 133 props = new Hashtable<String , String >(); 134 } else if (tag.tagName.equals("RULES")) 137 { 138 rules.setKeywords(keywords); 139 keywords = null; 140 rules = null; 141 } else if (tag.tagName.equals("IMPORT")) 144 { 145 if (!rules.getSetName().equals(tag.lastDelegateSet.getSetName())) 147 { 148 rules.addRuleSet(tag.lastDelegateSet); 149 } 150 } else if (tag.tagName.equals("TERMINATE")) 153 { 154 rules.setTerminateChar(tag.termChar); 155 } else if (tag.tagName.equals("SEQ")) 158 { 159 if(tag.lastStart == null) 160 { 161 error("empty-tag","SEQ"); 162 return; 163 } 164 165 rules.addRule(ParserRule.createSequenceRule( 166 tag.lastStartPosMatch,tag.lastStart.toString(), 167 tag.lastDelegateSet,tag.lastTokenID)); 168 } else if (tag.tagName.equals("SEQ_REGEXP")) 171 { 172 if(tag.lastStart == null) 173 { 174 error("empty-tag","SEQ_REGEXP"); 175 return; 176 } 177 178 try 179 { 180 if (null != tag.lastHashChars) 181 { 182 rules.addRule(ParserRule.createRegexpSequenceRule( 183 tag.lastStartPosMatch,tag.lastHashChars.toCharArray(), 184 tag.lastStart.toString(),tag.lastDelegateSet, 185 tag.lastTokenID,findParent("RULES").lastIgnoreCase)); 186 } 187 else 188 { 189 rules.addRule(ParserRule.createRegexpSequenceRule( 190 tag.lastHashChar,tag.lastStartPosMatch, 191 tag.lastStart.toString(),tag.lastDelegateSet, 192 tag.lastTokenID,findParent("RULES").lastIgnoreCase)); 193 } 194 } 195 catch(PatternSyntaxException re) 196 { 197 error("regexp",re); 198 } 199 } else if (tag.tagName.equals("SPAN")) 202 { 203 if(tag.lastStart == null) 204 { 205 error("empty-tag","BEGIN"); 206 return; 207 } 208 209 if(tag.lastEnd == null) 210 { 211 error("empty-tag","END"); 212 return; 213 } 214 215 rules.addRule(ParserRule 216 .createSpanRule( 217 tag.lastStartPosMatch,tag.lastStart.toString(), 218 tag.lastEndPosMatch,tag.lastEnd.toString(), 219 tag.lastDelegateSet, 220 tag.lastTokenID,tag.lastExcludeMatch, 221 tag.lastNoLineBreak, 222 tag.lastNoWordBreak, 223 tag.lastNoEscape)); 224 } else if (tag.tagName.equals("SPAN_REGEXP")) 227 { 228 if(tag.lastStart == null) 229 { 230 error("empty-tag","BEGIN"); 231 return; 232 } 233 234 if(tag.lastEnd == null) 235 { 236 error("empty-tag","END"); 237 return; 238 } 239 240 try 241 { 242 if (null != tag.lastHashChars) 243 { 244 rules.addRule(ParserRule 245 .createRegexpSpanRule( 246 tag.lastStartPosMatch,tag.lastHashChars.toCharArray(), 247 tag.lastStart.toString(), 248 tag.lastEndPosMatch,tag.lastEnd.toString(), 249 tag.lastDelegateSet, 250 tag.lastTokenID, 251 tag.lastExcludeMatch, 252 tag.lastNoLineBreak, 253 tag.lastNoWordBreak, 254 findParent("RULES").lastIgnoreCase, 255 tag.lastNoEscape)); 256 } 257 else 258 { 259 rules.addRule(ParserRule 260 .createRegexpSpanRule( 261 tag.lastHashChar, 262 tag.lastStartPosMatch,tag.lastStart.toString(), 263 tag.lastEndPosMatch,tag.lastEnd.toString(), 264 tag.lastDelegateSet, 265 tag.lastTokenID, 266 tag.lastExcludeMatch, 267 tag.lastNoLineBreak, 268 tag.lastNoWordBreak, 269 findParent("RULES").lastIgnoreCase, 270 tag.lastNoEscape)); 271 } 272 } 273 catch(PatternSyntaxException re) 274 { 275 error("regexp",re); 276 } 277 } else if (tag.tagName.equals("EOL_SPAN")) 280 { 281 if(tag.lastStart == null) 282 { 283 error("empty-tag","EOL_SPAN"); 284 return; 285 } 286 287 rules.addRule(ParserRule.createEOLSpanRule( 288 tag.lastStartPosMatch,tag.lastStart.toString(), 289 tag.lastDelegateSet,tag.lastTokenID, 290 tag.lastExcludeMatch)); 291 } else if (tag.tagName.equals("EOL_SPAN_REGEXP")) 294 { 295 if(tag.lastStart == null) 296 { 297 error("empty-tag","EOL_SPAN_REGEXP"); 298 return; 299 } 300 301 try 302 { 303 if (null != tag.lastHashChars) 304 { 305 rules.addRule(ParserRule.createRegexpEOLSpanRule( 306 tag.lastStartPosMatch,tag.lastHashChars.toCharArray(), 307 tag.lastStart.toString(),tag.lastDelegateSet, 308 tag.lastTokenID,tag.lastExcludeMatch, 309 findParent("RULES").lastIgnoreCase)); 310 } 311 else 312 { 313 rules.addRule(ParserRule.createRegexpEOLSpanRule( 314 tag.lastHashChar,tag.lastStartPosMatch, 315 tag.lastStart.toString(),tag.lastDelegateSet, 316 tag.lastTokenID,tag.lastExcludeMatch, 317 findParent("RULES").lastIgnoreCase)); 318 } 319 } 320 catch(PatternSyntaxException re) 321 { 322 error("regexp",re); 323 } 324 } else if (tag.tagName.equals("MARK_FOLLOWING")) 327 { 328 if(tag.lastStart == null) 329 { 330 error("empty-tag","MARK_FOLLOWING"); 331 return; 332 } 333 334 rules.addRule(ParserRule 335 .createMarkFollowingRule( 336 tag.lastStartPosMatch,tag.lastStart.toString(), 337 tag.lastTokenID,tag.lastExcludeMatch)); 338 } else if (tag.tagName.equals("MARK_PREVIOUS")) 341 { 342 if(tag.lastStart == null) 343 { 344 error("empty-tag","MARK_PREVIOUS"); 345 return; 346 } 347 348 rules.addRule(ParserRule 349 .createMarkPreviousRule( 350 tag.lastStartPosMatch,tag.lastStart.toString(), 351 tag.lastTokenID,tag.lastExcludeMatch)); 352 } else if ( 355 !tag.tagName.equals("END") 356 && !tag.tagName.equals("BEGIN") 357 && !tag.tagName.equals("KEYWORDS") 358 && !tag.tagName.equals("MODE") 359 ) { 360 byte token = Token.stringToToken(tag.tagName); 361 if(token != -1) 362 addKeyword(tag.lastKeyword.toString(),token); 363 } } 365 else 366 { 367 throw new InternalError (); 369 } 370 } 372 public void startDocument() 374 { 375 props = new Hashtable<String , String >(); 376 pushElement(null, null); 377 } 379 public void endDocument() 381 { 382 ParserRuleSet[] rulesets = marker.getRuleSets(); 383 for(int i = 0; i < rulesets.length; i++) 384 { 385 rulesets[i].resolveImports(); 386 } 387 } 389 395 public TokenMarker getTokenMarker() 396 { 397 return marker; 398 } 400 public Hashtable<String , String > getModeProperties() 402 { 403 return modeProps; 404 } 406 408 418 protected abstract void error(String msg, Object subst); 419 421 429 protected abstract TokenMarker getTokenMarker(String mode); 430 432 434 436 private String modeName; 438 439 private final TokenMarker marker; 440 private KeywordMap keywords; 441 442 private Stack<TagDecl> stateStack; 443 private String propName; 444 private String propValue; 445 private Hashtable<String , String > props; 446 private Hashtable<String , String > modeProps; 447 private ParserRuleSet rules; 448 450 private void addKeyword(String k, byte id) 452 { 453 if(k == null) 454 { 455 error("empty-keyword",null); 456 return; 457 } 458 459 if (keywords == null) return; 460 keywords.add(k,id); 461 } 463 private TagDecl pushElement(String name, Attributes attrs) 465 { 466 if (name != null) 467 { 468 TagDecl tag = new TagDecl(name, attrs); 469 stateStack.push(tag); 470 return tag; 471 } 472 else 473 { 474 stateStack.push(null); 475 return null; 476 } 477 } 479 private TagDecl peekElement() 481 { 482 return stateStack.peek(); 483 } 485 private TagDecl popElement() 487 { 488 return stateStack.pop(); 489 } 491 496 private TagDecl findParent(String tagName) 497 { 498 for (int idx = stateStack.size() - 1; idx >= 0; idx--) 499 { 500 TagDecl tag = stateStack.get(idx); 501 if (tag.tagName.equals(tagName)) 502 return tag; 503 } 504 return null; 505 } 507 509 514 private class TagDecl 515 { 516 517 public TagDecl(String tagName, Attributes attrs) 518 { 519 this.tagName = tagName; 520 521 String tmp; 522 523 propName = attrs.getValue("NAME"); 524 propValue = attrs.getValue("VALUE"); 525 526 tmp = attrs.getValue("TYPE"); 527 if (tmp != null) 528 { 529 lastTokenID = Token.stringToToken(tmp); 530 if(lastTokenID == -1) 531 error("token-invalid",tmp); 532 } 533 534 lastAtLineStart = "TRUE".equals(attrs.getValue("AT_LINE_START")); 535 lastAtWhitespaceEnd = "TRUE".equals(attrs.getValue("AT_WHITESPACE_END")); 536 lastAtWordStart = "TRUE".equals(attrs.getValue("AT_WORD_START")); 537 lastNoLineBreak = "TRUE".equals(attrs.getValue("NO_LINE_BREAK")); 538 lastNoWordBreak = "TRUE".equals(attrs.getValue("NO_WORD_BREAK")); 539 lastNoEscape = "TRUE".equals(attrs.getValue("NO_ESCAPE")); 540 lastExcludeMatch = "TRUE".equals(attrs.getValue("EXCLUDE_MATCH")); 541 lastIgnoreCase = (attrs.getValue("IGNORE_CASE") == null || 542 "TRUE".equals(attrs.getValue("IGNORE_CASE"))); 543 lastHighlightDigits = "TRUE".equals(attrs.getValue("HIGHLIGHT_DIGITS"));; 544 lastDigitRE = attrs.getValue("DIGIT_RE"); 545 546 tmp = attrs.getValue("NO_WORD_SEP"); 547 if (tmp != null) 548 lastNoWordSep = tmp; 549 550 tmp = attrs.getValue("AT_CHAR"); 551 if (tmp != null) 552 { 553 try 554 { 555 termChar = Integer.parseInt(tmp); 556 } 557 catch (NumberFormatException e) 558 { 559 error("termchar-invalid",tmp); 560 termChar = -1; 561 } 562 } 563 564 lastEscape = attrs.getValue("ESCAPE"); 565 lastSetName = attrs.getValue("SET"); 566 567 tmp = attrs.getValue("DELEGATE"); 568 if (tmp != null) 569 { 570 String delegateMode, delegateSetName; 571 572 int index = tmp.indexOf("::"); 573 574 if(index != -1) 575 { 576 delegateMode = tmp.substring(0,index); 577 delegateSetName = tmp.substring(index + 2); 578 } 579 else 580 { 581 delegateMode = modeName; 582 delegateSetName = tmp; 583 } 584 585 TokenMarker delegateMarker = getTokenMarker(delegateMode); 586 if(delegateMarker == null) 587 error("delegate-invalid",tmp); 588 else 589 { 590 lastDelegateSet = delegateMarker 591 .getRuleSet(delegateSetName); 592 if(delegateMarker == marker 593 && lastDelegateSet == null) 594 { 595 lastDelegateSet = new ParserRuleSet( 598 delegateMode, 599 delegateSetName); 600 lastDelegateSet.setDefault(Token.INVALID); 601 marker.addRuleSet(lastDelegateSet); 602 } 603 else if(lastDelegateSet == null) 604 error("delegate-invalid",tmp); 605 } 606 } 607 608 tmp = attrs.getValue("DEFAULT"); 609 if (tmp != null) 610 { 611 lastDefaultID = Token.stringToToken(tmp); 612 if(lastDefaultID == -1) 613 { 614 error("token-invalid",tmp); 615 lastDefaultID = Token.NULL; 616 } 617 } 618 619 lastHashChar = attrs.getValue("HASH_CHAR"); 620 lastHashChars = attrs.getValue("HASH_CHARS"); 621 if ((null != lastHashChar) && (null != lastHashChars)) 622 { 623 error("hash-char-and-hash-chars-mutually-exclusive",null); 624 lastHashChars = null; 625 } 626 } 627 628 public void setText(char[] c, int off, int len) 629 { 630 if (tagName.equals("EOL_SPAN") || 631 tagName.equals("EOL_SPAN_REGEXP") || 632 tagName.equals("MARK_PREVIOUS") || 633 tagName.equals("MARK_FOLLOWING") || 634 tagName.equals("SEQ") || 635 tagName.equals("SEQ_REGEXP") || 636 tagName.equals("BEGIN") 637 ) 638 { 639 TagDecl target = this; 640 if (tagName.equals("BEGIN")) 641 target = stateStack.get(stateStack.size() - 2); 642 643 if (target.lastStart == null) 644 { 645 target.lastStart = new StringBuffer (); 646 target.lastStart.append(c, off, len); 647 target.lastStartPosMatch = ((target.lastAtLineStart ? ParserRule.AT_LINE_START : 0) 648 | (target.lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0) 649 | (target.lastAtWordStart ? ParserRule.AT_WORD_START : 0)); 650 target.lastAtLineStart = false; 651 target.lastAtWordStart = false; 652 target.lastAtWhitespaceEnd = false; 653 } 654 else 655 { 656 target.lastStart.append(c, off, len); 657 } 658 } 659 else if (tagName.equals("END")) 660 { 661 TagDecl target = stateStack.get(stateStack.size() - 2); 662 if (target.lastEnd == null) 663 { 664 target.lastEnd = new StringBuffer (); 665 target.lastEnd.append(c, off, len); 666 target.lastEndPosMatch = ((target.lastAtLineStart ? ParserRule.AT_LINE_START : 0) 667 | (target.lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0) 668 | (target.lastAtWordStart ? ParserRule.AT_WORD_START : 0)); 669 target.lastAtLineStart = false; 670 target.lastAtWordStart = false; 671 target.lastAtWhitespaceEnd = false; 672 } 673 else 674 { 675 target.lastEnd.append(c, off, len); 676 } 677 } 678 else 679 { 680 if (lastKeyword == null) 681 lastKeyword = new StringBuffer (); 682 lastKeyword.append(c, off, len); 683 } 684 } 685 686 public String tagName; 687 public StringBuffer lastStart; 688 public StringBuffer lastEnd; 689 public StringBuffer lastKeyword; 690 public String lastSetName; 691 public String lastEscape; 692 public ParserRuleSet lastDelegateSet; 693 public String lastNoWordSep = "_"; 694 public ParserRuleSet rules; 695 public byte lastDefaultID = Token.NULL; 696 public byte lastTokenID; 697 public int termChar = -1; 698 public boolean lastNoLineBreak; 699 public boolean lastNoWordBreak; 700 public boolean lastExcludeMatch; 701 public boolean lastIgnoreCase = true; 702 public boolean lastHighlightDigits; 703 public boolean lastAtLineStart; 704 public boolean lastAtWhitespaceEnd; 705 public boolean lastAtWordStart; 706 public boolean lastNoEscape; 707 public int lastStartPosMatch; 708 public int lastEndPosMatch; 709 public String lastDigitRE; 710 public String lastHashChar; 711 public String lastHashChars; 712 } 713 } 714 | Popular Tags |