/jEdit/branches/4.4.x-merge-request-for-r19201/org/gjt/sp/jedit/Mode.java

# · Java · 463 lines · 272 code · 47 blank · 144 comment · 57 complexity · d4393c4bd22fdf1b8a08f7cf1ffb9394 MD5 · raw file

  1. /*
  2. * Mode.java - jEdit editing mode
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1998, 1999, 2000 Slava Pestov
  7. * Copyright (C) 1999 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;
  24. //{{{ Imports
  25. import java.lang.reflect.Method;
  26. import java.util.Hashtable;
  27. import java.util.Collections;
  28. import java.util.LinkedList;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.regex.Pattern;
  32. import java.util.regex.PatternSyntaxException;
  33. import org.gjt.sp.jedit.indent.DeepIndentRule;
  34. import org.gjt.sp.jedit.indent.IndentRule;
  35. import org.gjt.sp.jedit.indent.IndentRuleFactory;
  36. import org.gjt.sp.jedit.indent.WhitespaceRule;
  37. import org.gjt.sp.jedit.syntax.TokenMarker;
  38. import org.gjt.sp.jedit.syntax.ModeProvider;
  39. import org.gjt.sp.util.Log;
  40. import org.gjt.sp.util.StandardUtilities;
  41. //}}}
  42. /**
  43. * An edit mode defines specific settings for editing some type of file.
  44. * One instance of this class is created for each supported edit mode.
  45. *
  46. * @author Slava Pestov
  47. * @version $Id: Mode.java 18844 2010-10-25 19:15:40Z daleanson $
  48. */
  49. public class Mode
  50. {
  51. //{{{ Mode constructor
  52. /**
  53. * Creates a new edit mode.
  54. *
  55. * @param name The name used in mode listings and to query mode
  56. * properties
  57. * @see #getProperty(String)
  58. */
  59. public Mode(String name)
  60. {
  61. this.name = name;
  62. this.ignoreWhitespace = true;
  63. props = new Hashtable<String, Object>();
  64. } //}}}
  65. //{{{ init() method
  66. /**
  67. * Initializes the edit mode. Should be called after all properties
  68. * are loaded and set.
  69. */
  70. public void init()
  71. {
  72. try
  73. {
  74. filenameRE = null;
  75. String filenameGlob = (String)getProperty("filenameGlob");
  76. if(filenameGlob != null && filenameGlob.length() != 0)
  77. {
  78. filenameRE = Pattern.compile(StandardUtilities.globToRE(filenameGlob),
  79. Pattern.CASE_INSENSITIVE);
  80. }
  81. firstlineRE = null;
  82. String firstlineGlob = (String)getProperty("firstlineGlob");
  83. if(firstlineGlob != null && firstlineGlob.length() != 0)
  84. {
  85. firstlineRE = Pattern.compile(StandardUtilities.globToRE(firstlineGlob),
  86. Pattern.CASE_INSENSITIVE);
  87. }
  88. }
  89. catch(PatternSyntaxException re)
  90. {
  91. Log.log(Log.ERROR,this,"Invalid filename/firstline"
  92. + " globs in mode " + name);
  93. Log.log(Log.ERROR,this,re);
  94. }
  95. // Fix for this bug:
  96. // -- Put a mode into the user dir with the same name as one
  97. // on the system dir.
  98. // -- Reload edit modes.
  99. // -- Old mode from system dir still used for highlighting
  100. // until jEdit restart.
  101. marker = null;
  102. } //}}}
  103. //{{{ getTokenMarker() method
  104. /**
  105. * Returns the token marker for this mode.
  106. */
  107. public TokenMarker getTokenMarker()
  108. {
  109. loadIfNecessary();
  110. return marker;
  111. } //}}}
  112. //{{{ setTokenMarker() method
  113. /**
  114. * Sets the token marker for this mode.
  115. * @param marker The new token marker
  116. */
  117. public void setTokenMarker(TokenMarker marker)
  118. {
  119. this.marker = marker;
  120. } //}}}
  121. //{{{ loadIfNecessary() method
  122. /**
  123. * Loads the mode from disk if it hasn't been loaded already.
  124. * @since jEdit 2.5pre3
  125. */
  126. public void loadIfNecessary()
  127. {
  128. if(marker == null)
  129. {
  130. ModeProvider.instance.loadMode(this);
  131. if (marker == null)
  132. Log.log(Log.ERROR, this, "Mode not correctly loaded, token marker is still null");
  133. }
  134. } //}}}
  135. //{{{ getProperty() method
  136. /**
  137. * Returns a mode property.
  138. * @param key The property name
  139. *
  140. * @since jEdit 2.2pre1
  141. */
  142. public Object getProperty(String key)
  143. {
  144. Object value = props.get(key);
  145. if(value != null)
  146. return value;
  147. return null;
  148. } //}}}
  149. //{{{ getBooleanProperty() method
  150. /**
  151. * Returns the value of a boolean property.
  152. * @param key The property name
  153. *
  154. * @since jEdit 2.5pre3
  155. */
  156. public boolean getBooleanProperty(String key)
  157. {
  158. Object value = getProperty(key);
  159. return StandardUtilities.getBoolean(value, false);
  160. } //}}}
  161. //{{{ setProperty() method
  162. /**
  163. * Sets a mode property.
  164. * @param key The property name
  165. * @param value The property value
  166. */
  167. public void setProperty(String key, Object value)
  168. {
  169. props.put(key,value);
  170. } //}}}
  171. //{{{ unsetProperty() method
  172. /**
  173. * Unsets a mode property.
  174. * @param key The property name
  175. * @since jEdit 3.2pre3
  176. */
  177. public void unsetProperty(String key)
  178. {
  179. props.remove(key);
  180. } //}}}
  181. //{{{ setProperties() method
  182. /**
  183. * Should only be called by <code>XModeHandler</code>.
  184. * @since jEdit 4.0pre3
  185. */
  186. public void setProperties(Map props)
  187. {
  188. if(props == null)
  189. props = new Hashtable<String, Object>();
  190. ignoreWhitespace = !"false".equalsIgnoreCase(
  191. (String)props.get("ignoreWhitespace"));
  192. // need to carry over file name and first line globs because they are
  193. // not given to us by the XMode handler, but instead are filled in by
  194. // the catalog loader.
  195. String filenameGlob = (String)this.props.get("filenameGlob");
  196. String firstlineGlob = (String)this.props.get("firstlineGlob");
  197. String filename = (String)this.props.get("file");
  198. this.props = props;
  199. if(filenameGlob != null)
  200. props.put("filenameGlob",filenameGlob);
  201. if(firstlineGlob != null)
  202. props.put("firstlineGlob",firstlineGlob);
  203. if(filename != null)
  204. props.put("file",filename);
  205. } //}}}
  206. //{{{ accept() method
  207. /**
  208. * Returns true if the edit mode is suitable for editing the specified
  209. * file. The buffer name and first line is checked against the
  210. * file name and first line globs, respectively.
  211. * @param fileName The buffer's name
  212. * @param firstLine The first line of the buffer
  213. *
  214. * @since jEdit 3.2pre3
  215. */
  216. public boolean accept(String fileName, String firstLine)
  217. {
  218. return acceptFilename(fileName) || acceptFirstLine(firstLine);
  219. } //}}}
  220. //{{{ acceptFilename() method
  221. /**
  222. * Returns true if the buffer name matches the file name glob.
  223. * @param fileName The buffer's name
  224. * @return true if the file name matches the file name glob.
  225. * @since jEdit 4.3pre18
  226. */
  227. public boolean acceptFilename(String fileName)
  228. {
  229. return filenameRE != null && filenameRE.matcher(fileName).matches();
  230. } //}}}
  231. //{{{ acceptFilenameIdentical() method
  232. /**
  233. * Returns true if the buffer name is identical to the file name glob.
  234. * This works only for regular expressions that only represent themselves,
  235. * i.e. without any meta-characters.
  236. * @param fileName The buffer's name
  237. * @return true if the file name matches the file name glob.
  238. * @since jEdit 4.4pre1
  239. */
  240. public boolean acceptFilenameIdentical(String fileName)
  241. {
  242. if (fileName == null)
  243. {
  244. return false;
  245. }
  246. return (fileName.equals((String)getProperty("filenameGlob")) &&
  247. (filenameRE == null || filenameRE.matcher(fileName).matches()));
  248. } //}}}
  249. //{{{ acceptFirstLine() method
  250. /**
  251. * Returns true if the first line matches the first line glob.
  252. * @param firstLine The first line of the buffer
  253. * @return true if the first line matches the first line glob.
  254. * @since jEdit 4.3pre18
  255. */
  256. public boolean acceptFirstLine(String firstLine)
  257. {
  258. return firstlineRE != null && firstlineRE.matcher(firstLine).matches();
  259. } //}}}
  260. //{{{ getName() method
  261. /**
  262. * Returns the internal name of this edit mode.
  263. */
  264. public String getName()
  265. {
  266. return name;
  267. } //}}}
  268. //{{{ toString() method
  269. /**
  270. * Returns a string representation of this edit mode.
  271. */
  272. public String toString()
  273. {
  274. return name;
  275. } //}}}
  276. //{{{ getIgnoreWhitespace() method
  277. public boolean getIgnoreWhitespace()
  278. {
  279. return ignoreWhitespace;
  280. } //}}}
  281. //{{{ Indent rules
  282. public synchronized List<IndentRule> getIndentRules()
  283. {
  284. if (indentRules == null)
  285. {
  286. initIndentRules();
  287. }
  288. return indentRules;
  289. }
  290. public synchronized boolean isElectricKey(char ch)
  291. {
  292. if (electricKeys == null)
  293. {
  294. String[] props = {
  295. "indentOpenBrackets",
  296. "indentCloseBrackets",
  297. "electricKeys"
  298. };
  299. StringBuilder buf = new StringBuilder();
  300. for(int i = 0; i < props.length; i++)
  301. {
  302. String prop = (String) getProperty(props[i]);
  303. if (prop != null)
  304. buf.append(prop);
  305. }
  306. electricKeys = buf.toString();
  307. }
  308. return (electricKeys.indexOf(ch) >= 0);
  309. }
  310. private void initIndentRules()
  311. {
  312. List<IndentRule> rules = new LinkedList<IndentRule>();
  313. String[] regexpProps = {
  314. "indentNextLine",
  315. "indentNextLines"
  316. };
  317. for(int i = 0; i < regexpProps.length; i++)
  318. {
  319. IndentRule rule = createRegexpIndentRule(regexpProps[i]);
  320. if(rule != null)
  321. rules.add(rule);
  322. }
  323. String[] bracketProps = {
  324. "indentOpenBracket",
  325. "indentCloseBracket",
  326. "unalignedOpenBracket",
  327. "unalignedCloseBracket",
  328. };
  329. for(int i = 0; i < bracketProps.length; i++)
  330. {
  331. createBracketIndentRules(bracketProps[i], rules);
  332. }
  333. String[] finalProps = {
  334. "unindentThisLine",
  335. "unindentNextLines"
  336. };
  337. for(int i = 0; i < finalProps.length; i++)
  338. {
  339. IndentRule rule = createRegexpIndentRule(finalProps[i]);
  340. if(rule != null)
  341. rules.add(rule);
  342. }
  343. if (getBooleanProperty("deepIndent"))
  344. {
  345. String unalignedOpenBrackets = (String) getProperty("unalignedOpenBrackets");
  346. if (unalignedOpenBrackets != null)
  347. {
  348. for (int i = 0 ; i < unalignedOpenBrackets.length();i++)
  349. {
  350. char openChar = unalignedOpenBrackets.charAt(i);
  351. char closeChar = TextUtilities.getComplementaryBracket(openChar, null);
  352. if (closeChar != '\0')
  353. rules.add(new DeepIndentRule(openChar, closeChar));
  354. }
  355. }
  356. }
  357. if (!getIgnoreWhitespace())
  358. rules.add(new WhitespaceRule());
  359. indentRules = Collections.unmodifiableList(rules);
  360. }
  361. private IndentRule createRegexpIndentRule(String prop)
  362. {
  363. String value = (String) getProperty(prop);
  364. try
  365. {
  366. if(value != null)
  367. {
  368. Method m = IndentRuleFactory.class.getMethod(
  369. prop,new Class[] { String.class });
  370. return (IndentRule)m.invoke(null, value);
  371. }
  372. }
  373. catch(Exception e)
  374. {
  375. Log.log(Log.ERROR,this,"Bad indent rule " + prop
  376. + '=' + value + ':');
  377. Log.log(Log.ERROR,this,e);
  378. }
  379. return null;
  380. }
  381. private void createBracketIndentRules(String prop,
  382. List<IndentRule> rules)
  383. {
  384. String value = (String) getProperty(prop + 's');
  385. try
  386. {
  387. if(value != null)
  388. {
  389. for(int i = 0; i < value.length(); i++)
  390. {
  391. char ch = value.charAt(i);
  392. Method m = IndentRuleFactory.class.getMethod(
  393. prop,new Class[] { char.class });
  394. rules.add((IndentRule) m.invoke(null, ch));
  395. }
  396. }
  397. }
  398. catch(Exception e)
  399. {
  400. Log.log(Log.ERROR,this,"Bad indent rule " + prop
  401. + '=' + value + ':');
  402. Log.log(Log.ERROR,this,e);
  403. }
  404. }
  405. //}}}
  406. //{{{ Private members
  407. protected String name;
  408. protected Map<String, Object> props;
  409. private Pattern firstlineRE;
  410. private Pattern filenameRE;
  411. protected TokenMarker marker;
  412. private List<IndentRule> indentRules;
  413. private String electricKeys;
  414. private boolean ignoreWhitespace;
  415. //}}}
  416. }