/jEdit/tags/jedit-4-3/org/gjt/sp/jedit/syntax/TokenMarker.java

#
Java | 937 lines | 674 code | 112 blank | 151 comment | 193 complexity | 3e96714a613b7355efa7c14814dc1be7 MD5 | raw file

✨ Summary
  1. /*
  2. * TokenMarker.java - Tokenizes lines of text
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1998, 2003 Slava Pestov
  7. * Copyright (C) 1999, 2000 mike dillon
  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 javax.swing.text.Segment;
  26. import java.util.*;
  27. import java.util.regex.Matcher;
  28. import java.util.regex.Pattern;
  29. import org.gjt.sp.jedit.TextUtilities;
  30. import org.gjt.sp.util.SegmentCharSequence;
  31. import org.gjt.sp.util.StandardUtilities;
  32. //}}}
  33. /**
  34. * A token marker splits lines of text into tokens. Each token carries
  35. * a length field and an identification tag that can be mapped to a color
  36. * or font style for painting that token.
  37. *
  38. * @author Slava Pestov, mike dillon
  39. * @version $Id: TokenMarker.java 16344 2009-10-14 10:31:01Z kpouer $
  40. *
  41. * @see org.gjt.sp.jedit.syntax.Token
  42. * @see org.gjt.sp.jedit.syntax.TokenHandler
  43. */
  44. public class TokenMarker
  45. {
  46. //{{{ TokenMarker constructor
  47. public TokenMarker()
  48. {} //}}}
  49. //{{{ addRuleSet() method
  50. public void addRuleSet(ParserRuleSet rules)
  51. {
  52. ruleSets.put(rules.getSetName(), rules);
  53. if (rules.getSetName().equals("MAIN"))
  54. mainRuleSet = rules;
  55. } //}}}
  56. //{{{ getMainRuleSet() method
  57. public ParserRuleSet getMainRuleSet()
  58. {
  59. return mainRuleSet;
  60. } //}}}
  61. //{{{ getRuleSet() method
  62. public ParserRuleSet getRuleSet(String setName)
  63. {
  64. return ruleSets.get(setName);
  65. } //}}}
  66. //{{{ getRuleSets() method
  67. /**
  68. * @since jEdit 4.2pre3
  69. */
  70. public ParserRuleSet[] getRuleSets()
  71. {
  72. return ruleSets.values().toArray(new ParserRuleSet[ruleSets.size()]);
  73. } //}}}
  74. //{{{ markTokens() method
  75. /**
  76. * Do not call this method directly; call Buffer.markTokens() instead.
  77. *
  78. * @param prevContext the context of the previous line, it can be null
  79. * @param tokenHandler the token handler
  80. * @param line a segment containing the content of the line
  81. */
  82. public synchronized LineContext markTokens(LineContext prevContext,
  83. TokenHandler tokenHandler, Segment line)
  84. {
  85. //{{{ Set up some instance variables
  86. // this is to avoid having to pass around lots and lots of
  87. // parameters.
  88. this.tokenHandler = tokenHandler;
  89. this.line = line;
  90. lastOffset = line.offset;
  91. lineLength = line.count + line.offset;
  92. context = new LineContext();
  93. if(prevContext == null)
  94. {
  95. context.rules = getMainRuleSet();
  96. context.escapeRule = context.rules.getEscapeRule();
  97. }
  98. else
  99. {
  100. context.parent = prevContext.parent;
  101. context.setInRule(prevContext.inRule);
  102. context.rules = prevContext.rules;
  103. context.spanEndSubst = prevContext.spanEndSubst;
  104. }
  105. keywords = context.rules.getKeywords();
  106. seenWhitespaceEnd = false;
  107. whitespaceEnd = line.offset;
  108. //}}}
  109. //{{{ Main parser loop
  110. int terminateChar = context.rules.getTerminateChar();
  111. boolean terminated = false;
  112. main_loop: for(pos = line.offset; pos < lineLength; pos++)
  113. {
  114. //{{{ check if we have to stop parsing (happens if the terminateChar has been exceeded)
  115. if(terminateChar >= 0 && pos - line.offset >= terminateChar
  116. && !terminated)
  117. {
  118. terminated = true;
  119. context = new LineContext(ParserRuleSet
  120. .getStandardRuleSet(context.rules
  121. .getDefault()),context);
  122. keywords = context.rules.getKeywords();
  123. } //}}}
  124. //{{{ Check for the escape rule before anything else.
  125. if (context.escapeRule != null &&
  126. handleRule(context.escapeRule,false))
  127. {
  128. continue main_loop;
  129. } //}}}
  130. //{{{ check for end of delegate
  131. if (context.parent != null
  132. && context.parent.inRule != null
  133. && checkDelegateEnd(context.parent.inRule))
  134. {
  135. seenWhitespaceEnd = true;
  136. continue main_loop;
  137. } //}}}
  138. //{{{ check every rule
  139. Character ch = Character.valueOf(line.array[pos]);
  140. List<ParserRule> rules = context.rules.getRules(ch);
  141. for (ParserRule rule : rules)
  142. {
  143. // stop checking rules if there was a match
  144. if (handleRule(rule,false))
  145. {
  146. seenWhitespaceEnd = true;
  147. continue main_loop;
  148. }
  149. } //}}}
  150. //{{{ check if current character is a word separator
  151. if(Character.isWhitespace(ch))
  152. {
  153. if(!seenWhitespaceEnd)
  154. whitespaceEnd = pos + 1;
  155. if(context.inRule != null)
  156. handleRule(context.inRule,true);
  157. handleNoWordBreak();
  158. markKeyword(false);
  159. if(lastOffset != pos)
  160. {
  161. tokenHandler.handleToken(line,
  162. context.rules.getDefault(),
  163. lastOffset - line.offset,
  164. pos - lastOffset,
  165. context);
  166. }
  167. tokenHandler.handleToken(line,
  168. context.rules.getDefault(),
  169. pos - line.offset,1,context);
  170. lastOffset = pos + 1;
  171. }
  172. else
  173. {
  174. if(keywords != null || context.rules.getRuleCount() != 0)
  175. {
  176. String noWordSep = context.rules.getNoWordSep();
  177. if(!Character.isLetterOrDigit(ch)
  178. && noWordSep.indexOf(ch) == -1)
  179. {
  180. if(context.inRule != null)
  181. handleRule(context.inRule,true);
  182. handleNoWordBreak();
  183. markKeyword(true);
  184. tokenHandler.handleToken(line,
  185. context.rules.getDefault(),
  186. lastOffset - line.offset,1,
  187. context);
  188. lastOffset = pos + 1;
  189. }
  190. }
  191. seenWhitespaceEnd = true;
  192. } //}}}
  193. } //}}}
  194. //{{{ Mark all remaining characters
  195. pos = lineLength;
  196. if(context.inRule != null)
  197. handleRule(context.inRule,true);
  198. handleNoWordBreak();
  199. markKeyword(true);
  200. //}}}
  201. //{{{ Unwind any NO_LINE_BREAK parent delegates
  202. unwind: while(context.parent != null)
  203. {
  204. ParserRule rule = context.parent.inRule;
  205. if((rule != null && (rule.action
  206. & ParserRule.NO_LINE_BREAK) == ParserRule.NO_LINE_BREAK)
  207. || terminated)
  208. {
  209. context = context.parent;
  210. keywords = context.rules.getKeywords();
  211. context.setInRule(null);
  212. }
  213. else
  214. break unwind;
  215. } //}}}
  216. tokenHandler.handleToken(line,Token.END,
  217. pos - line.offset,0,context);
  218. context = context.intern();
  219. tokenHandler.setLineContext(context);
  220. /* for GC. */
  221. this.tokenHandler = null;
  222. this.line = null;
  223. return context;
  224. } //}}}
  225. //{{{ Private members
  226. //{{{ Instance variables
  227. private final Map<String, ParserRuleSet> ruleSets = new Hashtable<String, ParserRuleSet>(64);
  228. private ParserRuleSet mainRuleSet;
  229. // Instead of passing these around to each method, we just store them
  230. // as instance variables. Note that this is not thread-safe.
  231. private TokenHandler tokenHandler;
  232. /** The line from which we will mark the tokens. */
  233. private Segment line;
  234. /** The context of the current line. */
  235. private LineContext context;
  236. private KeywordMap keywords;
  237. private final Segment pattern = new Segment();
  238. private int lastOffset;
  239. private int lineLength;
  240. private int pos;
  241. private int whitespaceEnd;
  242. private boolean seenWhitespaceEnd;
  243. //}}}
  244. //{{{ checkDelegateEnd() method
  245. private boolean checkDelegateEnd(ParserRule rule)
  246. {
  247. if(rule.end == null)
  248. return false;
  249. LineContext tempContext = context;
  250. context = context.parent;
  251. keywords = context.rules.getKeywords();
  252. boolean handled = handleRule(rule,true);
  253. context = tempContext;
  254. keywords = context.rules.getKeywords();
  255. if (handled)
  256. {
  257. if(context.inRule != null)
  258. handleRule(context.inRule,true);
  259. markKeyword(true);
  260. context = (LineContext)context.parent.clone();
  261. tokenHandler.handleToken(line,
  262. matchToken(context.inRule, context.inRule, context),
  263. pos - line.offset,pattern.count,context);
  264. keywords = context.rules.getKeywords();
  265. context.setInRule(null);
  266. lastOffset = pos + pattern.count;
  267. // move pos to last character of match sequence
  268. pos += pattern.count - 1;
  269. return true;
  270. }
  271. return false;
  272. } //}}}
  273. //{{{ handleRule() method
  274. /**
  275. * Checks if the rule matches the line at the current position
  276. * and handles the rule if it does match
  277. */
  278. private boolean handleRule(ParserRule checkRule, boolean end)
  279. {
  280. //{{{ Some rules can only match in certain locations
  281. if(!end)
  282. {
  283. if (null == checkRule.upHashChars)
  284. {
  285. if (checkRule.upHashChar != null &&
  286. (pos + checkRule.upHashChar.length() < line.array.length) &&
  287. !checkHashString(checkRule))
  288. {
  289. return false;
  290. }
  291. }
  292. else
  293. {
  294. if (-1 == Arrays.binarySearch(
  295. checkRule.upHashChars,
  296. Character.toUpperCase(line.array[pos])))
  297. {
  298. return false;
  299. }
  300. }
  301. }
  302. int offset = (checkRule.action & ParserRule.MARK_PREVIOUS) != 0 ? lastOffset : pos;
  303. int posMatch = end ? checkRule.endPosMatch : checkRule.startPosMatch;
  304. if((posMatch & ParserRule.AT_LINE_START)
  305. == ParserRule.AT_LINE_START)
  306. {
  307. if(offset != line.offset)
  308. {
  309. return false;
  310. }
  311. }
  312. else if((posMatch & ParserRule.AT_WHITESPACE_END)
  313. == ParserRule.AT_WHITESPACE_END)
  314. {
  315. if(offset != whitespaceEnd)
  316. {
  317. return false;
  318. }
  319. }
  320. else if((posMatch & ParserRule.AT_WORD_START)
  321. == ParserRule.AT_WORD_START)
  322. {
  323. if(offset != lastOffset)
  324. {
  325. return false;
  326. }
  327. } //}}}
  328. int matchedChars = 1;
  329. CharSequence charSeq = null;
  330. Matcher match = null;
  331. //{{{ See if the rule's start or end sequence matches here
  332. if(!end || (checkRule.action & ParserRule.MARK_FOLLOWING) == 0)
  333. {
  334. // the end cannot be a regular expression
  335. if((checkRule.action & ParserRule.REGEXP) == 0 || end)
  336. {
  337. if(end)
  338. {
  339. if(context.spanEndSubst != null)
  340. pattern.array = context.spanEndSubst;
  341. else
  342. pattern.array = checkRule.end;
  343. }
  344. else
  345. pattern.array = checkRule.start;
  346. pattern.offset = 0;
  347. pattern.count = pattern.array.length;
  348. matchedChars = pattern.count;
  349. if(!SyntaxUtilities.regionMatches(context.rules
  350. .getIgnoreCase(),line,pos,pattern.array))
  351. {
  352. return false;
  353. }
  354. }
  355. else
  356. {
  357. // note that all regexps start with \A so they only
  358. // match the start of the string
  359. //int matchStart = pos - line.offset;
  360. charSeq = new SegmentCharSequence(line, pos - line.offset,
  361. line.count - (pos - line.offset));
  362. match = checkRule.startRegexp.matcher(charSeq);
  363. if(!match.lookingAt())
  364. {
  365. return false;
  366. }
  367. else if(match.start() != 0)
  368. {
  369. throw new InternalError("Can't happen");
  370. }
  371. else
  372. {
  373. matchedChars = match.end();
  374. /* workaround for hang if match was
  375. * zero-width. not sure if there is
  376. * a better way to handle this */
  377. if(matchedChars == 0)
  378. matchedChars = 1;
  379. }
  380. }
  381. } //}}}
  382. //{{{ Check for an escape sequence
  383. if((checkRule.action & ParserRule.IS_ESCAPE) == ParserRule.IS_ESCAPE)
  384. {
  385. pos += pattern.count;
  386. } //}}}
  387. //{{{ Handle start of rule
  388. else if(!end)
  389. {
  390. if(context.inRule != null)
  391. handleRule(context.inRule,true);
  392. markKeyword((checkRule.action & ParserRule.MARK_PREVIOUS)
  393. != ParserRule.MARK_PREVIOUS);
  394. switch(checkRule.action & ParserRule.MAJOR_ACTIONS)
  395. {
  396. //{{{ SEQ
  397. case ParserRule.SEQ:
  398. context.spanEndSubst = null;
  399. if((checkRule.action & ParserRule.REGEXP) != 0)
  400. {
  401. handleTokenWithSpaces(tokenHandler,
  402. checkRule.token,
  403. pos - line.offset,
  404. matchedChars,
  405. context);
  406. }
  407. else
  408. {
  409. tokenHandler.handleToken(line,
  410. checkRule.token,
  411. pos - line.offset,
  412. matchedChars,context);
  413. }
  414. // a DELEGATE attribute on a SEQ changes the
  415. // ruleset from the end of the SEQ onwards
  416. if(checkRule.delegate != null)
  417. {
  418. context = new LineContext(
  419. checkRule.delegate,
  420. context.parent);
  421. keywords = context.rules.getKeywords();
  422. }
  423. break;
  424. //}}}
  425. //{{{ SPAN, EOL_SPAN
  426. case ParserRule.SPAN:
  427. case ParserRule.EOL_SPAN:
  428. context.setInRule(checkRule);
  429. byte tokenType = matchToken(checkRule,
  430. context.inRule, context);
  431. if((checkRule.action & ParserRule.REGEXP) != 0)
  432. {
  433. handleTokenWithSpaces(tokenHandler,
  434. tokenType,
  435. pos - line.offset,
  436. matchedChars,
  437. context);
  438. }
  439. else
  440. {
  441. tokenHandler.handleToken(line,tokenType,
  442. pos - line.offset,
  443. matchedChars,context);
  444. }
  445. char[] spanEndSubst = null;
  446. /* substitute result of matching the rule start
  447. * into the end string.
  448. *
  449. * eg, in shell script mode, <<\s*(\w+) is
  450. * matched into \<$1\> to construct rules for
  451. * highlighting read-ins like this <<EOF
  452. * ...
  453. * EOF
  454. */
  455. if(charSeq != null && checkRule.end != null)
  456. {
  457. spanEndSubst = substitute(match,
  458. checkRule.end);
  459. }
  460. context.spanEndSubst = spanEndSubst;
  461. context = new LineContext(
  462. checkRule.delegate,
  463. context);
  464. keywords = context.rules.getKeywords();
  465. break;
  466. //}}}
  467. //{{{ MARK_FOLLOWING
  468. case ParserRule.MARK_FOLLOWING:
  469. tokenHandler.handleToken(line,
  470. matchToken(checkRule, checkRule, context),
  471. pos - line.offset,
  472. pattern.count,context);
  473. context.spanEndSubst = null;
  474. context.setInRule(checkRule);
  475. break;
  476. //}}}
  477. //{{{ MARK_PREVIOUS
  478. case ParserRule.MARK_PREVIOUS:
  479. context.spanEndSubst = null;
  480. if(pos != lastOffset)
  481. {
  482. tokenHandler.handleToken(line,
  483. checkRule.token,
  484. lastOffset - line.offset,
  485. pos - lastOffset,
  486. context);
  487. }
  488. tokenHandler.handleToken(line,
  489. matchToken(checkRule, checkRule, context),
  490. pos - line.offset,pattern.count,
  491. context);
  492. break;
  493. //}}}
  494. default:
  495. throw new InternalError("Unhandled major action");
  496. }
  497. // move pos to last character of match sequence
  498. pos += matchedChars - 1;
  499. lastOffset = pos + 1;
  500. // break out of inner for loop to check next char
  501. } //}}}
  502. //{{{ Handle end of MARK_FOLLOWING
  503. else if((context.inRule.action & ParserRule.MARK_FOLLOWING) != 0)
  504. {
  505. if(pos != lastOffset)
  506. {
  507. tokenHandler.handleToken(line,
  508. context.inRule.token,
  509. lastOffset - line.offset,
  510. pos - lastOffset,context);
  511. }
  512. lastOffset = pos;
  513. context.setInRule(null);
  514. } //}}}
  515. return true;
  516. } //}}}
  517. //{{{ handleNoWordBreak() method
  518. private void handleNoWordBreak()
  519. {
  520. if(context.parent != null)
  521. {
  522. ParserRule rule = context.parent.inRule;
  523. if(rule != null && (context.parent.inRule.action
  524. & ParserRule.NO_WORD_BREAK) != 0)
  525. {
  526. if(pos != lastOffset)
  527. {
  528. tokenHandler.handleToken(line,
  529. rule.token,
  530. lastOffset - line.offset,
  531. pos - lastOffset,context);
  532. }
  533. lastOffset = pos;
  534. context = context.parent;
  535. keywords = context.rules.getKeywords();
  536. context.setInRule(null);
  537. }
  538. }
  539. } //}}}
  540. //{{{ handleTokenWithSpaces() method
  541. private void handleTokenWithSpaces(TokenHandler tokenHandler,
  542. byte tokenType, int start, int len, LineContext context)
  543. {
  544. int last = start;
  545. int end = start + len;
  546. for(int i = start; i < end; i++)
  547. {
  548. if(Character.isWhitespace(line.array[i + line.offset]))
  549. {
  550. if(last != i)
  551. {
  552. tokenHandler.handleToken(line,
  553. tokenType,last,i - last,context);
  554. }
  555. tokenHandler.handleToken(line,tokenType,i,1,context);
  556. last = i + 1;
  557. }
  558. }
  559. if(last != end)
  560. {
  561. tokenHandler.handleToken(line,tokenType,last,
  562. end - last,context);
  563. }
  564. } //}}}
  565. //{{{ markKeyword() method
  566. private void markKeyword(boolean addRemaining)
  567. {
  568. int len = pos - lastOffset;
  569. if(len == 0)
  570. return;
  571. //{{{ Do digits
  572. if(context.rules.getHighlightDigits())
  573. {
  574. boolean digit = false;
  575. boolean mixed = false;
  576. for(int i = lastOffset; i < pos; i++)
  577. {
  578. char ch = line.array[i];
  579. if(Character.isDigit(ch))
  580. digit = true;
  581. else
  582. mixed = true;
  583. }
  584. if(mixed)
  585. {
  586. Pattern digitRE = context.rules.getDigitRegexp();
  587. // only match against regexp if its not all
  588. // digits; if all digits, no point matching
  589. if(digit)
  590. {
  591. if(digitRE == null)
  592. {
  593. // mixed digit/alpha keyword,
  594. // and no regexp... don't
  595. // highlight as DIGIT
  596. digit = false;
  597. }
  598. else
  599. {
  600. int oldCount = line.count;
  601. int oldOffset = line.offset;
  602. line.offset = lastOffset;
  603. line.count = len;
  604. CharSequence seq = new SegmentCharSequence(line);
  605. digit = digitRE.matcher(seq).matches();
  606. line.offset = oldOffset;
  607. line.count = oldCount;
  608. }
  609. }
  610. }
  611. if(digit)
  612. {
  613. tokenHandler.handleToken(line,Token.DIGIT,
  614. lastOffset - line.offset,
  615. len,context);
  616. lastOffset = pos;
  617. return;
  618. }
  619. } //}}}
  620. //{{{ Do keywords
  621. if(keywords != null)
  622. {
  623. byte id = keywords.lookup(line, lastOffset, len);
  624. if(id != Token.NULL)
  625. {
  626. tokenHandler.handleToken(line,id,
  627. lastOffset - line.offset,
  628. len,context);
  629. lastOffset = pos;
  630. return;
  631. }
  632. } //}}}
  633. //{{{ Handle any remaining crud
  634. if(addRemaining)
  635. {
  636. tokenHandler.handleToken(line,context.rules.getDefault(),
  637. lastOffset - line.offset,len,context);
  638. lastOffset = pos;
  639. } //}}}
  640. } //}}}
  641. //{{{ substitute() method
  642. private static char[] substitute(Matcher match, char[] end)
  643. {
  644. StringBuilder buf = new StringBuilder();
  645. for(int i = 0; i < end.length; i++)
  646. {
  647. char ch = end[i];
  648. if(ch == '$' || ch == '~')
  649. {
  650. if(i == end.length - 1)
  651. buf.append(ch);
  652. else
  653. {
  654. char digit = end[i + 1];
  655. if(!Character.isDigit(digit))
  656. buf.append(ch);
  657. else if (ch == '$')
  658. {
  659. buf.append(match.group(
  660. digit - '0'));
  661. i++;
  662. }
  663. else
  664. {
  665. String s = match.group(digit - '0');
  666. if (s.length() == 1)
  667. {
  668. char b = TextUtilities.getComplementaryBracket(s.charAt(0), null);
  669. if (b == '\0')
  670. b = s.charAt(0);
  671. buf.append(b);
  672. }
  673. else
  674. buf.append(ch);
  675. i++;
  676. }
  677. }
  678. }
  679. else
  680. buf.append(ch);
  681. }
  682. char[] returnValue = new char[buf.length()];
  683. buf.getChars(0,buf.length(),returnValue,0);
  684. return returnValue;
  685. } //}}}
  686. //{{{ matchToken() method
  687. private byte matchToken(ParserRule rule, ParserRule base, LineContext ctx)
  688. {
  689. switch (rule.matchType)
  690. {
  691. case ParserRule.MATCH_TYPE_RULE:
  692. return base.token;
  693. case ParserRule.MATCH_TYPE_CONTEXT:
  694. return context.rules.getDefault();
  695. default:
  696. return rule.matchType;
  697. }
  698. } //}}}
  699. //{{{ checkHashString() method
  700. private boolean checkHashString(ParserRule rule)
  701. {
  702. for (int i = 0; i < rule.upHashChar.length(); i++)
  703. {
  704. if (Character.toUpperCase(line.array[pos+i]) != rule.upHashChar.charAt(i))
  705. {
  706. return false;
  707. }
  708. }
  709. return true;
  710. } //}}}
  711. //}}}
  712. //{{{ LineContext class
  713. /**
  714. * Stores persistent per-line syntax parser state.
  715. */
  716. public static class LineContext
  717. {
  718. private static final Map<LineContext, LineContext> intern = new HashMap<LineContext, LineContext>();
  719. public LineContext parent;
  720. public ParserRule inRule;
  721. public ParserRuleSet rules;
  722. // used for SPAN_REGEXP rules; otherwise null
  723. public char[] spanEndSubst;
  724. public ParserRule escapeRule;
  725. //{{{ LineContext constructor
  726. public LineContext(ParserRuleSet rs, LineContext lc)
  727. {
  728. rules = rs;
  729. parent = (lc == null ? null : (LineContext)lc.clone());
  730. /*
  731. * SPANs with no delegate need to propagate the
  732. * escape rule to the child context, so this is
  733. * needed.
  734. */
  735. if (rs.getModeName() != null)
  736. escapeRule = rules.getEscapeRule();
  737. else
  738. escapeRule = lc.escapeRule;
  739. } //}}}
  740. //{{{ LineContext constructor
  741. public LineContext()
  742. {
  743. } //}}}
  744. //{{{ intern() method
  745. public LineContext intern()
  746. {
  747. LineContext obj = intern.get(this);
  748. if(obj == null)
  749. {
  750. intern.put(this,this);
  751. return this;
  752. }
  753. else
  754. return obj;
  755. } //}}}
  756. //{{{ hashCode() method
  757. public int hashCode()
  758. {
  759. if(inRule != null)
  760. return inRule.hashCode();
  761. else if(rules != null)
  762. return rules.hashCode();
  763. else
  764. return 0;
  765. } //}}}
  766. //{{{ equals() method
  767. public boolean equals(Object obj)
  768. {
  769. if(obj instanceof LineContext)
  770. {
  771. LineContext lc = (LineContext)obj;
  772. return lc.inRule == inRule && lc.rules == rules
  773. && StandardUtilities.objectsEqual(parent,lc.parent)
  774. && charArraysEqual(spanEndSubst,lc.spanEndSubst);
  775. }
  776. else
  777. return false;
  778. } //}}}
  779. //{{{ clone() method
  780. public Object clone()
  781. {
  782. LineContext lc = new LineContext();
  783. lc.inRule = inRule;
  784. lc.rules = rules;
  785. lc.parent = (parent == null) ? null : (LineContext) parent.clone();
  786. lc.spanEndSubst = spanEndSubst;
  787. lc.escapeRule = escapeRule;
  788. return lc;
  789. } //}}}
  790. //{{{ charArraysEqual() method
  791. private static boolean charArraysEqual(char[] c1, char[] c2)
  792. {
  793. if(c1 == null)
  794. return c2 == null;
  795. // c1 is not null
  796. if(c2 == null)
  797. return false;
  798. if(c1.length != c2.length)
  799. return false;
  800. for(int i = 0; i < c1.length; i++)
  801. {
  802. if(c1[i] != c2[i])
  803. return false;
  804. }
  805. return true;
  806. } //}}}
  807. //{{{ setInRule() method
  808. /**
  809. * Sets the current rule being processed and adjusts the
  810. * escape rule for the context based on the rule.
  811. */
  812. public void setInRule(ParserRule rule)
  813. {
  814. inRule = rule;
  815. if (rule != null && rule.escapeRule != null)
  816. escapeRule = rule.escapeRule;
  817. else if (rules != null && rules.getModeName() != null)
  818. escapeRule = rules.getEscapeRule();
  819. else if (parent != null)
  820. escapeRule = parent.escapeRule;
  821. else
  822. escapeRule = null;
  823. } //}}}
  824. } //}}}
  825. }