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