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

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

#
Java | 389 lines | 170 code | 31 blank | 188 comment | 20 complexity | cf969e014d824d387c802d83ae798612 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. * ServiceManager.java - Handles services.xml files in plugins
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2003 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit;
  23. import java.io.*;
  24. import java.net.URL;
  25. import java.util.*;
  26. import org.gjt.sp.util.Log;
  27. import org.gjt.sp.util.XMLUtilities;
  28. import org.gjt.sp.util.StandardUtilities;
  29. import org.gjt.sp.jedit.buffer.FoldHandlerProvider;
  30. import org.gjt.sp.jedit.buffer.FoldHandler;
  31. /**
  32. * A generic way for plugins to provide various API extensions.<p>
  33. *
  34. * Services are loaded from files named <code>services.xml</code> inside the
  35. * plugin JAR. A service definition file has the following form:
  36. *
  37. * <pre>&lt;?xml version="1.0"?&gt;
  38. *&lt;!DOCTYPE SERVICES SYSTEM "services.dtd"&gt;
  39. *&lt;SERVICES&gt;
  40. * &lt;SERVICE NAME="service name" CLASS="fully qualified class name"&gt;
  41. * // BeanShell code evaluated when the sevice is first activated
  42. * &lt;/SERVICE&gt;
  43. *&lt;/SERVICES&gt;</pre>
  44. *
  45. * The following elements are valid:
  46. *
  47. * <ul>
  48. * <li>
  49. * <code>SERVICES</code> is the top-level element and refers
  50. * to the set of services offered by the plugin.
  51. * </li>
  52. * <li>
  53. * A <code>SERVICE</code> contains the data for a particular service
  54. * activation.
  55. * It has two attributes, both required: <code>NAME</code> and
  56. * <code>CLASS</code>. The <code>CLASS</code> attribute must be the name of
  57. * a known sevice type; see below.
  58. * </li>
  59. * <li>
  60. * A <code>SERVICE</code> element should the BeanShell code that returns a
  61. * new instance of the named class. Note that this code can return
  62. * <code>null</code>.
  63. * </li>
  64. * </ul>
  65. *
  66. * The jEdit core defines the following service types:
  67. * <ul>
  68. * <li>{@link org.gjt.sp.jedit.buffer.FoldHandler}</li>
  69. * <li>{@link org.gjt.sp.jedit.io.VFS}</li>
  70. * <li>{@link org.gjt.sp.jedit.io.Encoding}</li>
  71. * <li>{@link org.gjt.sp.jedit.io.EncodingDetector}</li>
  72. * </ul>
  73. *
  74. * Plugins may provide more.<p>
  75. *
  76. * To have your plugin accept services, no extra steps are needed other than
  77. * a piece of code somewhere that calls {@link #getServiceNames(String)} and
  78. * {@link #getService(String,String)}.
  79. *
  80. * @see BeanShell
  81. * @see PluginJAR
  82. *
  83. * @since jEdit 4.2pre1
  84. * @author Slava Pestov
  85. * @version $Id: ServiceManager.java 20108 2011-10-18 12:16:38Z evanpw $
  86. */
  87. public class ServiceManager
  88. {
  89. //{{{ loadServices() method
  90. /**
  91. * Loads a <code>services.xml</code> file.
  92. * @since jEdit 4.2pre1
  93. */
  94. public static void loadServices(PluginJAR plugin, URL uri,
  95. PluginJAR.PluginCacheEntry cache)
  96. {
  97. ServiceListHandler dh = new ServiceListHandler(plugin,uri);
  98. try
  99. {
  100. if (!XMLUtilities.parseXML(uri.openStream(), dh)
  101. && cache != null)
  102. {
  103. cache.cachedServices = dh.getCachedServices();
  104. }
  105. }
  106. catch (IOException ioe)
  107. {
  108. Log.log(Log.ERROR, ServiceManager.class, ioe);
  109. }
  110. } //}}}
  111. //{{{ unloadServices() method
  112. /**
  113. * Removes all services belonging to the specified plugin.
  114. * @param plugin The plugin
  115. * @since jEdit 4.2pre1
  116. */
  117. public static void unloadServices(PluginJAR plugin)
  118. {
  119. Iterator<Descriptor> descriptors = serviceMap.keySet().iterator();
  120. while(descriptors.hasNext())
  121. {
  122. Descriptor d = descriptors.next();
  123. if(d.plugin == plugin)
  124. descriptors.remove();
  125. }
  126. } //}}}
  127. //{{{ registerService() method
  128. /**
  129. * Registers a service. Plugins should provide a
  130. * <code>services.xml</code> file instead of calling this directly.
  131. *
  132. * @param clazz The service class
  133. * @param name The service name
  134. * @param code BeanShell code to create an instance of this
  135. * @param plugin The plugin JAR, or null if this is a built-in service
  136. *
  137. * @since jEdit 4.2pre1
  138. */
  139. public static void registerService(String clazz, String name,
  140. String code, PluginJAR plugin)
  141. {
  142. Descriptor d = new Descriptor(clazz,name,code,plugin);
  143. serviceMap.put(d,d);
  144. } //}}}
  145. //{{{ unregisterService() method
  146. /**
  147. * Unregisters a service.
  148. *
  149. * @param clazz The service class
  150. * @param name The service name
  151. *
  152. * @since jEdit 4.2pre1
  153. */
  154. public static void unregisterService(String clazz, String name)
  155. {
  156. Descriptor d = new Descriptor(clazz,name);
  157. serviceMap.remove(d);
  158. } //}}}
  159. //{{{ getServiceTypes() method
  160. /**
  161. * Returns all known service class types.
  162. *
  163. * @since jEdit 4.2pre1
  164. */
  165. public static String[] getServiceTypes()
  166. {
  167. Set<String> returnValue = new HashSet<String>();
  168. Set<Descriptor> keySet = serviceMap.keySet();
  169. for (Descriptor d : keySet)
  170. returnValue.add(d.clazz);
  171. return returnValue.toArray(
  172. new String[returnValue.size()]);
  173. } //}}}
  174. //{{{ getServiceNames() method
  175. /**
  176. * Returns the names of all registered services with the given
  177. * class. For example, calling this with a parameter of
  178. * "org.gjt.sp.jedit.io.VFS" returns all known virtual file
  179. * systems.
  180. *
  181. * @param clazz The class name
  182. * @since jEdit 4.2pre1
  183. */
  184. public static String[] getServiceNames(String clazz)
  185. {
  186. List<String> returnValue = new ArrayList<String>();
  187. Set<Descriptor> keySet = serviceMap.keySet();
  188. for (Descriptor d : keySet)
  189. if(d.clazz.equals(clazz))
  190. returnValue.add(d.name);
  191. return returnValue.toArray(
  192. new String[returnValue.size()]);
  193. } //}}}
  194. //{{{ getServiceNames() method
  195. public static String[] getServiceNames(Class clazz)
  196. {
  197. return getServiceNames(clazz.getName());
  198. } //}}}
  199. //{{{ getService() methods
  200. /**
  201. * Returns an instance of the given service. The first time this is
  202. * called for a given service, the BeanShell code is evaluated. The
  203. * result is cached for future invocations, so in effect services are
  204. * singletons.
  205. *
  206. * @param clazz The service class
  207. * @param name The service name
  208. * @since jEdit 4.2pre1
  209. */
  210. public static Object getService(String clazz, String name)
  211. {
  212. Descriptor key = new Descriptor(clazz,name);
  213. Descriptor value = serviceMap.get(key);
  214. if(value == null)
  215. {
  216. // unknown service - <clazz,name> not in table
  217. return null;
  218. }
  219. else
  220. {
  221. if(value.code == null)
  222. {
  223. loadServices(value.plugin,
  224. value.plugin.getServicesURI(),
  225. null);
  226. value = serviceMap.get(key);
  227. }
  228. return value.getInstance();
  229. }
  230. }
  231. /**
  232. * Returns an instance of the given service. The first time this is
  233. * called for a given service, the BeanShell code is evaluated. The
  234. * result is cached for future invocations, so in effect services are
  235. * singletons.
  236. *
  237. * @param clazz The service class
  238. * @param name The service name
  239. * @return the service instance
  240. * @since jEdit 4.4pre1
  241. */
  242. public static <E> E getService(Class<E> clazz, String name)
  243. {
  244. return (E) getService(clazz.getName(), name);
  245. } //}}}
  246. //{{{ Package-private members
  247. //{{{ registerService() method
  248. /**
  249. * Registers a service.
  250. *
  251. * @param d the service descriptor
  252. * @since jEdit 4.2pre1
  253. */
  254. static void registerService(Descriptor d)
  255. {
  256. serviceMap.put(d,d);
  257. } //}}}
  258. //}}}
  259. //{{{ Private members
  260. private static final Map<Descriptor, Descriptor> serviceMap = new HashMap<Descriptor, Descriptor>();
  261. //}}}
  262. //{{{ Descriptor class
  263. static class Descriptor
  264. {
  265. final String clazz;
  266. final String name;
  267. String code;
  268. PluginJAR plugin;
  269. Object instance;
  270. boolean instanceIsNull;
  271. // this constructor keys the hash table
  272. Descriptor(String clazz, String name)
  273. {
  274. this.clazz = clazz;
  275. this.name = name;
  276. }
  277. // this constructor is the value of the hash table
  278. Descriptor(String clazz, String name, String code,
  279. PluginJAR plugin)
  280. {
  281. this.clazz = clazz;
  282. this.name = name;
  283. this.code = code;
  284. this.plugin = plugin;
  285. }
  286. Object getInstance()
  287. {
  288. if(instanceIsNull)
  289. return null;
  290. else if(instance == null)
  291. {
  292. // lazy instantiation
  293. instance = BeanShell.eval(null,
  294. BeanShell.getNameSpace(),
  295. code);
  296. if(instance == null)
  297. {
  298. // avoid re-running script if it gives
  299. // us null
  300. instanceIsNull = true;
  301. }
  302. }
  303. return instance;
  304. }
  305. public int hashCode()
  306. {
  307. return name.hashCode();
  308. }
  309. public boolean equals(Object o)
  310. {
  311. if(o instanceof Descriptor)
  312. {
  313. Descriptor d = (Descriptor)o;
  314. return d.clazz.equals(clazz)
  315. && d.name.equals(name);
  316. }
  317. else
  318. return false;
  319. }
  320. } //}}}
  321. /**
  322. * A FoldHandler based on the ServiceManager
  323. * @author Matthieu Casanova
  324. * @since jEdit 4.3pre10
  325. */
  326. public static class ServiceFoldHandlerProvider implements FoldHandlerProvider
  327. {
  328. /**
  329. * The service type. See {@link org.gjt.sp.jedit.ServiceManager}.
  330. * @since jEdit 4.3pre10
  331. */
  332. public static final String SERVICE = "org.gjt.sp.jedit.buffer.FoldHandler";
  333. /**
  334. * Returns the fold handler with the specified name, or null if
  335. * there is no registered handler with that name.
  336. * @param name The name of the desired fold handler
  337. * @return the FoldHandler or null if it doesn't exist
  338. * @since jEdit 4.3pre10
  339. */
  340. public FoldHandler getFoldHandler(String name)
  341. {
  342. FoldHandler handler = (FoldHandler) getService(SERVICE,name);
  343. return handler;
  344. }
  345. /**
  346. * Returns an array containing the names of all registered fold
  347. * handlers.
  348. *
  349. * @since jEdit 4.3pre10
  350. */
  351. public String[] getFoldModes()
  352. {
  353. String[] handlers = getServiceNames(SERVICE);
  354. Arrays.sort(handlers,new StandardUtilities.StringCompare<String>());
  355. return handlers;
  356. }
  357. }
  358. }