ResourceData.java
001 /*
002  *  ResourceData.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, 1/Sept/2000
013  *
014  *  $Id: ResourceData.java 18695 2015-05-20 17:19:18Z ian_roberts $
015  */
016 
017 package gate.creole;
018 
019 import gate.CreoleRegister;
020 import gate.DocumentExporter;
021 import gate.Gate;
022 import gate.LanguageResource;
023 import gate.ProcessingResource;
024 import gate.Resource;
025 import gate.creole.metadata.Sharable;
026 import gate.util.AbstractFeatureBearer;
027 import gate.util.GateClassLoader;
028 import gate.util.GateException;
029 
030 import java.beans.BeanInfo;
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.Field;
036 import java.lang.reflect.Method;
037 import java.net.URL;
038 import java.util.Collection;
039 import java.util.Collections;
040 import java.util.HashSet;
041 import java.util.List;
042 import java.util.concurrent.CopyOnWriteArrayList;
043 
044 /** Models an individual CREOLE resource metadata, plus configuration data,
045   * plus the instantiations of the resource current within the system.
046   * Some metadata elements are used by GATE to load resources, or index
047   * the members of the CREOLE register; some are used during resource
048   * parameterisation and initialisation.
049   * Metadata elements which are used by the CREOLE registration and loading
050   * mechanisms are properties of ResourceData implementations and have their
051   * own get/set methods. Other metadata elements are made features of the
052   * ResourceData. So, for example, if you add an element "FunkyElementThaing"
053   * to the metadata of a resource, this will be made a feature of that
054   * resource's ResourceData.
055   @see CreoleRegister
056   */
057 public class ResourceData extends AbstractFeatureBearer implements Serializable
058 {
059   private static final long serialVersionUID = -1275311260404979762L;
060 
061   /** Debug flag */
062   protected static final boolean DEBUG = false;
063 
064   protected static final String DEFAULT_LR_ICON = "lr";
065   protected static final String DEFAULT_PR_ICON = "pr";
066   protected static final String DEFAULT_EXPORTER_ICON = "DocumentExporter";
067   protected static final String DEFAULT_OTHER_ICON = "application";
068   /** Construction */
069   public ResourceData() {  }// ResourceData
070   
071   private int refCount = 1;
072   
073   public int getReferenceCount() {
074     return refCount;
075   }
076   
077   public int reduceReferenceCount() {
078     return --refCount;
079   }
080   
081   public int increaseReferenceCount() {
082     return ++refCount;
083   }
084 
085   /** String representation */
086   @Override
087   public String toString() {
088     int noInst = (instantiationStack == null0: instantiationStack.size();
089 /*
090     int noSmallViews = (smallViews == null) ? 0: smallViews.size();
091     int noViews = (views == null) ? 0: views.size();
092 */
093     StringBuffer s = new StringBuffer(
094       "ResourceDataImpl, name=" + name + "; className=" + className +
095       "; jarFileName=" + jarFileName + "; jarFileUrl=" + jarFileUrl +
096       "; xmlFileName=" + xmlFileName + "; xmlFileUrl=" + xmlFileUrl +
097       "; isAutoLoading=" + autoLoading + "; numberInstances=" + noInst +
098       "; isPrivate=" + priv +"; isTool="+ tool +
099       "; validityMessage=" + validityMessage +
100       "; interfaceName=" + interfaceName +
101       "; guiType=" + guiType +
102       "; mainViewer=" + isMainView +
103       "; resourceDisplayed=" + resourceDisplayed +
104       "; annotationTypeDisplayed=" + annotationTypeDisplayed +
105       "; parameterList=" + parameterList +
106       "; features=" + features
107     );
108     return s.toString();
109   // toString
110 
111   /** Equality: two resource data objects are the same if they have the
112     * same name
113     */
114   @Override
115   public boolean equals(Object other) {
116     if (other == nullreturn false;
117     if(name.equals(((ResourceDataother).getName()))
118       return true;
119     return false;
120   // equals
121 
122   /** Hashing, based on the name field of the object */
123   @Override
124   public int hashCode() {
125     return name.hashCode();
126   // hashCode
127 
128   /** The name of the resource */
129   protected String name;
130 
131   /** Set method for the resource name */
132   public void setName(String name) { this.name = name; }
133 
134   /** Get method for the resource name */
135   public String getName() { return name; }
136 
137   /** Location of an icon for the resource */
138   protected String icon;
139 
140   /** Set method for the resource icon */
141   public void setIcon(String icon) { this.icon = icon; }
142 
143   /** Get method for the resource icon */
144   public String getIcon() {
145     //if icon not set try and guess it
146     if(icon == null){
147       icon = guessIcon();
148     }
149     return icon;
150   }
151 
152   /**
153    * Makes the best attempt of guessing an appropriate icon for this resource
154    * type based on whether it is a Language Resource, a Processing Resource, or
155    * something else.
156    @return a String representing the file name for most appropriate icon for
157    * this resource type.
158    */
159   protected String guessIcon(){
160     //if no class set we can't do any guessing
161     if(className == nullreturn DEFAULT_OTHER_ICON;
162     if(resourceClass == nullreturn DEFAULT_OTHER_ICON;
163     if(LanguageResource.class.isAssignableFrom(resourceClass))
164       return DEFAULT_LR_ICON;
165     if(ProcessingResource.class.isAssignableFrom(resourceClass))
166       return DEFAULT_PR_ICON;
167     if (DocumentExporter.class.isAssignableFrom(resourceClass))
168       return DEFAULT_EXPORTER_ICON;
169     
170     return DEFAULT_OTHER_ICON;
171   }
172 
173   /** The stack of instantiations */
174   protected List<Resource> instantiationStack = new CopyOnWriteArrayList<Resource>();
175 
176   /**
177    * Unmodifiable view of the instantiation stack, returned by
178    * getInstantiations to ensure that the only way to modify the list is
179    * through the add/removeInstantiation methods of this class.
180    */
181   protected List<Resource> unmodifiableInstantiationStack =
182           Collections.unmodifiableList(instantiationStack);
183 
184   /** Get the list of instantiations of resources */
185   public List<Resource> getInstantiations() {
186     return unmodifiableInstantiationStack;
187   // getInstantiations
188 
189   /** Add an instantiation of the resource to the register of these */
190   public void addInstantiation(Resource resource) {
191     instantiationStack.add(0, resource);
192   // addInstantiation
193 
194   /**
195    * Remove an instantiation of the resource from the register of these.
196    @return true if the given instance was contained in the register,
197    *         false otherwise (i.e. the instance had already been removed).
198    */
199   public boolean removeInstantiation(Resource resource) {
200     return instantiationStack.remove(resource);
201     //persistantInstantiationList.remove(resource);
202   // removeInstantiation
203 
204   /** The class name of the resource */
205   protected String className;
206 
207   /** Set method for the resource class name */
208   public void setClassName(String className) { this.className = className; }
209 
210   /** Get method for the resource class name */
211   public String getClassName() { return className; }
212 
213   /** The interface name of the resource */
214   protected String interfaceName;
215 
216   /** Set method for the resource interface name */
217   public void setInterfaceName(String interfaceName) {
218     this.interfaceName = interfaceName;
219   // setInterfaceName
220 
221   /** Get method for the resource interface name */
222   public String getInterfaceName() { return interfaceName; }
223 
224   /** The class of the resource */
225   protected Class<? extends Resource> resourceClass;
226 
227   /** Set method for the resource class */
228   public void setResourceClass(Class<? extends Resource> resourceClass) {
229     this.resourceClass = resourceClass;
230   // setResourceClass
231 
232   /** Get method for the resource class. Asks the GATE class loader
233     * to load it, if it is not already present.
234     */
235   public Class<? extends Resource> getResourceClass() throws ClassNotFoundException {
236     if(resourceClass == null) {
237       GateClassLoader classLoader = Gate.getClassLoader().getDisposableClassLoader(xmlFileUrl.toExternalForm());
238       resourceClass = classLoader.loadClass(className).asSubclass(Resource.class);
239     }
240 
241     return resourceClass;
242   // getResourceClass
243 
244   /** The jar file name of the resource */
245   protected String jarFileName;
246 
247   /** Set method for the resource jar file name */
248   public void setJarFileName(String jarFileName) {
249     this.jarFileName = jarFileName;
250   // setJarFileName
251 
252   /** Get method for the resource jar file name */
253   public String getJarFileName() { return jarFileName; }
254 
255   /** The jar file URL of the resource */
256   protected URL jarFileUrl;
257 
258   /** Set method for the resource jar file URL */
259   public void setJarFileUrl(URL jarFileUrl) { this.jarFileUrl = jarFileUrl; }
260 
261   /** Get method for the resource jar file URL */
262   public URL getJarFileUrl() { return jarFileUrl; }
263 
264   /** The xml file name of the resource */
265   protected String xmlFileName;
266 
267   /** The xml file URL of the resource */
268   protected URL xmlFileUrl;
269 
270   /** Set the URL to the creole.xml file that defines this resource */
271   public void setXmlFileUrl(URL xmlFileUrl) { this.xmlFileUrl = xmlFileUrl; }
272 
273   /** Get the URL to the creole.xml file that defines this resource */
274   public URL getXmlFileUrl() { return xmlFileUrl; }
275 
276   /** The comment string */
277   protected String comment;
278 
279   /** Get method for the resource comment */
280   public String getComment() { return comment; }
281 
282   /** Set method for the resource comment */
283   public void setComment(String comment) { this.comment = comment; }
284 
285   /** The helpURL string */
286   protected String helpURL;
287 
288   /** Get method for the resource helpURL */
289   public String getHelpURL() { return helpURL; }
290 
291   /** Set method for the resource helpURL */
292   public void setHelpURL(String helpURL) { this.helpURL = helpURL; }
293 
294   /** The set of parameter lists */
295   protected ParameterList parameterList = new ParameterList();
296 
297   /** Set the parameter list */
298   public void setParameterList(ParameterList parameterList) {
299     this.parameterList = parameterList;
300   // addParameterList
301 
302   /** Get the parameter list */
303   public ParameterList getParameterList() { return parameterList; }
304 
305   /** Autoloading flag */
306   protected boolean autoLoading;
307 
308   /** Set method for resource autoloading flag */
309   public void setAutoLoading(boolean autoLoading) {
310     this.autoLoading = autoLoading;
311   // setAutoLoading
312 
313   /** Is the resource autoloading? */
314   public boolean isAutoLoading() { return autoLoading; }
315 
316   /** Private flag */
317   protected boolean priv = false;
318 
319   /** Set method for resource private flag */
320   public void setPrivate(boolean priv) {
321     this.priv = priv;
322   // setPrivate
323 
324   /** Is the resource private? */
325   public boolean isPrivate() { return priv; }
326 
327   /** Tool flag */
328   protected boolean tool = false;
329 
330   /** Set method for resource tool flag */
331   public void setTool(boolean tool) {
332     this.tool = tool;
333   // setTool
334 
335   /** Is the resource a tool? */
336   public boolean isTool() { return tool; }
337   /** Is this a valid resource data configuration? If not, leave an
338     * error message that can be returned by <TT>getValidityMessage()</TT>.
339     */
340   public boolean isValid() {
341     boolean valid = true;
342     validityMessage = "";
343 //******************************
344 // here should check that the resource has all mandatory elements,
345 // e.g. class name, and non-presence of runtime params on LRs and VRs etc.
346 //******************************
347     if(getClassName() == null || getClassName().length() == 0){
348       validityMessage += "No class name provided for the resource!";
349       valid = false;
350     }
351     if(getName() == null || getName().length() == 0){
352       //no name provided.
353       setName(className.substring(className.lastIndexOf('.'1));
354     }
355     return valid;
356   // isValid()
357 
358   /** Status message set by isValid() */
359   protected String validityMessage = "";
360 
361   /** Get validity statues message. */
362   public String getValidityMessage() { return validityMessage; }
363 
364   /////////////////////////////////////////////////////
365   // Fields added for GUI element
366   /////////////////////////////////////////////////////
367   /** This type indicates that the resource is not a GUI */
368   public static final int NULL_GUI = 0;
369   /**This type indicates that the resource goes into the large area of GATE GUI*/
370   public static final int LARGE_GUI = 1;
371   /**This type indicates that the resource goes into the small area of GATE GUI*/
372   public static final int SMALL_GUI = 2;
373   /** A filed which can have one of the 3 predefined values. See above.*/
374   protected int guiType = NULL_GUI;
375   /** Whether or not this viewer will be the default one*/
376   protected boolean isMainView = false;
377   /** The full class name of the resource displayed by this viewer.*/
378   protected String resourceDisplayed = null;
379   /** The full type name of the annotation displayed by this viewer.*/
380   protected String annotationTypeDisplayed = null;
381   /** A simple mutator for guiType field*/
382   public void setGuiType(int aGuiType){guiType = aGuiType;}
383   /** A simple accessor for guiType field*/
384   public int getGuiType(){return guiType;}
385   /** A simple mutator for isMainView field*/
386   public void setIsMainView(boolean mainView){isMainView = mainView;}
387   /** A simple accessor for isMainView field*/
388   public boolean isMainView(){return isMainView;}
389   /** A simple mutator for resourceDisplayed field*/
390   public void setResourceDisplayed(String aResourceDisplayed){
391     resourceDisplayed = aResourceDisplayed;
392   }// setResourceDisplayed
393   /** A simple accessor for resourceDisplayed field*/
394   public String getResourceDisplayed(){return resourceDisplayed;}
395   /** A simple mutator for annotationTypeDisplayed field*/
396   public void setAnnotationTypeDisplayed(String anAnnotationTypeDisplayed){
397     annotationTypeDisplayed = anAnnotationTypeDisplayed;
398   }// setAnnotationTypeDisplayed
399   /** A simple accessor for annotationTypeDisplayed field*/
400   public String getAnnotationTypeDisplayed(){return annotationTypeDisplayed;}
401 
402   // Sharable properties for duplication
403   protected Collection<String> sharableProperties = new HashSet<String>();
404 
405   /**
406    * Get the collection of property names that should be treated as
407    * sharable state when duplicating resources of this type.  The
408    * specified properties may also be declared as parameters of the
409    * resource but this is not required.
410    */
411   public Collection<String> getSharableProperties() {
412     return sharableProperties;
413   }
414 
415   /**
416    * Initialize this ResourceData.  Called by CreoleXmlHandler once all
417    * the properties of this ResourceData specified in the XML have been
418    * filled in, but before autoinstances are created.
419    */
420   public void init() throws Exception {
421     determineSharableProperties(getResourceClass()new HashSet<String>());
422   }
423 
424   private void determineSharableProperties(Class<?> cls, Collection<String> hiddenPropertyNamesthrows GateException {
425     BeanInfo bi;
426     try {
427       bi = Introspector.getBeanInfo(cls);
428     catch(IntrospectionException e) {
429       throw new GateException("Failed to introspect " + cls, e);
430     }
431 
432     // setter methods
433     for(Method m : cls.getDeclaredMethods()) {
434       Sharable sharableAnnot = m.getAnnotation(Sharable.class);
435       if(sharableAnnot != null) {
436         // determine the property name from the method name
437         PropertyDescriptor propDescriptor = null;
438         for(PropertyDescriptor pd : bi.getPropertyDescriptors()) {
439           if(m.equals(pd.getWriteMethod())) {
440             propDescriptor = pd;
441             break;
442           }
443         }
444 
445         if(propDescriptor == null) {
446           throw new GateException("@Sharable annotation found on "
447                   + m
448                   ", but only Java Bean property setters may have "
449                   "this annotation.");
450         }
451         if(propDescriptor.getReadMethod() == null) {
452           throw new GateException("@Sharable annotation found on "
453                   + m
454                   ", but no matching getter was found.");
455         }
456         String propName = propDescriptor.getName();
457         if(sharableAnnot.value() == false) {
458           // hide this property name from the search in superclasses
459           hiddenPropertyNames.add(propName);
460         }
461         else {
462           // this property is sharable if it has not been hidden in a subclass
463           if(!hiddenPropertyNames.contains(propName)) {
464             sharableProperties.add(propName);
465           }
466         }
467       }
468     }
469     
470     // fields
471     for(Field f : cls.getDeclaredFields()) {
472       Sharable sharableAnnot = f.getAnnotation(Sharable.class);
473       if(sharableAnnot != null) {
474         String propName = f.getName();
475         // check it's a valid Java Bean property
476         PropertyDescriptor propDescriptor = null;
477         for(PropertyDescriptor pd : bi.getPropertyDescriptors()) {
478           if(propName.equals(pd.getName())) {
479             propDescriptor = pd;
480             break;
481           }
482         }
483         if(propDescriptor == null || propDescriptor.getReadMethod() == null ||
484                 propDescriptor.getWriteMethod() == null) {
485           throw new GateException("@Sharable annotation found on "
486                   + f
487                   " without matching Java Bean getter and setter.");
488         }
489         if(sharableAnnot.value() == false) {
490           // hide this property name from the search in superclasses
491           hiddenPropertyNames.add(propName);
492         }
493         else {
494           // this property is sharable if it has not been hidden in a subclass
495           if(!hiddenPropertyNames.contains(propName)) {
496             sharableProperties.add(propName);
497           }
498         }
499       }
500     }
501 
502     // go up the class tree
503     if(cls.getSuperclass() != null) {
504       determineSharableProperties(cls.getSuperclass(), hiddenPropertyNames);
505     }
506     for(Class<?> intf : cls.getInterfaces()) {
507       determineSharableProperties(intf, hiddenPropertyNames);
508     }
509   }
510 // ResourceData