PageRenderTime 81ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/JEditActionSet.java

#
Java | 469 lines | 179 code | 32 blank | 258 comment | 30 complexity | b1cea2856ce087973f36cf92b69206b0 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. * JEditActionSet.java - A set of actions
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001, 2003 Slava Pestov
  7. * Portions copyright (C) 2007 Matthieu Casanova
  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. import java.io.*;
  25. import java.net.URL;
  26. import java.util.*;
  27. import org.gjt.sp.jedit.input.AbstractInputHandler;
  28. import org.gjt.sp.jedit.input.InputHandlerProvider;
  29. import org.gjt.sp.util.Log;
  30. import org.gjt.sp.util.XMLUtilities;
  31. /**
  32. * A set of actions, either loaded from an XML file, or constructed at runtime
  33. * by a plugin. <p>
  34. *
  35. * <h3>Action sets loaded from XML files</h3>
  36. *
  37. * Action sets are read from these files inside the plugin JAR:
  38. * <ul>
  39. * <li><code>actions.xml</code> - actions made available for use in jEdit views,
  40. * including the view's <b>Plugins</b> menu, the tool bar, etc.</li>
  41. * <li><code>browser.actions.xml</code> - actions for the file system browser's
  42. * <b>Plugins</b> menu.</li>
  43. * </ul>
  44. *
  45. * An action definition file has the following form:
  46. *
  47. * <pre>&lt;?xml version="1.0"?&gt;
  48. *&lt;!DOCTYPE ACTIONS SYSTEM "actions.dtd"&gt;
  49. *&lt;ACTIONS&gt;
  50. * &lt;ACTION NAME="some-action"&gt;
  51. * &lt;CODE&gt;
  52. * // BeanShell code evaluated when the action is invoked
  53. * &lt;/CODE&gt;
  54. * &lt;/ACTION&gt;
  55. * &lt;ACTION NAME="some-toggle-action"&gt;
  56. * &lt;CODE&gt;
  57. * // BeanShell code evaluated when the action is invoked
  58. * &lt;/CODE&gt;
  59. * &lt;IS_SELECTED&gt;
  60. * // BeanShell code that should evaluate to true or false
  61. * &lt;/IS_SELECTED&gt;
  62. * &lt;/ACTION&gt;
  63. *&lt;/ACTIONS&gt;</pre>
  64. *
  65. * The following elements are valid:
  66. *
  67. * <ul>
  68. * <li>
  69. * <code>ACTIONS</code> is the top-level element and refers
  70. * to the set of actions used by the plugin.
  71. * </li>
  72. * <li>
  73. * An <code>ACTION</code> contains the data for a particular action.
  74. * It has three attributes: a required <code>NAME</code>;
  75. * an optional <code>NO_REPEAT</code>, which is a flag
  76. * indicating whether the action should not be repeated with the
  77. * <b>C+ENTER</b> command; and an optional
  78. * <code>NO_RECORD</code> which is a a flag indicating whether the
  79. * action should be recorded if it is invoked while the user is recording a
  80. * macro. The two flag attributes
  81. * can have two possible values, "TRUE" or
  82. * "FALSE". In both cases, "FALSE" is the
  83. * default if the attribute is not specified.
  84. * </li>
  85. * <li>
  86. * An <code>ACTION</code> can have two child elements
  87. * within it: a required <code>CODE</code> element which
  88. * specifies the
  89. * BeanShell code that will be executed when the action is invoked,
  90. * and an optional <code>IS_SELECTED</code> element, used for
  91. * checkbox
  92. * menu items. The <code>IS_SELECTED</code> element contains
  93. * BeanShell code that returns a boolean flag that will
  94. * determine the state of the checkbox.
  95. * </li>
  96. * </ul>
  97. *
  98. * Each action must have a property <code><i>name</i>.label</code> containing
  99. * the action's menu item label.
  100. *
  101. * <h3>View actions</h3>
  102. *
  103. * Actions defined in <code>actions.xml</code> can be added to the view's
  104. * <b>Plugins</b> menu; see {@link EditPlugin}.
  105. * The action code may use any standard predefined
  106. * BeanShell variable; see {@link BeanShell}.
  107. *
  108. * <h3>File system browser actions</h3>
  109. *
  110. * Actions defined in <code>actions.xml</code> can be added to the file
  111. * system browser's <b>Plugins</b> menu; see {@link EditPlugin}.
  112. * The action code may use any standard predefined
  113. * BeanShell variable, in addition to a variable <code>browser</code> which
  114. * contains a reference to the current
  115. * {@link org.gjt.sp.jedit.browser.VFSBrowser} instance.<p>
  116. *
  117. * File system browser actions should not define
  118. * <code>&lt;IS_SELECTED&gt;</code> blocks.
  119. *
  120. * <h3>Custom action sets</h3>
  121. *
  122. * Call {@link jEdit#addActionSet(ActionSet)} to add a custom action set to
  123. * jEdit's action context. You must also call {@link #initKeyBindings()} for new
  124. * action sets. Don't forget to call {@link jEdit#removeActionSet(ActionSet)}
  125. * before your plugin is unloaded, too.
  126. *
  127. * @see jEdit#getActionContext()
  128. * @see org.gjt.sp.jedit.browser.VFSBrowser#getActionContext()
  129. * @see ActionContext#getActionNames()
  130. * @see ActionContext#getAction(String)
  131. * @see jEdit#addActionSet(ActionSet)
  132. * @see jEdit#removeActionSet(ActionSet)
  133. * @see PluginJAR#getActionSet()
  134. * @see BeanShell
  135. * @see View
  136. *
  137. * @author Slava Pestov
  138. * @author John Gellene (API documentation)
  139. * @version $Id: ActionSet.java 9529 2007-05-12 15:06:52Z ezust $
  140. * @since jEdit 4.3pre13
  141. */
  142. public abstract class JEditActionSet<E extends JEditAbstractEditAction> implements InputHandlerProvider
  143. {
  144. //{{{ JEditActionSet constructor
  145. /**
  146. * Creates a new action set.
  147. * @since jEdit 4.3pre13
  148. */
  149. protected JEditActionSet()
  150. {
  151. actions = new Hashtable<String, Object>();
  152. loaded = true;
  153. } //}}}
  154. //{{{ JEditActionSet constructor
  155. /**
  156. * Creates a new action set.
  157. * @param cachedActionNames The list of cached action names
  158. * @param uri The actions.xml URI
  159. * @since jEdit 4.3pre13
  160. */
  161. protected JEditActionSet(String[] cachedActionNames, URL uri)
  162. {
  163. this();
  164. this.uri = uri;
  165. if(cachedActionNames != null)
  166. {
  167. for(int i = 0; i < cachedActionNames.length; i++)
  168. {
  169. actions.put(cachedActionNames[i],placeholder);
  170. }
  171. }
  172. loaded = false;
  173. } //}}}
  174. //{{{ addAction() method
  175. /**
  176. * Adds an action to the action set.
  177. * @param action The action
  178. * @since jEdit 4.0pre1
  179. */
  180. public void addAction(E action)
  181. {
  182. actions.put(action.getName(),action);
  183. if(context != null)
  184. {
  185. context.actionNames = null;
  186. context.actionHash.put(action.getName(),this);
  187. }
  188. } //}}}
  189. //{{{ removeAction() method
  190. /**
  191. * Removes an action from the action set.
  192. * @param name The action name
  193. * @since jEdit 4.0pre1
  194. */
  195. public void removeAction(String name)
  196. {
  197. actions.remove(name);
  198. if(context != null)
  199. {
  200. context.actionNames = null;
  201. context.actionHash.remove(name);
  202. }
  203. } //}}}
  204. //{{{ removeAllActions() method
  205. /**
  206. * Removes all actions from the action set.
  207. * @since jEdit 4.0pre1
  208. */
  209. public void removeAllActions()
  210. {
  211. if(context != null)
  212. {
  213. context.actionNames = null;
  214. String[] actions = getActionNames();
  215. for(int i = 0; i < actions.length; i++)
  216. {
  217. context.actionHash.remove(actions[i]);
  218. }
  219. }
  220. actions.clear();
  221. } //}}}
  222. //{{{ getAction() method
  223. /**
  224. * Returns an action with the specified name.<p>
  225. *
  226. * <b>Deferred loading:</b> this will load the action set if necessary.
  227. *
  228. * @param name The action name
  229. * @since jEdit 4.0pre1
  230. */
  231. public E getAction(String name)
  232. {
  233. Object obj = actions.get(name);
  234. if(obj == placeholder)
  235. {
  236. load();
  237. obj = actions.get(name);
  238. if(obj == placeholder)
  239. {
  240. Log.log(Log.WARNING,this,"Outdated cache");
  241. obj = null;
  242. }
  243. }
  244. return (E) obj;
  245. } //}}}
  246. //{{{ getActionCount() method
  247. /**
  248. * Returns the number of actions in the set.
  249. * @since jEdit 4.0pre1
  250. */
  251. public int getActionCount()
  252. {
  253. return actions.size();
  254. } //}}}
  255. //{{{ getActionNames() method
  256. /**
  257. * Returns an array of all action names in this action set.
  258. * @since jEdit 4.2pre1
  259. */
  260. public String[] getActionNames()
  261. {
  262. String[] retVal = new String[actions.size()];
  263. Set<String> keys = actions.keySet();
  264. int i = 0;
  265. for (String key : keys)
  266. {
  267. retVal[i++] = key;
  268. }
  269. return retVal;
  270. } //}}}
  271. //{{{ getCacheableActionNames() method
  272. /**
  273. * Returns an array of all action names in this action set that should
  274. * be cached; namely, <code>BeanShellAction</code>s.
  275. * @since jEdit 4.2pre1
  276. */
  277. public String[] getCacheableActionNames()
  278. {
  279. LinkedList<String> retVal = new LinkedList<String>();
  280. for (Object obj : actions.values())
  281. {
  282. if (obj == placeholder)
  283. {
  284. // ??? this should only be called with
  285. // fully loaded action set
  286. Log.log(Log.WARNING, this, "Action set not up "
  287. + "to date");
  288. }
  289. else if (obj instanceof JEditBeanShellAction)
  290. retVal.add(((JEditBeanShellAction) obj).getName());
  291. }
  292. return retVal.toArray(new String[retVal.size()]);
  293. } //}}}
  294. //{{{ getArray() method
  295. /**
  296. * Returns an empty array E[].
  297. * I know it is bad, if you find a method to instantiate a generic Array,
  298. * tell me
  299. * @param size the size of the array
  300. * @return the empty array
  301. */
  302. protected abstract E[] getArray(int size);
  303. //}}}
  304. //{{{ getActions() method
  305. /**
  306. * Returns an array of all actions in this action set.<p>
  307. *
  308. * <b>Deferred loading:</b> this will load the action set if necessary.
  309. *
  310. * @since jEdit 4.0pre1
  311. */
  312. public E[] getActions()
  313. {
  314. load();
  315. E[] retVal = getArray(actions.size());
  316. Collection<Object> values = actions.values();
  317. int i = 0;
  318. for (Object value : values)
  319. {
  320. retVal[i++] = (E) value;
  321. }
  322. return retVal;
  323. } //}}}
  324. //{{{ contains() method
  325. /**
  326. * Returns if this action set contains the specified action.
  327. * @param action The action
  328. * @since jEdit 4.2pre1
  329. */
  330. public boolean contains(String action)
  331. {
  332. boolean retval = actions.containsKey(action);
  333. return retval;
  334. // return actions.containsKey(action);
  335. } //}}}
  336. //{{{ size() method
  337. /**
  338. * Returns the number of actions in this action set.
  339. * @since jEdit 4.2pre2
  340. */
  341. public int size()
  342. {
  343. return actions.size();
  344. } //}}}
  345. //{{{ load() method
  346. /**
  347. * Forces the action set to be loaded. Plugins and macros should not
  348. * call this method.
  349. * @since jEdit 4.2pre1
  350. */
  351. public void load()
  352. {
  353. if(loaded)
  354. return;
  355. loaded = true;
  356. //actions.clear();
  357. if (uri == null)
  358. return;
  359. try
  360. {
  361. Log.log(Log.DEBUG,this,"Loading actions from " + uri);
  362. ActionListHandler ah = new ActionListHandler(uri.toString(),this);
  363. if ( XMLUtilities.parseXML(uri.openStream(), ah))
  364. {
  365. Log.log(Log.ERROR, this, "Unable to parse: " + uri);
  366. }
  367. }
  368. catch(IOException e)
  369. {
  370. Log.log(Log.ERROR,this,uri,e);
  371. }
  372. } //}}}
  373. //{{{ createBeanShellAction() method
  374. /**
  375. * This method should be implemented to return an action that will execute
  376. * the given code
  377. * @since 4.3pre13
  378. */
  379. protected abstract JEditAbstractEditAction createBeanShellAction(String actionName,
  380. String code,
  381. String selected,
  382. boolean noRepeat,
  383. boolean noRecord,
  384. boolean noRememberLast);
  385. //}}}
  386. //{{{ initKeyBindings() method
  387. /**
  388. * Initializes the action set's key bindings.
  389. * jEdit calls this method for all registered action sets when the
  390. * user changes key bindings in the <b>Global Options</b> dialog box.<p>
  391. *
  392. * Note if your plugin adds a custom action set to jEdit's collection,
  393. * it must also call this method on the action set after adding it.
  394. *
  395. * @since jEdit 4.2pre1
  396. */
  397. public void initKeyBindings()
  398. {
  399. AbstractInputHandler inputHandler = getInputHandler();
  400. Set<Map.Entry<String, Object>> entries = actions.entrySet();
  401. for (Map.Entry<String, Object> entry : entries)
  402. {
  403. String name = entry.getKey();
  404. String shortcut1 = getProperty(name + ".shortcut");
  405. if(shortcut1 != null)
  406. inputHandler.addKeyBinding(shortcut1,name);
  407. String shortcut2 = getProperty(name + ".shortcut2");
  408. if(shortcut2 != null)
  409. inputHandler.addKeyBinding(shortcut2,name);
  410. }
  411. } //}}}
  412. //{{{ getProperty() method
  413. /**
  414. * Returns a property for the given name.
  415. * In jEdit it will returns a jEdit.getProperty(name), but it can
  416. * return something else for a standalone textarea.
  417. * @param name the property name
  418. * @return the property value
  419. * @since 4.3pre13
  420. */
  421. protected abstract String getProperty(String name);
  422. //}}}
  423. //{{{ Package-private members
  424. JEditActionContext context;
  425. //{{{ getActionNames() method
  426. void getActionNames(List<String> list)
  427. {
  428. list.addAll(actions.keySet());
  429. } //}}}
  430. //}}}
  431. //{{{ Private members
  432. protected Hashtable<String,Object> actions;
  433. protected URL uri;
  434. protected boolean loaded;
  435. protected static final Object placeholder = new Object();
  436. //}}}
  437. }