1   /*
2    *  AbstractResource.java
3    *
4    *  Copyright (c) 1998-2005, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Hamish Cunningham, 15/Oct/2000
12   *
13   *  $Id: AbstractResource.java,v 1.18 2005/01/11 13:51:31 ian Exp $
14   */
15  
16  package gate.creole;
17  
18  import java.beans.*;
19  import java.io.Serializable;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.util.*;
23  
24  import gate.*;
25  import gate.FeatureMap;
26  import gate.Resource;
27  import gate.util.*;
28  
29  
30  /** A convenience implementation of Resource with some default code.
31    */
32  abstract public class AbstractResource
33  extends AbstractFeatureBearer implements Resource, Serializable
34  {
35    static final long serialVersionUID = -9196293927841163321L;
36  
37    /** Initialise this resource, and return it. */
38    public Resource init() throws ResourceInstantiationException {
39      return this;
40    } // init()
41  
42      /** Sets the name of this resource*/
43    public void setName(String name){
44      this.name = name;
45    }
46  
47    /** Returns the name of this resource*/
48    public String getName(){
49      return name;
50    }
51  
52    protected String name;
53    /**
54     * releases the memory allocated to this resource
55     */
56    public void cleanup(){
57    }
58  
59    //Parameters utility methods
60    /**
61     * Gets the value of a parameter for a resource.
62     * @param resource the resource from which the parameter value will be
63     * obtained
64     * @param paramaterName the name of the parameter
65     * @return the current value of the parameter
66     */
67    public static Object getParameterValue(Resource resource,
68                                           String paramaterName)
69                  throws ResourceInstantiationException{
70      // get the beaninfo for the resource bean, excluding data about Object
71      BeanInfo resBeanInf = null;
72      try {
73        resBeanInf = getBeanInfo(resource.getClass());
74      } catch(Exception e) {
75        throw new ResourceInstantiationException(
76          "Couldn't get bean info for resource " + resource.getClass().getName()
77          + Strings.getNl() + "Introspector exception was: " + e
78        );
79      }
80      PropertyDescriptor[] properties = resBeanInf.getPropertyDescriptors();
81  
82      //find the property we're interested on
83      if(properties == null){
84        throw new ResourceInstantiationException(
85          "Couldn't get properties info for resource " +
86          resource.getClass().getName());
87      }
88      boolean done = false;
89      int i = 0;
90      Object value = null;
91      while(!done && i < properties.length){
92        PropertyDescriptor prop = properties[i];
93        if(prop.getName().equals(paramaterName)){
94          Method getMethod = prop.getReadMethod();
95          if(getMethod == null){
96            throw new ResourceInstantiationException(
97              "Couldn't get read accessor method for parameter " + paramaterName +
98              " in " + resource.getClass().getName());
99          }
100         // call the get method with the parameter value
101         Object[] args = new Object[0];
102         try {
103           value = getMethod.invoke(resource, args);
104         } catch(Exception e) {
105           throw new ResourceInstantiationException(
106             "couldn't invoke get method: " + e
107           );
108         }
109         done = true;
110       }//if(prop.getName().equals(paramaterName))
111       i++;
112     }//while(!done && i < properties.length)
113     if(done) return value;
114     else throw new ResourceInstantiationException(
115             "Couldn't find parameter named " + paramaterName +
116             " in " + resource.getClass().getName());
117   }
118 
119   /**
120    * Sets the value for a specified parameter for a resource.
121    *
122    * @param resource the resource for which the parameter value will be set
123    * @param paramaterName the name for the parameter
124    * @param parameterValue the value the parameter will receive
125    */
126   public static void setParameterValue(Resource resource, BeanInfo resBeanInf,
127                                        String paramaterName,
128                                        Object parameterValue)
129               throws ResourceInstantiationException{
130     PropertyDescriptor[] properties = resBeanInf.getPropertyDescriptors();
131     //find the property we're interested on
132     if(properties == null){
133       throw new ResourceInstantiationException(
134         "Couldn't get properties info for resource " +
135         resource.getClass().getName());
136     }
137     boolean done = false;
138     int i = 0;
139     while(!done && i < properties.length){
140       PropertyDescriptor prop = properties[i];
141       if(prop.getName().equals(paramaterName)){
142         Method setMethod = prop.getWriteMethod();
143         if(setMethod == null){
144           throw new ResourceInstantiationException(
145             "Couldn't get write accessor method for parameter " +
146             paramaterName + " in " + resource.getClass().getName());
147         }
148 
149         // convert the parameter to the right type eg String -> URL
150         if(parameterValue != null){
151           Class propertyType = prop.getPropertyType();
152           Class paramType = parameterValue.getClass();
153           if(!propertyType.isAssignableFrom(paramType)) {
154             //first try to find an appropriate constructor
155             try {
156               parameterValue = propertyType.getConstructor(
157                   new Class[]{paramType}
158                 ).newInstance( new Object[]{parameterValue} );
159             } catch(Exception e) {
160               //this didn't work; if the parameter value is String
161               //try to use the Parameter implementation for finding the 
162               //value
163               if(String.class.isAssignableFrom(paramType)){
164                 ResourceData rData = (ResourceData)Gate.getCreoleRegister().
165                   get(resource.getClass().getName());
166                 ParameterList pList = rData.getParameterList();
167                 Parameter param = null;
168                 Iterator disjIter = pList.getInitimeParameters().iterator();
169                 while(param == null && disjIter.hasNext()){
170                   Iterator paramIter = ((List)disjIter.next()).iterator();
171                   while(param == null && paramIter.hasNext()){
172                     Parameter aParam = (Parameter)paramIter.next();
173                     if(aParam.getName().equals(paramaterName)) param = aParam; 
174                   }
175                 }
176                 disjIter = pList.getRuntimeParameters().iterator();
177                 while(param == null && disjIter.hasNext()){
178                   Iterator paramIter = ((List)disjIter.next()).iterator();
179                   while(param == null && paramIter.hasNext()){
180                     Parameter aParam = (Parameter)paramIter.next();
181                     if(aParam.getName().equals(paramaterName)) param = aParam; 
182                   }
183                 }
184                 if(param != null){
185                   try{
186                     parameterValue = param.calculateValueFromString(
187                             (String)parameterValue);
188                   }catch(ParameterException pe){
189                     throw new ResourceInstantiationException(pe);
190                   }
191                 }else{
192                   //this should never happen
193                   throw new LuckyException("Unknown parameter " + paramaterName +
194                           " for resource " + resource.getClass().getName() + 
195                           "!");
196                 }
197               }else{
198                 throw new ResourceInstantiationException(
199                   "Error converting " + parameterValue.getClass() +
200                   " to " + propertyType + ": " + e.toString()
201                 );
202               }
203             }
204           }
205         }//if(parameterValue != null)
206 
207         // call the set method with the parameter value
208         Object[] args = new Object[1];
209         args[0] = parameterValue;
210         try {
211           setMethod.invoke(resource, args);
212         } catch(Exception e) {
213           e.printStackTrace(Err.getPrintWriter());
214           throw new ResourceInstantiationException(
215             "couldn't invoke set method for " + paramaterName +
216             " on " + resource.getClass().getName() + ": " + e);
217         }
218         done = true;
219       }//if(prop.getName().equals(paramaterName))
220       i++;
221     }//while(!done && i < properties.length)
222     if(!done) throw new ResourceInstantiationException(
223                           "Couldn't find parameter named " + paramaterName +
224                           " in " + resource.getClass().getName());
225   }//public void setParameterValue(String paramaterName, Object parameterValue)
226 
227 
228   /**
229    * Sets the values for more parameters for a resource in one step.
230    *
231    * @param parameters a feature map that has paramete names as keys and
232    * parameter values as values.
233    */
234   public static void setParameterValues(Resource resource,
235                                         FeatureMap parameters)
236               throws ResourceInstantiationException{
237     // get the beaninfo for the resource bean, excluding data about Object
238     BeanInfo resBeanInf = null;
239     try {
240       resBeanInf = getBeanInfo(resource.getClass());
241     } catch(Exception e) {
242       throw new ResourceInstantiationException(
243         "Couldn't get bean info for resource " + resource.getClass().getName()
244         + Strings.getNl() + "Introspector exception was: " + e
245       );
246     }
247 
248     Iterator parnameIter = parameters.keySet().iterator();
249     while(parnameIter.hasNext()){
250       String parName = (String)parnameIter.next();
251       setParameterValue(resource, resBeanInf, parName, parameters.get(parName));
252     }
253   }
254 
255 
256   /**
257    * Adds listeners to a resource.
258    * @param listeners The listeners to be registered with the resource. A
259    * {@link java.util.Map} that maps from fully qualified class name (as a
260    * string) to listener (of the type declared by the key).
261    * @param resource the resource that listeners will be registered to.
262    */
263   public static void setResourceListeners(Resource resource, Map listeners)
264   throws
265     IntrospectionException, InvocationTargetException,
266     IllegalAccessException, GateException
267   {
268     // get the beaninfo for the resource bean, excluding data about Object
269     BeanInfo resBeanInfo = getBeanInfo(resource.getClass());
270 
271     // get all the events the bean can fire
272     EventSetDescriptor[] events = resBeanInfo.getEventSetDescriptors();
273 
274     // add the listeners
275     if (events != null) {
276       EventSetDescriptor event;
277       for(int i = 0; i < events.length; i++) {
278         event = events[i];
279 
280         // did we get such a listener?
281         Object listener =
282           listeners.get(event.getListenerType().getName());
283         if(listener != null) {
284           Method addListener = event.getAddListenerMethod();
285 
286           // call the set method with the parameter value
287           Object[] args = new Object[1];
288           args[0] = listener;
289           addListener.invoke(resource, args);
290         }
291       } // for each event
292     }   // if events != null
293   } // setResourceListeners()
294 
295   /**
296    * Removes listeners from a resource.
297    * @param listeners The listeners to be removed from the resource. A
298    * {@link java.util.Map} that maps from fully qualified class name
299    * (as a string) to listener (of the type declared by the key).
300    * @param resource the resource that listeners will be removed from.
301    */
302   public static void removeResourceListeners(Resource resource, Map listeners)
303                      throws IntrospectionException, InvocationTargetException,
304                             IllegalAccessException, GateException{
305 
306     // get the beaninfo for the resource bean, excluding data about Object
307     BeanInfo resBeanInfo = getBeanInfo(resource.getClass());
308 
309     // get all the events the bean can fire
310     EventSetDescriptor[] events = resBeanInfo.getEventSetDescriptors();
311 
312     //remove the listeners
313     if(events != null) {
314       EventSetDescriptor event;
315       for(int i = 0; i < events.length; i++) {
316         event = events[i];
317 
318         // did we get such a listener?
319         Object listener =
320           listeners.get(event.getListenerType().getName());
321         if(listener != null) {
322           Method removeListener = event.getRemoveListenerMethod();
323 
324           // call the set method with the parameter value
325           Object[] args = new Object[1];
326           args[0] = listener;
327           removeListener.invoke(resource, args);
328         }
329       } // for each event
330     }   // if events != null
331   } // removeResourceListeners()
332 
333   /**
334    * Checks whether the provided {@link Resource} has values for all the
335    * required parameters from the provided list of parameters.
336    *
337    * @param resource the resource being checked
338    * @param parameters is a {@link List} of {@link List} of {@link Parameter}
339    * representing a list of parameter disjunctions (e.g. the one returned by
340    * {@link ParameterList#getRuntimeParameters()}).
341    * @return <tt>true</tt> if all the required parameters have non null values,
342    * <tt>false</tt> otherwise.
343    * @throws {@link ResourceInstantiationException} if problems occur while
344    * inspecting the parameters for the resource. These will normally be
345    * introspection problems and are usually caused by the lack of a parameter
346    * or of the read accessor for a parameter.
347    */
348   public static boolean checkParameterValues(Resource resource,
349                                              List parameters)
350                 throws ResourceInstantiationException{
351     Iterator disIter = parameters.iterator();
352     while(disIter.hasNext()){
353       List disjunction = (List)disIter.next();
354       boolean required = !((Parameter)disjunction.get(0)).isOptional();
355       if(required){
356         //at least one parameter in the disjunction must have a value
357         boolean valueSet = false;
358         Iterator parIter = disjunction.iterator();
359         while(!valueSet && parIter.hasNext()){
360           Parameter par = (Parameter)parIter.next();
361           valueSet = (resource.getParameterValue(par.getName()) != null);
362         }
363         if(!valueSet) return false;
364       }
365     }
366     return true;
367   }
368 
369 
370 
371   /**
372    * Gets the value of a parameter of this resource.
373    * @param paramaterName the name of the parameter
374    * @return the current value of the parameter
375    */
376   public Object getParameterValue(String paramaterName)
377                 throws ResourceInstantiationException{
378     return getParameterValue(this, paramaterName);
379   }
380 
381   /**
382    * Sets the value for a specified parameter for this resource.
383    *
384    * @param paramaterName the name for the parameter
385    * @param parameterValue the value the parameter will receive
386    */
387   public void setParameterValue(String paramaterName, Object parameterValue)
388               throws ResourceInstantiationException{
389     // get the beaninfo for the resource bean, excluding data about Object
390     BeanInfo resBeanInf = null;
391     try {
392       resBeanInf = getBeanInfo(this.getClass());
393     } catch(Exception e) {
394       throw new ResourceInstantiationException(
395         "Couldn't get bean info for resource " + this.getClass().getName()
396         + Strings.getNl() + "Introspector exception was: " + e
397       );
398     }
399     setParameterValue(this, resBeanInf, paramaterName, parameterValue);
400   }
401 
402   /**
403    * Sets the values for more parameters for this resource in one step.
404    *
405    * @param parameters a feature map that has paramete names as keys and
406    * parameter values as values.
407    */
408   public void setParameterValues(FeatureMap parameters)
409               throws ResourceInstantiationException{
410     setParameterValues(this, parameters);
411   }
412 
413   private static int beanCount = 0;
414   private static Hashtable beanInfoCache = new Hashtable();
415 
416   public static BeanInfo getBeanInfo (Class c) throws IntrospectionException
417   {
418     beanCount = beanCount + 1;
419     BeanInfo r = ((BeanInfo) beanInfoCache.get(c));
420     if (r == null) {
421       r = Introspector.getBeanInfo(c, Object.class);
422       beanInfoCache.put(c, r);
423     }
424     return r;
425   }
426 
427 } // class AbstractResource
428