CreoleRegisterImpl.java
0001 /*
0002  *  CreoleRegisterImpl.java
0003  *
0004  *  Copyright (c) 1995-2013, The University of Sheffield. See the file
0005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0006  *
0007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0008  *  software, licenced under the GNU Library General Public License,
0009  *  Version 2, June 1991 (in the distribution as file licence.html,
0010  *  and also available at http://gate.ac.uk/gate/licence.html).
0011  *
0012  *  Hamish Cunningham, 1/Sept/2000
0013  *
0014  *  $Id: CreoleRegisterImpl.java 18729 2015-05-29 16:10:29Z markagreenwood $
0015  */
0016 
0017 package gate.creole;
0018 
0019 import gate.Controller;
0020 import gate.CreoleRegister;
0021 import gate.Factory;
0022 import gate.Gate;
0023 import gate.Gate.DirectoryInfo;
0024 import gate.Gate.ResourceInfo;
0025 import gate.LanguageResource;
0026 import gate.ProcessingResource;
0027 import gate.Resource;
0028 import gate.VisualResource;
0029 import gate.event.CreoleEvent;
0030 import gate.event.CreoleListener;
0031 import gate.event.PluginListener;
0032 import gate.util.CreoleXmlUpperCaseFilter;
0033 import gate.util.Err;
0034 import gate.util.GateClassLoader;
0035 import gate.util.GateException;
0036 import gate.util.GateRuntimeException;
0037 import gate.util.LuckyException;
0038 import gate.util.Out;
0039 
0040 import java.io.IOException;
0041 import java.io.InputStream;
0042 import java.net.MalformedURLException;
0043 import java.net.URL;
0044 import java.util.AbstractList;
0045 import java.util.ArrayList;
0046 import java.util.Collection;
0047 import java.util.Collections;
0048 import java.util.HashMap;
0049 import java.util.HashSet;
0050 import java.util.Iterator;
0051 import java.util.LinkedHashSet;
0052 import java.util.LinkedList;
0053 import java.util.List;
0054 import java.util.Map;
0055 import java.util.Set;
0056 import java.util.Vector;
0057 import java.util.concurrent.CopyOnWriteArrayList;
0058 
0059 import org.apache.log4j.Logger;
0060 import org.jdom.Document;
0061 import org.jdom.Element;
0062 import org.jdom.JDOMException;
0063 import org.jdom.input.SAXBuilder;
0064 import org.jdom.output.Format;
0065 import org.jdom.output.SAXOutputter;
0066 import org.jdom.output.XMLOutputter;
0067 import org.xml.sax.helpers.DefaultHandler;
0068 
0069 /**
0070  * This class implements the CREOLE register interface. DO NOT construct objects
0071  * of this class unless your name is gate.Gate (in which case please go back to
0072  * the source code repository and stop looking at other class's code).
0073  <P>
0074  * The CREOLE register records the set of resources that are currently known to
0075  * the system. Each member of the register is a {@link gate.creole.ResourceData}
0076  * object, indexed by the class name of the resource.
0077  *
0078  @see gate.CreoleRegister
0079  */
0080 @SuppressWarnings("serial")
0081 public class CreoleRegisterImpl extends HashMap<String, ResourceData>
0082                                                                      implements
0083                                                                      CreoleRegister {
0084 
0085   /** A logger to use instead of sending messages to Out or Err **/
0086   protected static final Logger log = Logger.getLogger(CreoleRegisterImpl.class);
0087 
0088   /** Debug flag */
0089   protected static final boolean DEBUG = false;
0090 
0091   /** The set of CREOLE directories (URLs). */
0092   protected Set<URL> directories;
0093 
0094   /** The parser for the CREOLE directory files */
0095   protected transient SAXBuilder jdomBuilder = null;
0096 
0097   /**
0098    * Name of the plugin-mappings file
0099    */
0100   public static final String PLUGIN_NAMES_MAPPING_FILE = "plugin-mappings.xml";
0101 
0102   /**
0103    * maps previous plugin names to new plugin names
0104    */
0105   protected Map<String, String> pluginNamesMappings = null;
0106 
0107   /**
0108    * Default constructor. Sets up directory files parser. <B>NOTE:</B> only
0109    * Factory should call this method.
0110    */
0111   public CreoleRegisterImpl() throws GateException {
0112 
0113     // initialise the various maps
0114     directories = new LinkedHashSet<URL>();
0115     lrTypes = new HashSet<String>();
0116     prTypes = new HashSet<String>();
0117     vrTypes = new LinkedList<String>();
0118     toolTypes = new HashSet<String>();
0119     applicationTypes = new HashSet<String>();
0120 
0121     // construct a SAX parser for parsing the CREOLE directory files
0122     jdomBuilder = new SAXBuilder(false);
0123     jdomBuilder.setXMLFilter(new CreoleXmlUpperCaseFilter());
0124 
0125     // read plugin name mappings file
0126     readPluginNamesMappings();
0127 
0128   // default constructor
0129 
0130   /**
0131    * reads plugins-mapping.xml file which is used for mapping old plugin
0132    * names to new plugin names
0133    */
0134   private void readPluginNamesMappings() {
0135     // should load it only once
0136     if(pluginNamesMappings != nullreturn;
0137 
0138     pluginNamesMappings = new HashMap<String, String>();
0139 
0140     // use jdom
0141     SAXBuilder builder = new SAXBuilder();
0142 
0143     // command line should offer URIs or file names
0144     try {
0145       URL creoleDirURL = Gate.getBuiltinCreoleDir();
0146       URL pluginMappingsFileURL =
0147         new URL(creoleDirURL, PLUGIN_NAMES_MAPPING_FILE);
0148       Document document = builder.build(pluginMappingsFileURL);
0149       @SuppressWarnings("unchecked")
0150       List<Element> plugins = document.getRootElement().getChildren("Plugin");
0151       if(plugins != null) {
0152         for(Element aPlugin : plugins) {
0153           String oldName = aPlugin.getChildText("OldName");
0154           String newName = aPlugin.getChildText("NewName");
0155           pluginNamesMappings.put(oldName, newName);
0156         }
0157       }
0158     }
0159     // indicates a well-formedness error
0160     catch(JDOMException e) {
0161       log.warn(PLUGIN_NAMES_MAPPING_FILE + " is not well-formed.", e);
0162     }
0163     catch(IOException e) {
0164       log.warn("Could not check " + PLUGIN_NAMES_MAPPING_FILE,e);
0165     }
0166   }
0167 
0168   /** Get the list of CREOLE directory URLs. */
0169   @Override
0170   public Set<URL> getDirectories() {
0171     return Collections.unmodifiableSet(directories);
0172   // getDirectories
0173 
0174   @Override
0175   public void registerComponent(Class<? extends Resource> resourceClassthrows GateException {
0176     URL creoleFileUrl = resourceClass.getResource("/gate/creole/CreoleRegisterImpl.class");
0177     Gate.addKnownPlugin(creoleFileUrl);
0178     Document doc = new Document();
0179     Element element;
0180     doc.addContent(element = new Element("CREOLE-DIRECTORY"));
0181     element.addContent(element = new Element("CREOLE"));
0182     element.addContent(element = new Element("RESOURCE"));
0183     Element classElement  = new Element("CLASS");
0184     classElement.setText(resourceClass.getName());
0185     element.addContent(classElement);
0186     CreoleAnnotationHandler annotationHandler = new CreoleAnnotationHandler(creoleFileUrl);
0187     annotationHandler.processCreoleResourceAnnotations(element, resourceClass);
0188     try {
0189       processFullCreoleXmlTree(new URL(creoleFileUrl, "."), creoleFileUrl, doc, annotationHandler);
0190     }
0191     catch(IOException e) {
0192       throw new GateException(e);
0193     }
0194     catch(JDOMException e) {
0195       throw new GateException(e);
0196     }
0197   }
0198 
0199   /**
0200    * Register a single CREOLE directory. The <CODE>creole.xml</CODE> file at the
0201    * URL is parsed, and <CODE>CreoleData</CODE> objects added to the register.
0202    * If the directory URL has not yet been added it is now added. If any other
0203    * plugins that nees top be loaded for this plugin to load (specified by
0204    <CODE>REQUIRES</Code> elements in <code>creole.xml</code>) will only be
0205    * loaded if the <code>loadDependencies</code> param is true. It is useful to
0206    * be able to ignore dependencies when loading an xgapp where we know they
0207    * have already been resolved.
0208    */
0209   @Override
0210   public void registerDirectories(URL directoryUrl, boolean loadDependencies)
0211       throws GateException {
0212     
0213  // directory URLs shouldn't include "creole.xml"
0214     String urlName = directoryUrl.toExternalForm();
0215     if(urlName.toLowerCase().endsWith("creole.xml")) { throw new GateException(
0216       "CREOLE directory URLs should point to the parent location of "
0217         "the creole.xml file, not the file itself; bad URL was: " + urlName)}
0218     // CREOLE URLs are directory URLs so they should end with "/"
0219     String separator = "/";
0220     if(!urlName.endsWith(separator)) {
0221       urlName += separator;
0222       try {
0223         directoryUrl = new URL(urlName);
0224       }
0225       catch(MalformedURLException mue) {
0226         throw new GateRuntimeException(mue);
0227       }
0228     }
0229     // normalise to remove any /../ path components
0230     try {
0231       directoryUrl = new URL(directoryUrl, ".");
0232       urlName = directoryUrl.toExternalForm();
0233     }
0234     catch(MalformedURLException mue) {
0235       throw new GateRuntimeException(mue);
0236     }
0237     // create a URL for the creole.xml file, based on the directory URL
0238     URL directoryXmlFileUrl = directoryUrl;
0239     try {
0240       directoryXmlFileUrl = new URL(urlName + "creole.xml");
0241     }
0242     catch(MalformedURLException e) {
0243       throw (new GateException("bad creole.xml URL, based on " + urlName));
0244     }
0245 
0246     // creole stream
0247     InputStream creoleStream = null;
0248 
0249     // lets check if it is an old plugin name
0250     try {
0251       creoleStream = directoryXmlFileUrl.openStream();
0252     }
0253     catch(IOException ioe) {
0254 
0255       // if this happens
0256       // locating last separator
0257       int indexOfSeparator = urlName.lastIndexOf('/', urlName.length() 2);
0258 
0259       // plugin name
0260       String pluginName =
0261         urlName.substring(indexOfSeparator + 1, urlName.length() 1);
0262 
0263       if(pluginNamesMappings != null && pluginNamesMappings.containsKey(pluginName)) {
0264         String newPluginName = pluginNamesMappings.get(pluginName);
0265 
0266         urlName =
0267           urlName.substring(0, indexOfSeparator + 1+ newPluginName + "/";
0268 
0269         try {
0270           log.warn("Trying to use new plugin name for " + pluginName);
0271           directoryUrl = new URL(urlName);
0272           directoryXmlFileUrl = new URL(directoryUrl, "creole.xml");
0273           creoleStream = directoryXmlFileUrl.openStream();
0274           log.warn("Please note that plugin names have changed. "
0275             "Please correct your application to rename " + pluginName
0276             " to " + newPluginName);
0277         }
0278         catch(IOException e) {
0279           throw new GateException("couldn't open creole.xml: " + e.toString());
0280         }
0281       else {
0282         throw new GateException("couldn't find:" + directoryUrl);
0283       }
0284     }
0285 
0286     // if already present do nothing
0287     if(!directories.contains(directoryUrl)) {
0288       // add it to the list of known directories
0289       Gate.addKnownPlugin(directoryUrl);
0290       
0291       // parse the directory file
0292       try {
0293         parseDirectory(creoleStream, directoryUrl,
0294           directoryXmlFileUrl, loadDependencies);
0295         log.info("CREOLE plugin loaded: " + urlName);
0296       }
0297       catch(Throwable e) {
0298         // it failed:
0299         throw (new GateException("couldn't open creole.xml",e));
0300       }
0301       
0302       directories.add(directoryUrl);
0303       
0304       firePluginLoaded(directoryUrl);
0305     }
0306   }
0307   
0308   /** Register a single CREOLE directory. The <CODE>creole.xml</CODE>
0309    * file at the URL is parsed, and <CODE>CreoleData</CODE> objects added
0310    * to the register. If the directory URL has not yet been added it
0311    * is now added. If any other plugins that nees top be loaded for this
0312    * plugin to load (specified by <CODE>REQUIRES</Code> elements in
0313    <code>creole.xml</code>) will also be loaded.
0314    */
0315   @Override
0316   public void registerDirectories(URL directoryUrlthrows GateException {
0317     registerDirectories(directoryUrl, true);    
0318   }
0319 
0320   /**
0321    * Parse a directory file (represented as an open stream), adding resource
0322    * data objects to the CREOLE register as they occur. If the resource is from
0323    * a URL then that location is passed (otherwise null).
0324    */
0325   protected void parseDirectory(InputStream directoryStream, URL directoryUrl,
0326     URL creoleFileUrl, boolean loadDependenciesthrows GateException {
0327     // create a handler for the directory file and parse it;
0328     // this will create ResourceData entries in the register
0329     try {
0330       Document jdomDoc =
0331         jdomBuilder.build(directoryStream, creoleFileUrl.toExternalForm());
0332       CreoleAnnotationHandler annotationHandler =
0333         new CreoleAnnotationHandler(creoleFileUrl);
0334 
0335       if (loadDependencies) {
0336         annotationHandler.loadRequiredPlugins(jdomDoc);
0337       }
0338       
0339       GateClassLoader gcl = Gate.getClassLoader().getDisposableClassLoader(creoleFileUrl.toExternalForm());
0340 
0341       // Add any JARs from the creole.xml to the GATE ClassLoader
0342       annotationHandler.addJarsToClassLoader(gcl,jdomDoc);
0343 
0344       // Make sure there is a RESOURCE element for every resource type the
0345       // directory defines
0346       annotationHandler.createResourceElementsForDirInfo(jdomDoc);
0347 
0348       processFullCreoleXmlTree(directoryUrl, creoleFileUrl, jdomDoc, annotationHandler);
0349     }
0350     catch(IOException e) {
0351       throw (new GateException(e));
0352     }
0353     catch(JDOMException je) {
0354       if(DEBUGje.printStackTrace(Err.getPrintWriter());
0355       throw (new GateException(je));
0356     }
0357 
0358   // parseDirectory
0359 
0360   private void processFullCreoleXmlTree(URL directoryUrl, URL creoleFileUrl,
0361           Document jdomDoc, CreoleAnnotationHandler annotationHandler)
0362           throws GateException, IOException, JDOMException {
0363     // now we can process any annotations on the new classes
0364     // and augment the XML definition
0365     annotationHandler.processAnnotations(jdomDoc);
0366 
0367     // debugging
0368     if(DEBUG) {
0369       XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
0370       xmlOut.output(jdomDoc, System.out);
0371     }
0372 
0373     // finally, parse the augmented definition with the normal parser
0374     DefaultHandler handler =
0375       new CreoleXmlHandler(this, directoryUrl, creoleFileUrl);
0376     SAXOutputter outputter =
0377       new SAXOutputter(handler, handler, handler, handler);
0378     outputter.output(jdomDoc);
0379     if(DEBUG) {
0380       Out.prln("done parsing "
0381         ((directoryUrl == null"null" : directoryUrl.toString()));
0382     }
0383   }
0384 
0385   /**
0386    * Register resources that are built in to the GATE distribution. These
0387    * resources are described by the <TT>creole.xml</TT> file in
0388    <TT>resources/creole</TT>.
0389    */
0390   @Override
0391   public void registerBuiltins() throws GateException {
0392 
0393     try {
0394       URL creoleDirURL = Gate.getBuiltinCreoleDir();
0395       URL creoleFileURL = new URL(creoleDirURL, "creole.xml");
0396       // URL creoleFileURL = Files.getGateResource("/creole/creole.xml");
0397       parseDirectory(creoleFileURL.openStream(), creoleDirURL, creoleFileURL,true);
0398     }
0399     catch(IOException e) {
0400       if(DEBUGlog.debug(e);
0401       throw (new GateException(e));
0402     }
0403   // registerBuiltins()
0404 
0405   /**
0406    * Overide HashMap's put method to maintain a list of all the types of LR in
0407    * the register, and a list of tool types. The key is the resource type, the
0408    * value its data.
0409    */
0410   @Override
0411   public ResourceData put(String key, ResourceData rd) {
0412 
0413     if(super.containsKey(key)) {      
0414       ResourceData rData = super.get(key);
0415       rData.increaseReferenceCount();
0416       if(DEBUG)
0417         Out.println(key + " is already defined, new reference will be ignored.");
0418 
0419       // TODO not sure what we should actually return here
0420       return rData;
0421     }
0422     
0423     // get the resource implementation class
0424     Class<? extends Resource> resClass = null;
0425     try {
0426       resClass = rd.getResourceClass();
0427     }
0428     catch(ClassNotFoundException e) {
0429       throw new GateRuntimeException(
0430         "Couldn't get resource class from the resource data:" + e);
0431     }
0432 
0433     // add class names to the type lists
0434     if(LanguageResource.class.isAssignableFrom(resClass)) {
0435       if(DEBUGOut.prln("LR: " + resClass);
0436       if(lrTypes == nulllrTypes = new HashSet<String>()// for
0437       // deserialisation
0438       lrTypes.add(rd.getClassName());
0439     }
0440     if(ProcessingResource.class.isAssignableFrom(resClass)) {
0441       if(DEBUG) {
0442         Out.prln("PR: " + resClass);
0443         // Out.prln("prTypes: " + prTypes);
0444         // Out.prln("rd.getClassName(): " + rd.getClassName());
0445       }
0446       if(prTypes == nullprTypes = new HashSet<String>()// for
0447       // deserialisation
0448       prTypes.add(rd.getClassName());
0449     }
0450     if(VisualResource.class.isAssignableFrom(resClass)) {
0451       if(DEBUGOut.prln("VR: " + resClass);
0452       if(vrTypes == nullvrTypes = new LinkedList<String>()// for
0453       // deserialisation
0454       // we have to simulate Set behaviour as this is a list
0455       if(!vrTypes.contains(rd.getClassName())) vrTypes.add(rd.getClassName());
0456     }
0457     if(Controller.class.isAssignableFrom(resClass)) {
0458       if(DEBUGOut.prln("Controller: " + resClass);
0459       if(controllerTypes == nullcontrollerTypes = new HashSet<String>()// for
0460       // deserialisation
0461       controllerTypes.add(rd.getClassName());
0462     }
0463     if (PackagedController.class.isAssignableFrom(resClass)) {
0464       if(DEBUGOut.prln("Application: " + resClass);
0465       if (applicationTypes == nullapplicationTypes = new HashSet<String>();
0466       applicationTypes.add(rd.getClassName());
0467     }
0468 
0469     // maintain tool types list
0470     if(rd.isTool()) {
0471       if(toolTypes == nulltoolTypes = new HashSet<String>()// for
0472       // deserialisation
0473       toolTypes.add(rd.getClassName());
0474     }
0475 
0476     return super.put(key, rd);
0477   // put(key, value)
0478 
0479   /**
0480    * Removes a CREOLE directory from the set of loaded directories.
0481    *
0482    @param directory
0483    */
0484   @Override
0485   public void removeDirectory(URL directory) {
0486     int prCount = 0;
0487     
0488     if(directories.remove(directory)) {
0489       DirectoryInfo dInfo = Gate.getDirectoryInfo(directory);
0490       if(dInfo != null) {
0491         
0492         for(ResourceInfo rInfo : dInfo.getResourceInfoList()) {
0493           ResourceData rData = get(rInfo.getResourceClassName());          
0494           if (rData != null && rData.getReferenceCount() == 1) {
0495             // we only need to remove resources if we are actually going to
0496             // remove the plugin
0497             try {
0498               List<Resource> loaded =
0499                   getAllInstances(rInfo.getResourceClassName(),true);
0500               prCount += loaded.size();
0501               for(Resource r : loaded) {
0502                 //System.out.println(r);
0503                 Factory.deleteResource(r);  
0504               }
0505             catch(GateException e) {
0506               // not much we can do here other than dump the exception
0507               e.printStackTrace();
0508             }
0509           }
0510           
0511           remove(rInfo.getResourceClassName());
0512         }
0513       }
0514       try {
0515         Gate.getClassLoader().forgetClassLoader(new URL(directory,"creole.xml").toExternalForm(), dInfo);
0516       }
0517       catch (Exception e) {
0518         e.printStackTrace();
0519       }
0520 
0521       log.info("CREOLE plugin unloaded: " + directory);
0522       if (prCount > 0)
0523         log.warn(prCount+" resources were deleted as they relied on the " + dInfo.getName() +" plugin");
0524       
0525       firePluginUnloaded(directory);
0526     }
0527   }
0528 
0529   /**
0530    * Overide HashMap's delete method to update the lists of types in the
0531    * register.
0532    */
0533   @Override
0534   public ResourceData remove(Object key) {
0535     ResourceData rd = get(key);
0536     if(rd == nullreturn null;
0537     
0538     // TODO not sure what we should actually return here
0539     if(rd.reduceReferenceCount() 0) {
0540       if(DEBUG)
0541         Out.println(key
0542             " is still defined by another plugin so won't be unloaded");
0543       return rd;
0544     }
0545     
0546     if(DEBUG) {
0547       Out.prln(key);
0548       Out.prln(rd);
0549     }
0550     
0551     try {
0552       if(LanguageResource.class.isAssignableFrom(rd.getResourceClass())) {
0553         lrTypes.remove(rd.getClassName());
0554       }
0555       else if(ProcessingResource.class.isAssignableFrom(rd.getResourceClass())) {
0556         prTypes.remove(rd.getClassName());
0557       }
0558       else if(VisualResource.class.isAssignableFrom(rd.getResourceClass())) {
0559         vrTypes.remove(rd.getClassName());
0560       }
0561       else if(Controller.class.isAssignableFrom(rd.getResourceClass())) {
0562         controllerTypes.remove(rd.getClassName());
0563       }
0564       else if (PackagedController.class.isAssignableFrom(rd.getResourceClass())) {
0565         applicationTypes.remove(rd.getClassName());
0566       }
0567     }
0568     catch(ClassNotFoundException cnfe) {
0569       throw new GateRuntimeException(
0570         "Could not load class specified in CREOLE data.", cnfe);
0571     }
0572     // maintain tool types list
0573     if(rd.isTool()) toolTypes.remove(rd.getClassName());
0574 
0575     return super.remove(key);
0576   // remove(Object)
0577 
0578   /**
0579    * Overide HashMap's clear to update the list of LR types in the register, and
0580    * remove all resources and forgets all directories.
0581    */
0582   @Override
0583   public void clear() {
0584     lrTypes.clear();
0585     prTypes.clear();
0586     vrTypes.clear();
0587     toolTypes.clear();
0588     directories.clear();
0589     applicationTypes.clear();
0590     super.clear();
0591   // clear()
0592 
0593   /** Get the list of types of LR in the register. */
0594   @Override
0595   public Set<String> getLrTypes() {
0596     return Collections.unmodifiableSet(lrTypes);
0597   }
0598 
0599   /** Get the list of types of PR in the register. */
0600   @Override
0601   public Set<String> getPrTypes() {
0602     return Collections.unmodifiableSet(prTypes);
0603   }
0604 
0605   /** Get the list of types of VR in the register. */
0606   @Override
0607   public Set<String> getVrTypes() {
0608     return Collections.unmodifiableSet(new HashSet<String>(vrTypes));
0609   }
0610 
0611   /** Get the list of types of Controller in the register. */
0612   @Override
0613   public Set<String> getControllerTypes() {
0614     return Collections.unmodifiableSet(controllerTypes);
0615   }
0616 
0617   /** Get the list of types of TOOL resources in the register. */
0618   @Override
0619   public Set<String> getToolTypes() {
0620     return Collections.unmodifiableSet(toolTypes);
0621   }
0622 
0623   /** Get the list of types of packaged application resources in the register. */
0624   @Override
0625   public Set<String>getApplicationTypes() {
0626     return Collections.unmodifiableSet(applicationTypes);
0627   }
0628 
0629   /** Get a list of all instantiations of LR in the register. */
0630   @Override
0631   public List<LanguageResource> getLrInstances() {
0632     Set<String> lrTypeSet = getLrTypes();
0633     List<LanguageResource> instances = new ArrayList<LanguageResource>();
0634 
0635     Iterator<String> iter = lrTypeSet.iterator();
0636     while(iter.hasNext()) {
0637       String type = iter.next();
0638       instances.addAll(getLrInstances(type));
0639     }// End while
0640     return Collections.unmodifiableList(instances);
0641   // getLrInstances()
0642 
0643   /** Get a list of all instantiations of PR in the register. */
0644   @Override
0645   public List<ProcessingResource> getPrInstances() {
0646     Set<String> prTypeSet = getPrTypes();
0647     List<ProcessingResource> instances = new ArrayList<ProcessingResource>();
0648 
0649     Iterator<String> iter = prTypeSet.iterator();
0650     while(iter.hasNext()) {
0651       String type = iter.next();
0652       instances.addAll(getPrInstances(type));
0653     }// End while
0654 
0655     return Collections.unmodifiableList(instances);
0656   // getPrInstances()
0657 
0658   /** Get a list of all instantiations of VR in the register. */
0659   @Override
0660   public List<VisualResource> getVrInstances() {
0661     Set<String> vrTypeSet = getVrTypes();
0662     List<VisualResource> instances = new ArrayList<VisualResource>();
0663 
0664     Iterator<String> iter = vrTypeSet.iterator();
0665     while(iter.hasNext()) {
0666       String type = iter.next();
0667       instances.addAll(getVrInstances(type));
0668     }// End while
0669 
0670     return Collections.unmodifiableList(instances);
0671   // getVrInstances()
0672 
0673   @Override
0674   public List<LanguageResource> getLrInstances(String resourceTypeName) {
0675     ResourceData resData = get(resourceTypeName);
0676     if(resData == nullreturn Collections.emptyList();
0677 
0678     return new TypedResourceList<LanguageResource>(resData.getInstantiations(),
0679       LanguageResource.class);
0680   // getLrInstances
0681 
0682   @Override
0683   public List<ProcessingResource> getPrInstances(String resourceTypeName) {
0684     ResourceData resData = get(resourceTypeName);
0685     if(resData == nullreturn Collections.emptyList();
0686 
0687     return new TypedResourceList<ProcessingResource>(resData
0688       .getInstantiations(), ProcessingResource.class);
0689   // getPrInstances
0690 
0691   @Override
0692   public List<VisualResource> getVrInstances(String resourceTypeName) {
0693     ResourceData resData = get(resourceTypeName);
0694     if(resData == nullreturn Collections.emptyList();
0695 
0696     return new TypedResourceList<VisualResource>(resData.getInstantiations(),
0697       VisualResource.class);
0698   // getVrInstances
0699 
0700   /** Get a list of all non-private instantiations of LR in the register. */
0701   @Override
0702   public List<LanguageResource> getPublicLrInstances() {
0703     return Collections.unmodifiableList(getPublics(getLrInstances()));
0704   }// getPublicLrInstances()
0705 
0706   /** Get a list of all non-private instantiations of PR in the register. */
0707   @Override
0708   public List<ProcessingResource> getPublicPrInstances() {
0709     return Collections.unmodifiableList(getPublics(getPrInstances()));
0710   }// getPublicPrInstances()
0711 
0712   /** Get a list of all non-private instantiations of VR in the register. */
0713   @Override
0714   public List<VisualResource> getPublicVrInstances() {
0715     return Collections.unmodifiableList(getPublics(getVrInstances()));
0716   }// getPublicVrInstances()
0717 
0718   /** Get a list of all non-private types of LR in the register. */
0719   @Override
0720   public List<String> getPublicLrTypes() {
0721     return Collections.unmodifiableList(getPublicTypes(getLrTypes()));
0722   }// getPublicLrTypes()
0723 
0724   /** Get a list of all non-private types of PR in the register. */
0725   @Override
0726   public List<String> getPublicPrTypes() {
0727     return Collections.unmodifiableList(getPublicTypes(getPrTypes()));
0728   }// getPublicPrTypes()
0729 
0730   /** Get a list of all non-private types of VR in the register. */
0731   @Override
0732   public List<String> getPublicVrTypes() {
0733     return Collections.unmodifiableList(getPublicTypes(vrTypes));
0734   }// getPublicVrTypes()
0735 
0736   /** Get a list of all non-private types of controller in the register. */
0737   @Override
0738   public List<String> getPublicControllerTypes() {
0739     return Collections.unmodifiableList(getPublicTypes(getControllerTypes()));
0740   }// getPublicPrTypes()
0741 
0742   @Override
0743   public List<Resource> getAllInstances(String typethrows GateException {
0744     return getAllInstances(type, false);
0745   }
0746 
0747 
0748   public List<Resource> getAllInstances(String type, boolean includeHiddenthrows GateException {
0749     
0750     List<Resource> res = new ArrayList<Resource>();
0751     Class<? extends Resource> targetClass;
0752     try {
0753       targetClass =
0754         Gate.getClassLoader().loadClass(type).asSubclass(Resource.class);
0755     }
0756     catch(ClassNotFoundException cnfe) {
0757       throw new GateException("Invalid type " + type);
0758     }
0759     for(Map.Entry<String,ResourceData> entry : entrySet()) {
0760       String aType = entry.getKey();
0761       Class<?> aClass;
0762       try {
0763         aClass = entry.getValue().getResourceClass();
0764         if(targetClass.isAssignableFrom(aClass)) {
0765           // filter out hidden instances
0766           Iterator<? extends Resource> newInstancesIter =
0767             get(aType).getInstantiations().iterator();
0768           while(newInstancesIter.hasNext()) {
0769             Resource instance = newInstancesIter.next();
0770             if(includeHidden || !Gate.getHiddenAttribute(instance.getFeatures())) {
0771               res.add(instance);
0772             }
0773           }
0774         }
0775       }
0776       catch(ClassNotFoundException cnfe) {
0777         throw new LuckyException(
0778           "A type registered in the creole register does not exist in the VM!");
0779       }
0780 
0781     }// while(typesIter.hasNext())
0782 
0783     return res;
0784   }
0785 
0786   /**
0787    * Returns a list of strings representing class names for large VRs valid for
0788    * a given type of language/processing resource. The default VR will be the
0789    * first in the returned list.
0790    *
0791    @param resourceClassName
0792    *          the name of the resource that has large viewers. If
0793    *          resourceClassName is <b>null</b> then an empty list will be
0794    *          returned.
0795    @return a list with Strings representing the large VRs for the
0796    *         resourceClassName
0797    */
0798   @Override
0799   public List<String> getLargeVRsForResource(String resourceClassName) {
0800     return getVRsForResource(resourceClassName, ResourceData.LARGE_GUI);
0801   }// getLargeVRsForResource()
0802 
0803   /**
0804    * Returns a list of strings representing class names for small VRs valid for
0805    * a given type of language/processing resource The default VR will be the
0806    * first in the returned list.
0807    *
0808    @param resourceClassName
0809    *          the name of the resource that has large viewers. If
0810    *          resourceClassName is <b>null</b> then an empty list will be
0811    *          returned.
0812    @return a list with Strings representing the large VRs for the
0813    *         resourceClassName
0814    */
0815   @Override
0816   public List<String> getSmallVRsForResource(String resourceClassName) {
0817     return getVRsForResource(resourceClassName, ResourceData.SMALL_GUI);
0818   }// getSmallVRsForResource
0819 
0820   /**
0821    * Returns a list of strings representing class names for guiType VRs valid
0822    * for a given type of language/processing resource The default VR will be the
0823    * first in the returned list.
0824    *
0825    @param resourceClassName
0826    *          the name of the resource that has large viewers. If
0827    *          resourceClassName is <b>null</b> then an empty list will be
0828    *          returned.
0829    @param guiType
0830    *          can be ResourceData's LARGE_GUI or SMALL_GUI
0831    @return a list with Strings representing the large VRs for the
0832    *         resourceClassName
0833    */
0834   private List<String> getVRsForResource(String resourceClassName, int guiType) {
0835     // If resurceClassName is null return a simply list
0836     if(resourceClassName == null)
0837       return Collections.unmodifiableList(new ArrayList<String>());
0838     // create a Class object for the resource
0839     Class<?> resourceClass = null;
0840     GateClassLoader classLoader = Gate.getClassLoader();
0841     try {
0842       resourceClass = classLoader.loadClass(resourceClassName);
0843     }
0844     catch(ClassNotFoundException ex) {
0845       throw new GateRuntimeException(
0846         "Couldn't get resource class from the resource name:" + ex);
0847     }// End try
0848     LinkedList<String> responseList = new LinkedList<String>();
0849     String defaultVR = null;
0850     // Take all VRs and for each large one, test if
0851     // resourceClassName is asignable form VR's RESOURCE_DISPLAYED
0852     Iterator<String> vrIterator = vrTypes.iterator();
0853     while(vrIterator.hasNext()) {
0854       String vrClassName = vrIterator.next();
0855       ResourceData vrResourceData = this.get(vrClassName);
0856       if(vrResourceData == null)
0857         throw new GateRuntimeException(
0858           "Couldn't get resource data for VR called " + vrClassName);
0859       if(vrResourceData.getGuiType() == guiType) {
0860         String resourceDisplayed = vrResourceData.getResourceDisplayed();
0861         if(resourceDisplayed != null) {
0862           Class<?> resourceDisplayedClass = null;
0863           try {
0864             resourceDisplayedClass = classLoader.loadClass(resourceDisplayed);
0865           }
0866           catch(ClassNotFoundException ex) {
0867             throw new GateRuntimeException(
0868               "Couldn't get resource class from the resource name :"
0869                 + resourceDisplayed + " " + ex);
0870           }// End try
0871           if(resourceDisplayedClass.isAssignableFrom(resourceClass)) {
0872             responseList.add(vrClassName);
0873             if(vrResourceData.isMainView()) {
0874               defaultVR = vrClassName;
0875             }// End if
0876           }// End if
0877         }// End if
0878       }// End if
0879     }// End while
0880     if(defaultVR != null) {
0881       responseList.remove(defaultVR);
0882       responseList.addFirst(defaultVR);
0883     }// End if
0884     return Collections.unmodifiableList(responseList);
0885   }// getVRsForResource()
0886 
0887   /**
0888    * Returns a list of strings representing class names for annotation VRs that
0889    * are able to display/edit all types of annotations. The default VR will be
0890    * the first in the returned list.
0891    *
0892    @return a list with all VRs that can display all annotation types
0893    */
0894   @Override
0895   public List<String> getAnnotationVRs() {
0896     LinkedList<String> responseList = new LinkedList<String>();
0897     String defaultVR = null;
0898     Iterator<String> vrIterator = vrTypes.iterator();
0899     while(vrIterator.hasNext()) {
0900       String vrClassName = vrIterator.next();
0901       ResourceData vrResourceData = this.get(vrClassName);
0902       if(vrResourceData == null)
0903         throw new GateRuntimeException(
0904           "Couldn't get resource data for VR called " + vrClassName);
0905       Class<?> vrResourceClass = null;
0906       try {
0907         vrResourceClass = vrResourceData.getResourceClass();
0908       }
0909       catch(ClassNotFoundException ex) {
0910         throw new GateRuntimeException(
0911           "Couldn't create a class object for VR called " + vrClassName);
0912       }// End try
0913       // Test if VR can display all types of annotations
0914       if(vrResourceData.getGuiType() == ResourceData.NULL_GUI
0915         && vrResourceData.getAnnotationTypeDisplayed() == null
0916         && vrResourceData.getResourceDisplayed() == null
0917         && gate.creole.AnnotationVisualResource.class
0918           .isAssignableFrom(vrResourceClass)) {
0919 
0920         responseList.add(vrClassName);
0921         if(vrResourceData.isMainView()) defaultVR = vrClassName;
0922       }// End if
0923     }// End while
0924     if(defaultVR != null) {
0925       responseList.remove(defaultVR);
0926       responseList.addFirst(defaultVR);
0927     }// End if
0928     return Collections.unmodifiableList(responseList);
0929   }// getAnnotationVRs()
0930 
0931   /**
0932    * Returns a list of strings representing class names for annotation VRs that
0933    * are able to display/edit a given annotation type The default VR will be the
0934    * first in the returned list.
0935    */
0936   @Override
0937   public List<String> getAnnotationVRs(String annotationType) {
0938     if(annotationType == null)
0939       return Collections.unmodifiableList(new ArrayList<String>());
0940     LinkedList<String> responseList = new LinkedList<String>();
0941     String defaultVR = null;
0942     Iterator<String> vrIterator = vrTypes.iterator();
0943     while(vrIterator.hasNext()) {
0944       String vrClassName = vrIterator.next();
0945       ResourceData vrResourceData = this.get(vrClassName);
0946       if(vrResourceData == null)
0947         throw new GateRuntimeException(
0948           "Couldn't get resource data for VR called " + vrClassName);
0949       Class<?> vrResourceClass = null;
0950       try {
0951         vrResourceClass = vrResourceData.getResourceClass();
0952       }
0953       catch(ClassNotFoundException ex) {
0954         throw new GateRuntimeException(
0955           "Couldn't create a class object for VR called " + vrClassName);
0956       }// End try
0957       // Test if VR can display all types of annotations
0958       if(vrResourceData.getGuiType() == ResourceData.NULL_GUI
0959         && vrResourceData.getAnnotationTypeDisplayed() != null
0960         && gate.creole.AnnotationVisualResource.class
0961           .isAssignableFrom(vrResourceClass)) {
0962 
0963         String annotationTypeDisplayed =
0964           vrResourceData.getAnnotationTypeDisplayed();
0965         if(annotationTypeDisplayed.equals(annotationType)) {
0966           responseList.add(vrClassName);
0967           if(vrResourceData.isMainView()) defaultVR = vrClassName;
0968         }// End if
0969       }// End if
0970     }// End while
0971     if(defaultVR != null) {
0972       responseList.remove(defaultVR);
0973       responseList.addFirst(defaultVR);
0974     }// End if
0975     return Collections.unmodifiableList(responseList);
0976   }// getAnnotationVRs()
0977 
0978   /**
0979    * Renames an existing resource.
0980    */
0981   @Override
0982   public void setResourceName(Resource res, String newName) {
0983     String oldName = res.getName();
0984     res.setName(newName);
0985     fireResourceRenamed(res, oldName, newName);
0986   }
0987 
0988   /**
0989    * Returns a list of strings representing annotations types for which there
0990    * are custom viewers/editor registered.
0991    */
0992   @Override
0993   public List<String> getVREnabledAnnotationTypes() {
0994     LinkedList<String> responseList = new LinkedList<String>();
0995     Iterator<String> vrIterator = vrTypes.iterator();
0996     while(vrIterator.hasNext()) {
0997       String vrClassName = vrIterator.next();
0998       ResourceData vrResourceData = this.get(vrClassName);
0999       if(vrResourceData == null)
1000         throw new GateRuntimeException(
1001           "Couldn't get resource data for VR called " + vrClassName);
1002       // Test if VR can display all types of annotations
1003       if(vrResourceData.getGuiType() == ResourceData.NULL_GUI
1004         && vrResourceData.getAnnotationTypeDisplayed() != null) {
1005 
1006         String annotationTypeDisplayed =
1007           vrResourceData.getAnnotationTypeDisplayed();
1008         responseList.add(annotationTypeDisplayed);
1009       }// End if
1010     }// End while
1011     return Collections.unmodifiableList(responseList);
1012   }// getVREnabledAnnotationTypes()
1013 
1014   /** Get a list of all non-private instantiations. */
1015   protected <T> List<T> getPublics(List<T> instances) {
1016     Iterator<T> iter = instances.iterator();
1017     List<T> publics = new ArrayList<T>();
1018 
1019     // for each instance, if resource data specifies it isn't private,
1020     // add to the publics list
1021     while(iter.hasNext()) {
1022       T res = iter.next();
1023       ResourceData rd = get(res.getClass().getName());
1024       if(!rd.isPrivate()) publics.add(res);
1025     }
1026 
1027     return Collections.unmodifiableList(publics);
1028   // getPublics
1029 
1030   /** Gets a list of all non private types from alist of types */
1031   protected List<String> getPublicTypes(Collection<String> types) {
1032     Iterator<String> iter = types.iterator();
1033     List<String> publics = new ArrayList<String>();
1034     while(iter.hasNext()) {
1035       String oneType = iter.next();
1036       ResourceData rData = get(oneType);
1037       if(rData != null && !rData.isPrivate()) publics.add(oneType);
1038     }
1039     return Collections.unmodifiableList(publics);
1040   }// getPublicTypes
1041 
1042   @Override
1043   public synchronized void removeCreoleListener(CreoleListener l) {
1044     if(creoleListeners != null && creoleListeners.contains(l)) {
1045       @SuppressWarnings("unchecked")
1046       Vector<CreoleListener> v = (Vector<CreoleListener>)creoleListeners.clone();
1047       v.removeElement(l);
1048       creoleListeners = v;
1049     }
1050   }
1051 
1052   @Override
1053   public synchronized void addCreoleListener(CreoleListener l) {
1054     @SuppressWarnings("unchecked")
1055     Vector<CreoleListener> v =
1056       creoleListeners == null new Vector<CreoleListener>(2(Vector<CreoleListener>)creoleListeners.clone();
1057     if(!v.contains(l)) {
1058       v.addElement(l);
1059       creoleListeners = v;
1060     }
1061   // getPublicTypes
1062 
1063   /**
1064    * Removes a {@link gate.event.CreoleListener} previously registered with this
1065    * CreoleRegister. {@see #addCreoleListener()}
1066    */
1067 
1068   /**
1069    * Registers a {@link gate.event.CreoleListener}with this CreoleRegister. The
1070    * register will fire events every time a resource is added to or removed from
1071    * the system.
1072    */
1073   // addCreoleListener
1074   /**
1075    * Notifies all listeners that a new {@link gate.Resource} has been loaded
1076    * into the system
1077    */
1078   // fireResourceLoaded
1079   /**
1080    * Notifies all listeners that a {@link gate.Resource} has been unloaded from
1081    * the system
1082    */
1083   // fireResourceUnloaded
1084   /** A list of the types of LR in the register. */
1085   protected Set<String> lrTypes;
1086 
1087   /** A list of the types of PR in the register. */
1088   protected Set<String> prTypes;
1089 
1090   /** A list of the types of VR in the register. */
1091   protected List<String> vrTypes;
1092 
1093   /** A list of the types of Controller in the register. */
1094   protected Set<String> controllerTypes;
1095 
1096   /** A list of the types of TOOL in the register. */
1097   protected Set<String> toolTypes;
1098 
1099   /** A list of the types of Packaged Applications in the register */
1100   protected Set<String> applicationTypes;
1101 
1102   private transient Vector<CreoleListener> creoleListeners;
1103   
1104   private transient List<PluginListener> pluginListeners = new CopyOnWriteArrayList<PluginListener>();
1105   
1106   protected void firePluginLoaded(URL url) {
1107     for (PluginListener listener : pluginListeners) {
1108       listener.pluginLoaded(url);
1109     }
1110   }
1111   
1112   protected void firePluginUnloaded(URL url) {
1113     for (PluginListener listener : pluginListeners) {
1114       listener.pluginUnloaded(url);
1115     }
1116   }
1117   
1118   public void addPluginListener(PluginListener listener) {
1119     pluginListeners.add(listener);
1120   }
1121   
1122   public void removePluginListener(PluginListener listener) {
1123     pluginListeners.remove(listener);
1124   }
1125 
1126   protected void fireResourceLoaded(CreoleEvent e) {
1127     if(creoleListeners != null) {
1128       Vector<CreoleListener> listeners = creoleListeners;
1129       int count = listeners.size();
1130       for(int i = 0; i < count; i++) {
1131         listeners.elementAt(i).resourceLoaded(e);
1132       }
1133     }
1134   }
1135 
1136   protected void fireResourceUnloaded(CreoleEvent e) {
1137     if(creoleListeners != null) {
1138       Vector<CreoleListener> listeners = creoleListeners;
1139       int count = listeners.size();
1140       for(int i = 0; i < count; i++) {
1141         listeners.elementAt(i).resourceUnloaded(e);
1142       }
1143     }
1144   }
1145 
1146   protected void fireResourceRenamed(Resource res, String oldName,
1147     String newName) {
1148     if(creoleListeners != null) {
1149       Vector<CreoleListener> listeners = creoleListeners;
1150       int count = listeners.size();
1151       for(int i = 0; i < count; i++) {
1152         listeners.elementAt(i).resourceRenamed(res, oldName,
1153           newName);
1154       }
1155     }
1156   }
1157 
1158   protected void fireDatastoreOpened(CreoleEvent e) {
1159     if(creoleListeners != null) {
1160       Vector<CreoleListener> listeners = creoleListeners;
1161       int count = listeners.size();
1162       for(int i = 0; i < count; i++) {
1163         listeners.elementAt(i).datastoreOpened(e);
1164       }
1165     }
1166   }
1167 
1168   protected void fireDatastoreCreated(CreoleEvent e) {
1169     if(creoleListeners != null) {
1170       Vector<CreoleListener> listeners = creoleListeners;
1171       int count = listeners.size();
1172       for(int i = 0; i < count; i++) {
1173         listeners.elementAt(i).datastoreCreated(e);
1174       }
1175     }
1176   }
1177 
1178   protected void fireDatastoreClosed(CreoleEvent e) {
1179     if(creoleListeners != null) {
1180       Vector<CreoleListener> listeners = creoleListeners;
1181       int count = listeners.size();
1182       for(int i = 0; i < count; i++) {
1183         listeners.elementAt(i).datastoreClosed(e);
1184       }
1185     }
1186   }
1187 
1188   @Override
1189   public void resourceLoaded(CreoleEvent e) {
1190     fireResourceLoaded(e);
1191   }
1192 
1193   @Override
1194   public void resourceUnloaded(CreoleEvent e) {
1195     fireResourceUnloaded(e);
1196   }
1197 
1198   @Override
1199   public void resourceRenamed(Resource resource, String oldName, String newName) {
1200     fireResourceRenamed(resource, oldName, newName);
1201   }
1202 
1203   @Override
1204   public void datastoreOpened(CreoleEvent e) {
1205     fireDatastoreOpened(e);
1206   }
1207 
1208   @Override
1209   public void datastoreCreated(CreoleEvent e) {
1210     fireDatastoreCreated(e);
1211   }
1212 
1213   @Override
1214   public void datastoreClosed(CreoleEvent e) {
1215     fireDatastoreClosed(e);
1216   }
1217 
1218   /** The lists of listeners registered with this CreoleRegister */
1219 
1220   /**
1221    * Type-safe read-only list used by getLrInstances, getPrInstances, etc.
1222    */
1223   private static class TypedResourceList<T extends Resource>
1224                                                              extends
1225                                                                AbstractList<T> {
1226     private List<Resource> backingList;
1227 
1228     private Class<T> realType;
1229 
1230     TypedResourceList(List<Resource> backingList, Class<T> realType) {
1231       this.backingList = backingList;
1232       this.realType = realType;
1233     }
1234 
1235     @Override
1236     public T get(int i) {
1237       return realType.cast(backingList.get(i));
1238     }
1239 
1240     @Override
1241     public int size() {
1242       return backingList.size();
1243     }
1244   }
1245 
1246 // class CreoleRegisterImpl