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