PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/syntax/XModeHandler.java

#
Java | 711 lines | 553 code | 58 blank | 100 comment | 180 complexity | 689cd4d0fd0cbfc15eb66957beb6b1a1 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * XModeHandler.java - XML handler for mode files
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999 mike dillon
  7. * Portions copyright (C) 2000, 2001 Slava Pestov
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.syntax;
  24. //{{{ Imports
  25. import com.microstar.xml.*;
  26. import gnu.regexp.*;
  27. import java.io.*;
  28. import java.util.*;
  29. import org.gjt.sp.util.Log;
  30. //}}}
  31. /**
  32. * XML handler for mode definition files.
  33. */
  34. public abstract class XModeHandler extends HandlerBase
  35. {
  36. //{{{ XModeHandler constructor
  37. public XModeHandler (String modeName)
  38. {
  39. this.modeName = modeName;
  40. marker = new TokenMarker();
  41. marker.addRuleSet(new ParserRuleSet(modeName,"MAIN"));
  42. stateStack = new Stack();
  43. // default value
  44. lastNoWordSep = "_";
  45. } //}}}
  46. //{{{ resolveEntity() method
  47. public Object resolveEntity(String publicId, String systemId)
  48. {
  49. if("xmode.dtd".equals(systemId))
  50. {
  51. // this will result in a slight speed up, since we
  52. // don't need to read the DTD anyway, as AElfred is
  53. // non-validating
  54. return new StringReader("<!-- -->");
  55. /* try
  56. {
  57. return new BufferedReader(new InputStreamReader(
  58. getClass().getResourceAsStream(
  59. "/org/gjt/sp/jedit/syntax/xmode.dtd")));
  60. }
  61. catch(Exception e)
  62. {
  63. error("dtd",e);
  64. } */
  65. }
  66. return null;
  67. } //}}}
  68. //{{{ attribute() method
  69. public void attribute(String aname, String value, boolean isSpecified)
  70. {
  71. aname = (aname == null) ? null : aname.intern();
  72. if (aname == "NAME")
  73. {
  74. propName = value;
  75. }
  76. else if (aname == "VALUE")
  77. {
  78. propValue = value;
  79. }
  80. else if (aname == "TYPE")
  81. {
  82. lastTokenID = Token.stringToToken(value);
  83. if(lastTokenID == -1)
  84. error("token-invalid",value);
  85. }
  86. else if (aname == "AT_LINE_START")
  87. {
  88. lastAtLineStart = (isSpecified) ? (value.equals("TRUE")) :
  89. false;
  90. }
  91. else if (aname == "AT_WHITESPACE_END")
  92. {
  93. lastAtWhitespaceEnd = (isSpecified) ? (value.equals("TRUE")) :
  94. false;
  95. }
  96. else if (aname == "AT_WORD_START")
  97. {
  98. lastAtWordStart = (isSpecified) ? (value.equals("TRUE")) :
  99. false;
  100. }
  101. else if (aname == "NO_LINE_BREAK")
  102. {
  103. lastNoLineBreak = (isSpecified) ? (value.equals("TRUE")) :
  104. false;
  105. }
  106. else if (aname == "NO_WORD_BREAK")
  107. {
  108. lastNoWordBreak = (isSpecified) ? (value.equals("TRUE")) :
  109. false;
  110. }
  111. else if (aname == "NO_ESCAPE")
  112. {
  113. lastNoEscape = (isSpecified) ? (value.equals("TRUE")) :
  114. false;
  115. }
  116. else if (aname == "EXCLUDE_MATCH")
  117. {
  118. lastExcludeMatch = (isSpecified) ? (value.equals("TRUE")) :
  119. false;
  120. }
  121. else if (aname == "IGNORE_CASE")
  122. {
  123. lastIgnoreCase = (isSpecified) ? (value.equals("TRUE")) :
  124. true;
  125. }
  126. else if (aname == "HIGHLIGHT_DIGITS")
  127. {
  128. lastHighlightDigits = (isSpecified) ? (value.equals("TRUE")) :
  129. false;
  130. }
  131. else if (aname == "DIGIT_RE")
  132. {
  133. lastDigitRE = value;
  134. }
  135. else if (aname == "NO_WORD_SEP")
  136. {
  137. if(isSpecified)
  138. lastNoWordSep = value;
  139. }
  140. else if (aname == "AT_CHAR")
  141. {
  142. try
  143. {
  144. if (isSpecified) termChar =
  145. Integer.parseInt(value);
  146. }
  147. catch (NumberFormatException e)
  148. {
  149. error("termchar-invalid",value);
  150. termChar = -1;
  151. }
  152. }
  153. else if (aname == "ESCAPE")
  154. {
  155. lastEscape = value;
  156. }
  157. else if (aname == "SET")
  158. {
  159. lastSetName = value;
  160. }
  161. else if (aname == "DELEGATE")
  162. {
  163. String delegateMode, delegateSetName;
  164. if(value != null)
  165. {
  166. int index = value.indexOf("::");
  167. if(index != -1)
  168. {
  169. delegateMode = value.substring(0,index);
  170. delegateSetName = value.substring(index + 2);
  171. }
  172. else
  173. {
  174. delegateMode = modeName;
  175. delegateSetName = value;
  176. }
  177. TokenMarker delegateMarker = getTokenMarker(delegateMode);
  178. if(delegateMarker == null)
  179. error("delegate-invalid",value);
  180. else
  181. {
  182. lastDelegateSet = delegateMarker
  183. .getRuleSet(delegateSetName);
  184. if(delegateMarker == marker
  185. && lastDelegateSet == null)
  186. {
  187. // stupid hack to handle referencing
  188. // a rule set that is defined later!
  189. lastDelegateSet = new ParserRuleSet(
  190. delegateMode,
  191. delegateSetName);
  192. lastDelegateSet.setDefault(Token.INVALID);
  193. marker.addRuleSet(lastDelegateSet);
  194. }
  195. else if(lastDelegateSet == null)
  196. error("delegate-invalid",value);
  197. }
  198. }
  199. }
  200. else if (aname == "DEFAULT")
  201. {
  202. lastDefaultID = Token.stringToToken(value);
  203. if(lastDefaultID == -1)
  204. {
  205. error("token-invalid",value);
  206. lastDefaultID = Token.NULL;
  207. }
  208. }
  209. else if (aname == "HASH_CHAR")
  210. {
  211. if(value.length() != 1)
  212. {
  213. error("hash-char-invalid",value);
  214. lastDefaultID = Token.NULL;
  215. }
  216. else
  217. lastHashChar = value.charAt(0);
  218. }
  219. } //}}}
  220. //{{{ doctypeDecl() method
  221. public void doctypeDecl(String name, String publicId,
  222. String systemId) throws Exception
  223. {
  224. if ("MODE".equalsIgnoreCase(name)) return;
  225. error("doctype-invalid",name);
  226. } //}}}
  227. //{{{ charData() method
  228. public void charData(char[] c, int off, int len)
  229. {
  230. String tag = peekElement();
  231. String text = new String(c, off, len);
  232. if (tag == "EOL_SPAN" ||
  233. tag == "EOL_SPAN_REGEXP" ||
  234. tag == "MARK_PREVIOUS" ||
  235. tag == "MARK_FOLLOWING" ||
  236. tag == "SEQ" ||
  237. tag == "SEQ_REGEXP" ||
  238. tag == "BEGIN"
  239. )
  240. {
  241. lastStart = text;
  242. lastStartPosMatch = ((lastAtLineStart ? ParserRule.AT_LINE_START : 0)
  243. | (lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0)
  244. | (lastAtWordStart ? ParserRule.AT_WORD_START : 0));
  245. lastAtLineStart = false;
  246. lastAtWordStart = false;
  247. lastAtWhitespaceEnd = false;
  248. }
  249. else if (tag == "END")
  250. {
  251. lastEnd = text;
  252. lastEndPosMatch = ((lastAtLineStart ? ParserRule.AT_LINE_START : 0)
  253. | (lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0)
  254. | (lastAtWordStart ? ParserRule.AT_WORD_START : 0));
  255. lastAtLineStart = false;
  256. lastAtWordStart = false;
  257. lastAtWhitespaceEnd = false;
  258. }
  259. else
  260. {
  261. lastKeyword = text;
  262. }
  263. } //}}}
  264. //{{{ startElement() method
  265. public void startElement (String tag)
  266. {
  267. tag = pushElement(tag);
  268. if (tag == "WHITESPACE")
  269. {
  270. Log.log(Log.WARNING,this,modeName + ": WHITESPACE rule "
  271. + "no longer needed");
  272. }
  273. else if (tag == "KEYWORDS")
  274. {
  275. keywords = new KeywordMap(rules.getIgnoreCase());
  276. }
  277. else if (tag == "RULES")
  278. {
  279. if(lastSetName == null)
  280. lastSetName = "MAIN";
  281. rules = marker.getRuleSet(lastSetName);
  282. if(rules == null)
  283. {
  284. rules = new ParserRuleSet(modeName,lastSetName);
  285. marker.addRuleSet(rules);
  286. }
  287. rules.setIgnoreCase(lastIgnoreCase);
  288. rules.setHighlightDigits(lastHighlightDigits);
  289. if(lastDigitRE != null)
  290. {
  291. try
  292. {
  293. rules.setDigitRegexp(new RE(lastDigitRE,
  294. lastIgnoreCase
  295. ? RE.REG_ICASE : 0,
  296. ParserRule.RE_SYNTAX_JEDIT));
  297. }
  298. catch(REException e)
  299. {
  300. error("regexp",e);
  301. }
  302. }
  303. if(lastEscape != null)
  304. rules.setEscapeRule(ParserRule.createEscapeRule(lastEscape));
  305. rules.setDefault(lastDefaultID);
  306. rules.setNoWordSep(lastNoWordSep);
  307. }
  308. } //}}}
  309. //{{{ endElement() method
  310. public void endElement (String name)
  311. {
  312. if (name == null) return;
  313. String tag = popElement();
  314. if (name.equals(tag))
  315. {
  316. //{{{ PROPERTY
  317. if (tag == "PROPERTY")
  318. {
  319. props.put(propName,propValue);
  320. } //}}}
  321. //{{{ PROPS
  322. else if (tag == "PROPS")
  323. {
  324. if(peekElement().equals("RULES"))
  325. rules.setProperties(props);
  326. else
  327. modeProps = props;
  328. props = new Hashtable();
  329. } //}}}
  330. //{{{ RULES
  331. else if (tag == "RULES")
  332. {
  333. rules.setKeywords(keywords);
  334. keywords = null;
  335. lastSetName = null;
  336. lastEscape = null;
  337. lastIgnoreCase = true;
  338. lastHighlightDigits = false;
  339. lastDigitRE = null;
  340. lastDefaultID = Token.NULL;
  341. lastNoWordSep = "_";
  342. rules = null;
  343. } //}}}
  344. //{{{ IMPORT
  345. else if (tag == "IMPORT")
  346. {
  347. rules.addRuleSet(lastDelegateSet);
  348. lastDelegateSet = null;
  349. } //}}}
  350. //{{{ TERMINATE
  351. else if (tag == "TERMINATE")
  352. {
  353. rules.setTerminateChar(termChar);
  354. termChar = -1;
  355. } //}}}
  356. //{{{ SEQ
  357. else if (tag == "SEQ")
  358. {
  359. if(lastStart == null)
  360. {
  361. error("empty-tag","SEQ");
  362. return;
  363. }
  364. rules.addRule(ParserRule.createSequenceRule(
  365. lastStartPosMatch,lastStart,lastDelegateSet,
  366. lastTokenID));
  367. reset();
  368. } //}}}
  369. //{{{ SEQ_REGEXP
  370. else if (tag == "SEQ_REGEXP")
  371. {
  372. if(lastStart == null)
  373. {
  374. error("empty-tag","SEQ_REGEXP");
  375. return;
  376. }
  377. try
  378. {
  379. rules.addRule(ParserRule.createRegexpSequenceRule(
  380. lastHashChar,lastStartPosMatch,
  381. lastStart,lastDelegateSet,lastTokenID,
  382. lastIgnoreCase));
  383. }
  384. catch(REException re)
  385. {
  386. error("regexp",re);
  387. }
  388. reset();
  389. } //}}}
  390. //{{{ SPAN
  391. else if (tag == "SPAN")
  392. {
  393. if(lastStart == null)
  394. {
  395. error("empty-tag","BEGIN");
  396. return;
  397. }
  398. if(lastEnd == null)
  399. {
  400. error("empty-tag","END");
  401. return;
  402. }
  403. rules.addRule(ParserRule
  404. .createSpanRule(
  405. lastStartPosMatch,lastStart,
  406. lastEndPosMatch,lastEnd,
  407. lastDelegateSet,
  408. lastTokenID,lastExcludeMatch,
  409. lastNoLineBreak,
  410. lastNoWordBreak,
  411. lastNoEscape));
  412. reset();
  413. } //}}}
  414. //{{{ SPAN_REGEXP
  415. else if (tag == "SPAN_REGEXP")
  416. {
  417. if(lastStart == null)
  418. {
  419. error("empty-tag","BEGIN");
  420. return;
  421. }
  422. if(lastEnd == null)
  423. {
  424. error("empty-tag","END");
  425. return;
  426. }
  427. try
  428. {
  429. rules.addRule(ParserRule
  430. .createRegexpSpanRule(
  431. lastHashChar,
  432. lastStartPosMatch,lastStart,
  433. lastEndPosMatch,lastEnd,
  434. lastDelegateSet,
  435. lastTokenID,
  436. lastExcludeMatch,
  437. lastNoLineBreak,
  438. lastNoWordBreak,
  439. lastIgnoreCase,
  440. lastNoEscape));
  441. }
  442. catch(REException re)
  443. {
  444. error("regexp",re);
  445. }
  446. reset();
  447. } //}}}
  448. //{{{ EOL_SPAN
  449. else if (tag == "EOL_SPAN")
  450. {
  451. if(lastStart == null)
  452. {
  453. error("empty-tag","EOL_SPAN");
  454. return;
  455. }
  456. rules.addRule(ParserRule.createEOLSpanRule(
  457. lastStartPosMatch,lastStart,
  458. lastDelegateSet,lastTokenID,
  459. lastExcludeMatch));
  460. reset();
  461. } //}}}
  462. //{{{ EOL_SPAN_REGEXP
  463. else if (tag == "EOL_SPAN_REGEXP")
  464. {
  465. if(lastStart == null)
  466. {
  467. error("empty-tag","EOL_SPAN_REGEXP");
  468. return;
  469. }
  470. try
  471. {
  472. rules.addRule(ParserRule.createRegexpEOLSpanRule(
  473. lastHashChar,lastStartPosMatch,lastStart,
  474. lastDelegateSet,lastTokenID,
  475. lastExcludeMatch,lastIgnoreCase));
  476. }
  477. catch(REException re)
  478. {
  479. error("regexp",re);
  480. }
  481. reset();
  482. } //}}}
  483. //{{{ MARK_FOLLOWING
  484. else if (tag == "MARK_FOLLOWING")
  485. {
  486. if(lastStart == null)
  487. {
  488. error("empty-tag","MARK_FOLLOWING");
  489. return;
  490. }
  491. rules.addRule(ParserRule
  492. .createMarkFollowingRule(
  493. lastStartPosMatch,lastStart,
  494. lastTokenID,lastExcludeMatch));
  495. reset();
  496. } //}}}
  497. //{{{ MARK_PREVIOUS
  498. else if (tag == "MARK_PREVIOUS")
  499. {
  500. if(lastStart == null)
  501. {
  502. error("empty-tag","MARK_PREVIOUS");
  503. return;
  504. }
  505. rules.addRule(ParserRule
  506. .createMarkPreviousRule(
  507. lastStartPosMatch,lastStart,
  508. lastTokenID,lastExcludeMatch));
  509. reset();
  510. } //}}}
  511. //{{{ Keywords
  512. else
  513. {
  514. byte token = Token.stringToToken(tag);
  515. if(token != -1)
  516. addKeyword(lastKeyword,token);
  517. } //}}}
  518. }
  519. else
  520. {
  521. // can't happen
  522. throw new InternalError();
  523. }
  524. } //}}}
  525. //{{{ startDocument() method
  526. public void startDocument()
  527. {
  528. props = new Hashtable();
  529. pushElement(null);
  530. } //}}}
  531. //{{{ endDocument() method
  532. public void endDocument()
  533. {
  534. ParserRuleSet[] rulesets = marker.getRuleSets();
  535. for(int i = 0; i < rulesets.length; i++)
  536. {
  537. rulesets[i].resolveImports();
  538. }
  539. } //}}}
  540. //{{{ getTokenMarker() method
  541. public TokenMarker getTokenMarker()
  542. {
  543. return marker;
  544. } //}}}
  545. //{{{ getModeProperties() method
  546. public Hashtable getModeProperties()
  547. {
  548. return modeProps;
  549. } //}}}
  550. //{{{ Protected members
  551. //{{{ error() method
  552. /**
  553. * Reports an error.
  554. * You must override this method so that the mode loader can do error
  555. * reporting.
  556. * @param msg The error type
  557. * @param subst A <code>String</code> or a <code>Throwable</code>
  558. * containing specific information
  559. * @since jEdit 4.2pre1
  560. */
  561. protected abstract void error(String msg, Object subst);
  562. //}}}
  563. //{{{ getTokenMarker() method
  564. /**
  565. * Returns the token marker for the given mode.
  566. * You must override this method so that the mode loader can resolve
  567. * delegate targets.
  568. * @param mode The mode name
  569. * @since jEdit 4.2pre1
  570. */
  571. protected abstract TokenMarker getTokenMarker(String mode);
  572. //}}}
  573. //}}}
  574. //{{{ Private members
  575. //{{{ Instance variables
  576. private String modeName;
  577. private TokenMarker marker;
  578. private KeywordMap keywords;
  579. private Stack stateStack;
  580. private String propName;
  581. private String propValue;
  582. private Hashtable props;
  583. private Hashtable modeProps;
  584. private String lastStart;
  585. private String lastEnd;
  586. private String lastKeyword;
  587. private String lastSetName;
  588. private String lastEscape;
  589. private ParserRuleSet lastDelegateSet;
  590. private String lastNoWordSep;
  591. private ParserRuleSet rules;
  592. private byte lastDefaultID = Token.NULL;
  593. private byte lastTokenID;
  594. private int termChar = -1;
  595. private boolean lastNoLineBreak;
  596. private boolean lastNoWordBreak;
  597. private boolean lastExcludeMatch;
  598. private boolean lastIgnoreCase = true;
  599. private boolean lastHighlightDigits;
  600. private boolean lastAtLineStart;
  601. private boolean lastAtWhitespaceEnd;
  602. private boolean lastAtWordStart;
  603. private boolean lastNoEscape;
  604. private int lastStartPosMatch;
  605. private int lastEndPosMatch;
  606. private String lastDigitRE;
  607. private char lastHashChar;
  608. //}}}
  609. //{{{ reset() method
  610. private void reset()
  611. {
  612. lastHashChar = '\0';
  613. lastStartPosMatch = 0;
  614. lastStart = null;
  615. lastEndPosMatch = 0;
  616. lastEnd = null;
  617. lastDelegateSet = null;
  618. lastTokenID = Token.NULL;
  619. lastExcludeMatch = false;
  620. lastNoLineBreak = false;
  621. lastNoWordBreak = false;
  622. lastNoEscape = false;
  623. } //}}}
  624. //{{{ addKeyword() method
  625. private void addKeyword(String k, byte id)
  626. {
  627. if(k == null)
  628. {
  629. error("empty-keyword",null);
  630. return;
  631. }
  632. if (keywords == null) return;
  633. keywords.add(k,id);
  634. } //}}}
  635. //{{{ pushElement() method
  636. private String pushElement(String name)
  637. {
  638. name = (name == null) ? null : name.intern();
  639. stateStack.push(name);
  640. return name;
  641. } //}}}
  642. //{{{ peekElement() method
  643. private String peekElement()
  644. {
  645. return (String) stateStack.peek();
  646. } //}}}
  647. //{{{ popElement() method
  648. private String popElement()
  649. {
  650. return (String) stateStack.pop();
  651. } //}}}
  652. //}}}
  653. }