AbstractResource.java
001 /*
002  *  AbstractResource.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 15/Oct/2000
013  *
014  *  $Id: AbstractResource.java 18719 2015-05-28 12:06:30Z markagreenwood $
015  */
016 
017 package gate.creole;
018 
019 import gate.Factory;
020 import gate.FeatureMap;
021 import gate.Gate;
022 import gate.Resource;
023 import gate.util.AbstractFeatureBearer;
024 import gate.util.Err;
025 import gate.util.GateException;
026 import gate.util.Strings;
027 import gate.util.Tools;
028 
029 import java.beans.BeanInfo;
030 import java.beans.EventSetDescriptor;
031 import java.beans.IntrospectionException;
032 import java.beans.Introspector;
033 import java.beans.PropertyDescriptor;
034 import java.io.Serializable;
035 import java.lang.reflect.Constructor;
036 import java.lang.reflect.InvocationTargetException;
037 import java.lang.reflect.Method;
038 import java.util.HashMap;
039 import java.util.Iterator;
040 import java.util.List;
041 import java.util.Map;
042 
043 
044 /** A convenience implementation of Resource with some default code.
045   */
046 abstract public class AbstractResource
047 extends AbstractFeatureBearer implements Resource, Serializable
048 {
049   static final long serialVersionUID = -9196293927841163321L;
050 
051   /** Initialise this resource, and return it. */
052   @Override
053   public Resource init() throws ResourceInstantiationException {
054     return this;
055   // init()
056 
057     /** Sets the name of this resource*/
058   @Override
059   public void setName(String name){
060     this.name = name;
061   }
062 
063   /** Returns the name of this resource*/
064   @Override
065   public String getName(){
066     return name;
067   }
068 
069   protected String name;
070   /**
071    * releases the memory allocated to this resource
072    */
073   @Override
074   public void cleanup(){
075   }
076 
077   //Parameters utility methods
078   /**
079    * Gets the value of a parameter for a resource.
080    @param resource the resource from which the parameter value will be
081    * obtained
082    @param paramaterName the name of the parameter
083    @return the current value of the parameter
084    */
085   public static Object getParameterValue(Resource resource,
086                                          String paramaterName)
087                 throws ResourceInstantiationException{
088     // get the beaninfo for the resource bean, excluding data about Object
089     BeanInfo resBeanInf = null;
090     try {
091       resBeanInf = getBeanInfo(resource.getClass());
092     catch(Exception e) {
093       throw new ResourceInstantiationException(
094         "Couldn't get bean info for resource " + resource.getClass().getName()
095         + Strings.getNl() "Introspector exception was: " + e
096       );
097     }
098     PropertyDescriptor[] properties = resBeanInf.getPropertyDescriptors();
099 
100     //find the property we're interested on
101     if(properties == null){
102       throw new ResourceInstantiationException(
103         "Couldn't get properties info for resource " +
104         resource.getClass().getName());
105     }
106     boolean done = false;
107     int i = 0;
108     Object value = null;
109     while(!done && i < properties.length){
110       PropertyDescriptor prop = properties[i];
111       if(prop.getName().equals(paramaterName)){
112         Method getMethod = prop.getReadMethod();
113         if(getMethod == null){
114           throw new ResourceInstantiationException(
115             "Couldn't get read accessor method for parameter " + paramaterName +
116             " in " + resource.getClass().getName());
117         }
118         // call the get method with the parameter value
119         Object[] args = new Object[0];
120         try {
121           value = getMethod.invoke(resource, args);
122         catch(Exception e) {
123           throw new ResourceInstantiationException(
124             "couldn't invoke get method: " + e
125           );
126         }
127         done = true;
128       }//if(prop.getName().equals(paramaterName))
129       i++;
130     }//while(!done && i < properties.length)
131     if(donereturn value;
132     else throw new ResourceInstantiationException(
133             "Couldn't find parameter named " + paramaterName +
134             " in " + resource.getClass().getName());
135   }
136 
137   /**
138    * Sets the value for a specified parameter for a resource.
139    *
140    @param resource the resource for which the parameter value will be set
141    @param paramaterName the name for the parameter
142    @param parameterValue the value the parameter will receive
143    */
144   public static void setParameterValue(Resource resource, BeanInfo resBeanInf,
145                                        String paramaterName,
146                                        Object parameterValue)
147               throws ResourceInstantiationException{
148     PropertyDescriptor[] properties = resBeanInf.getPropertyDescriptors();
149     //find the property we're interested on
150     if(properties == null){
151       throw new ResourceInstantiationException(
152         "Couldn't get properties info for resource " +
153         resource.getClass().getName());
154     }
155     boolean done = false;
156     int i = 0;
157     while(!done && i < properties.length){
158       PropertyDescriptor prop = properties[i];
159       if(prop.getName().equals(paramaterName)){
160         Method setMethod = prop.getWriteMethod();
161         if(setMethod == null){
162           throw new ResourceInstantiationException(
163             "Couldn't get write accessor method for parameter " +
164             paramaterName + " in " + resource.getClass().getName());
165         }
166 
167         // convert the parameter to the right type eg String -> URL
168         if(parameterValue != null){
169           Class<?> propertyType = prop.getPropertyType();
170           Class<?> typeToCreate = propertyType;
171           if(Parameter.substituteClasses.containsKey(propertyType)) {
172             typeToCreate = Parameter.substituteClasses.get(propertyType);
173           }
174           Class<?> paramType = parameterValue.getClass();
175           if(!propertyType.isAssignableFrom(paramType)) {
176             try {
177               Constructor<?> mostSpecificConstructor =
178                 Tools.getMostSpecificConstructor(typeToCreate, paramType);
179               parameterValue = mostSpecificConstructor
180                  .newInstancenew Object[]{parameterValue} );
181             catch(Exception e) {
182               //this didn't work; if the parameter value is String
183               //try to use the Parameter implementation for finding the
184               //value
185               if(String.class.isAssignableFrom(paramType)){
186                 ResourceData rData = Gate.getCreoleRegister().
187                   get(resource.getClass().getName());
188                 ParameterList pList = rData.getParameterList();
189                 Parameter param = null;
190                 Iterator<List<Parameter>> disjIter = pList.getInitimeParameters().iterator();
191                 while(param == null && disjIter.hasNext()){
192                   Iterator<Parameter> paramIter = disjIter.next().iterator();
193                   while(param == null && paramIter.hasNext()){
194                     Parameter aParam = paramIter.next();
195                     if(aParam.getName().equals(paramaterName)) param = aParam;
196                   }
197                 }
198                 disjIter = pList.getRuntimeParameters().iterator();
199                 while(param == null && disjIter.hasNext()){
200                   Iterator<Parameter> paramIter = disjIter.next().iterator();
201                   while(param == null && paramIter.hasNext()){
202                     Parameter aParam = paramIter.next();
203                     if(aParam.getName().equals(paramaterName)) param = aParam;
204                   }
205                 }
206                 if(param != null){
207                   try{
208                     parameterValue = param.calculateValueFromString(
209                             (String)parameterValue);
210                   }catch(ParameterException pe){
211                     throw new ResourceInstantiationException(pe);
212                   }
213                 }else{
214                   // if this happens it means that the class has the specified parameter
215                   // name does indeed correspond to a getter/setter pair on the class
216                   // but there is no associated CREOLE metadata for that parameter name
217                   throw new ResourceInstantiationException("Property " + paramaterName +
218                       " of resource class " + resource.getClass().getName() +
219                       " is not declared as a CREOLE parameter.  Automatic" +
220                       " conversion of string values to other types is only" +
221                       " supported for declared parameters.")
222                 }
223               }else{
224                 throw new ResourceInstantiationException(
225                   "Error converting " + parameterValue.getClass() +
226                   " to " + propertyType + ": " + e.toString()
227                 );
228               }
229             }
230           }
231         }//if(parameterValue != null)
232 
233         // call the set method with the parameter value
234         Object[] args = new Object[1];
235         args[0= parameterValue;
236         try {
237           setMethod.invoke(resource, args);
238         catch(Exception e) {
239           e.printStackTrace(Err.getPrintWriter());
240           throw new ResourceInstantiationException(
241             "couldn't invoke set method for " + paramaterName +
242             " on " + resource.getClass().getName() ": " + e);
243         }
244         done = true;
245       }//if(prop.getName().equals(paramaterName))
246       i++;
247     }//while(!done && i < properties.length)
248     if(!donethrow new ResourceInstantiationException(
249                           "Couldn't find parameter named " + paramaterName +
250                           " in " + resource.getClass().getName());
251   }//public void setParameterValue(String paramaterName, Object parameterValue)
252 
253 
254   /**
255    * Sets the values for more parameters for a resource in one step.
256    *
257    @param parameters a feature map that has parameter names as keys and
258    * parameter values as values.
259    */
260   public static void setParameterValues(Resource resource,
261                                         FeatureMap parameters)
262               throws ResourceInstantiationException{
263     // get the beaninfo for the resource bean, excluding data about Object
264     BeanInfo resBeanInf = null;
265     try {
266       resBeanInf = getBeanInfo(resource.getClass());
267     catch(Exception e) {
268       throw new ResourceInstantiationException(
269         "Couldn't get bean info for resource " + resource.getClass().getName()
270         + Strings.getNl() "Introspector exception was: " + e
271       );
272     }
273 
274     Iterator<Object> parnameIter = parameters.keySet().iterator();
275     while(parnameIter.hasNext()){
276       String parName = (String)parnameIter.next();
277       setParameterValue(resource, resBeanInf, parName, parameters.get(parName));
278     }
279   }
280 
281 
282   /**
283    * Adds listeners to a resource.
284    @param listeners The listeners to be registered with the resource. A
285    {@link java.util.Map} that maps from fully qualified class name (as a
286    * string) to listener (of the type declared by the key).
287    @param resource the resource that listeners will be registered to.
288    */
289   public static void setResourceListeners(Resource resource, Map<String, ? extends Object> listeners)
290   throws
291     IntrospectionException, InvocationTargetException,
292     IllegalAccessException, GateException
293   {
294     // get the beaninfo for the resource bean, excluding data about Object
295     BeanInfo resBeanInfo = getBeanInfo(resource.getClass());
296 
297     // get all the events the bean can fire
298     EventSetDescriptor[] events = resBeanInfo.getEventSetDescriptors();
299 
300     // add the listeners
301     if (events != null) {
302       EventSetDescriptor event;
303       for(int i = 0; i < events.length; i++) {
304         event = events[i];
305 
306         // did we get such a listener?
307         Object listener =
308           listeners.get(event.getListenerType().getName());
309         if(listener != null) {
310           Method addListener = event.getAddListenerMethod();
311 
312           // call the set method with the parameter value
313           Object[] args = new Object[1];
314           args[0= listener;
315           addListener.invoke(resource, args);
316         }
317       // for each event
318     }   // if events != null
319   // setResourceListeners()
320 
321   /**
322    * Removes listeners from a resource.
323    @param listeners The listeners to be removed from the resource. A
324    {@link java.util.Map} that maps from fully qualified class name
325    * (as a string) to listener (of the type declared by the key).
326    @param resource the resource that listeners will be removed from.
327    */
328   public static void removeResourceListeners(Resource resource, Map<String, ? extends Object> listeners)
329                      throws IntrospectionException, InvocationTargetException,
330                             IllegalAccessException, GateException{
331 
332     // get the beaninfo for the resource bean, excluding data about Object
333     BeanInfo resBeanInfo = getBeanInfo(resource.getClass());
334 
335     // get all the events the bean can fire
336     EventSetDescriptor[] events = resBeanInfo.getEventSetDescriptors();
337 
338     //remove the listeners
339     if(events != null) {
340       EventSetDescriptor event;
341       for(int i = 0; i < events.length; i++) {
342         event = events[i];
343 
344         // did we get such a listener?
345         Object listener =
346           listeners.get(event.getListenerType().getName());
347         if(listener != null) {
348           Method removeListener = event.getRemoveListenerMethod();
349 
350           // call the set method with the parameter value
351           Object[] args = new Object[1];
352           args[0= listener;
353           removeListener.invoke(resource, args);
354         }
355       // for each event
356     }   // if events != null
357   // removeResourceListeners()
358 
359   /**
360    * Checks whether the provided {@link Resource} has values for all the
361    * required parameters from the provided list of parameters.
362    *
363    @param resource the resource being checked
364    @param parameters is a {@link List} of {@link List} of {@link Parameter}
365    * representing a list of parameter disjunctions (e.g. the one returned by
366    {@link ParameterList#getRuntimeParameters()}).
367    @return <tt>true</tt> if all the required parameters have non null values,
368    <tt>false</tt> otherwise.
369    @throws {@link ResourceInstantiationException} if problems occur while
370    * inspecting the parameters for the resource. These will normally be
371    * introspection problems and are usually caused by the lack of a parameter
372    * or of the read accessor for a parameter.
373    */
374   public static boolean checkParameterValues(Resource resource,
375                                              List<List<Parameter>> parameters)
376                 throws ResourceInstantiationException{
377     Iterator<List<Parameter>> disIter = parameters.iterator();
378     while(disIter.hasNext()){
379       List<Parameter> disjunction = disIter.next();
380       boolean required = !disjunction.get(0).isOptional();
381       if(required){
382         //at least one parameter in the disjunction must have a value
383         boolean valueSet = false;
384         Iterator<Parameter> parIter = disjunction.iterator();
385         while(!valueSet && parIter.hasNext()){
386           Parameter par = parIter.next();
387           valueSet = (resource.getParameterValue(par.getName()) != null);
388         }
389         if(!valueSetreturn false;
390       }
391     }
392     return true;
393   }
394 
395 
396 
397   /**
398    * Gets the value of a parameter of this resource.
399    @param paramaterName the name of the parameter
400    @return the current value of the parameter
401    */
402   @Override
403   public Object getParameterValue(String paramaterName)
404                 throws ResourceInstantiationException{
405     return getParameterValue(this, paramaterName);
406   }
407 
408   /**
409    * Sets the value for a specified parameter for this resource.
410    *
411    @param paramaterName the name for the parameter
412    @param parameterValue the value the parameter will receive
413    */
414   @Override
415   public void setParameterValue(String paramaterName, Object parameterValue)
416               throws ResourceInstantiationException{
417     // get the beaninfo for the resource bean, excluding data about Object
418     BeanInfo resBeanInf = null;
419     try {
420       resBeanInf = getBeanInfo(this.getClass());
421     catch(Exception e) {
422       throw new ResourceInstantiationException(
423         "Couldn't get bean info for resource " this.getClass().getName()
424         + Strings.getNl() "Introspector exception was: " + e
425       );
426     }
427     setParameterValue(this, resBeanInf, paramaterName, parameterValue);
428   }
429 
430   /**
431    * Sets the values for more parameters for this resource in one step.
432    *
433    @param parameters a feature map that has paramete names as keys and
434    * parameter values as values.
435    */
436   @Override
437   public void setParameterValues(FeatureMap parameters)
438               throws ResourceInstantiationException{
439     setParameterValues(this, parameters);
440   }
441 
442   /**
443    * Get the current values of the given parameters from the given
444    * resource.
445    *
446    @param res the resource
447    @param params a list of parameter disjunctions such as would
448    *         be returned by {@link ParameterList#getInitimeParameters()}
449    *         or similar.
450    */
451   public static FeatureMap getParameterValues(Resource res,
452           List<List<Parameter>> paramsthrows ResourceInstantiationException {
453     FeatureMap fm = Factory.newFeatureMap();
454     for(List<Parameter> parDisjunction : params) {
455       for(Parameter p : parDisjunction) {
456         fm.put(p.getName(), res.getParameterValue(p.getName()));
457       }
458     }
459     return fm;
460   }
461 
462   /**
463    * Get the current values for all of a specified resource's
464    * registered init-time parameters.
465    */
466   public static FeatureMap getInitParameterValues(Resource res)
467               throws ResourceInstantiationException {
468     ResourceData rData = Gate.getCreoleRegister().get(
469             res.getClass().getName());
470     if(rData == null)
471       throw new ResourceInstantiationException(
472               "Could not find CREOLE data for " + res.getClass().getName());
473 
474     ParameterList params = rData.getParameterList();
475 
476     return getParameterValues(res, params.getInitimeParameters());
477   }
478 
479   /**
480    * Get the current values for all this resource's registered
481    * init-time parameters.
482    */
483   public FeatureMap getInitParameterValues() throws ResourceInstantiationException {
484     return getInitParameterValues(this);
485   }
486 
487   private static Map<Class<? extends Resource>, BeanInfo> beanInfoCache = new HashMap<Class<? extends Resource>,BeanInfo>();
488   
489   public static BeanInfo getBeanInfo(Class<? extends Resource> cthrows IntrospectionException {
490     
491     BeanInfo r = beanInfoCache.get(c);
492     if(r == null) {
493       r = Introspector.getBeanInfo(c, Object.class);
494       beanInfoCache.put(c, r);
495     }
496     return r;
497   }
498   
499   public static boolean forgetBeanInfo(Class<? extends Resource> c) {
500     return (beanInfoCache.remove(c!= null);
501   }
502   
503   public static void flushBeanInfoCache() {
504     beanInfoCache.clear();
505   }
506   
507   @Override
508   public String toString() {
509     return getName();
510   }
511 }