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