| CreoleXmlHandler |

    1   /*
    2    *  CreoleXmlHandler.java
    3    *
    4    *  Copyright (c) 1998-2001, The University of Sheffield.
    5    *
    6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
    7    *  software, licenced under the GNU Library General Public License,
    8    *  Version 2, June 1991 (in the distribution as file licence.html,
    9    *  and also available at http://gate.ac.uk/gate/licence.html).
    10   *
    11   *  Hamish Cunningham, 1/Sept/2000
    12   *
    13   *  $Id: CreoleXmlHandler.java,v 1.35 2001/11/29 18:07:36 cursu Exp $
    14   */
    15
    16  package gate.creole;
    17
    18  import java.util.*;
    19
    20  import org.xml.sax.*;
    21  import org.xml.sax.helpers.*;
    22  import javax.xml.parsers.*;
    23  import java.net.*;
    24  import java.io.*;
    25
    26  import gate.*;
    27  import gate.util.*;
    28  import gate.xml.SimpleErrorHandler;
    29
    30  /** This is a SAX handler for processing <CODE>creole.xml</CODE> files.
    31    * It would have been better to write it using DOM or JDOM but....
    32    * Resource data objects are created and added to the CREOLE register.
    33    * URLs for resource JAR files are added to the GATE class loader.
    34    */
    35  public class CreoleXmlHandler extends DefaultHandler {
    36
    37    /** A stack to stuff PCDATA onto for reading back at element ends.
    38     *  (Probably redundant to have a stack as we only push one item
    39     *  onto it. Probably. Ok, so I should check, but a) it works, b)
    40     *  I'm bald already and c) life is short.)
    41     */
    42    private Stack contentStack = new Stack();
    43
    44    /** The current resource data object */
    45    private ResourceData resourceData;
    46
    47    /** The current parameter list */
    48    private ParameterList currentParamList = new ParameterList();
    49
    50    /** The current parameter disjunction */
    51    private List currentParamDisjunction = new ArrayList();
    52
    53    /** The current parameter */
    54    private Parameter currentParam = new Parameter();
    55
    56    /** The current element's attribute list */
    57    private Attributes currentAttributes;
    58
    59    /** Debug flag */
    60    private static final boolean DEBUG = false;
    61
    62    /** The source URL of the directory file being parsed. */
    63    private URL sourceUrl;
    64
    65    /** This object indicates what to do when the parser encounts an error*/
    66    private SimpleErrorHandler _seh = new SimpleErrorHandler();
    67
    68    /** This field represents the params map required for autoinstantiation
    69      * Its a map from param name to param value.
    70      */
    71    private FeatureMap currentAutoinstanceParams = null;
    72
    73    /** This field holds autoinstanceParams describing the resource that
    74      * needs to be instantiated
    75      */
    76    private List currentAutoinstances = null;
    77
    78    /** Construction */
    79    public CreoleXmlHandler(CreoleRegister register, URL directoryUrl) {
    80      this.register = register;
    81      this.sourceUrl = directoryUrl;
    82    } // construction
    83
    84    /** The register object that we add ResourceData objects to during parsing.
    85      */
    86    private CreoleRegister register;
    87
    88    /** Called when the SAX parser encounts the beginning of the XML document */
    89    public void startDocument() throws GateSaxException {
    90      if(DEBUG) Out.prln("start document");
    91    } // startDocument
    92
    93    /** Called when the SAX parser encounts the end of the XML document */
    94    public void endDocument() throws GateSaxException {
    95      if(DEBUG) Out.prln("end document");
    96      if(! contentStack.isEmpty()) {
    97        StringBuffer errorMessage =
    98          new StringBuffer("document ended but element stack not empty:");
    99        while(! contentStack.isEmpty())
    100         errorMessage.append(Strings.getNl()+"  "+(String) contentStack.pop());
    101       throw new GateSaxException(errorMessage.toString());
    102     }
    103   } // endDocument
    104
    105   /** A verboase method for Attributes*/
    106   private String attributes2String(Attributes atts){
    107     StringBuffer strBuf = new StringBuffer("");
    108     if (atts == null) return strBuf.toString();
    109     for (int i = 0; i < atts.getLength(); i++) {
    110      String attName  = atts.getQName(i);
    111      String attValue = atts.getValue(i);
    112      strBuf.append(" ");
    113      strBuf.append(attName);
    114      strBuf.append("=");
    115      strBuf.append(attValue);
    116     }// End for
    117     return strBuf.toString();
    118   }// attributes2String()
    119
    120   /** Called when the SAX parser encounts the beginning of an XML element */
    121   public void startElement (String uri, String qName, String elementName,
    122                                                              Attributes atts){
    123
    124     if(DEBUG) {
    125       Out.pr("startElement: ");
    126       Out.println(
    127         elementName + " " +
    128         attributes2String(atts)
    129       );
    130     }
    131
    132     // create a new ResourceData when it's a RESOURCE element
    133     if(elementName.toUpperCase().equals("RESOURCE")) {
    134       resourceData = new ResourceData();
    135       resourceData.setFeatures(Factory.newFeatureMap());
    136       currentAutoinstances = new ArrayList();
    137     }// End if RESOURCE
    138
    139     // record the attributes of this element
    140     currentAttributes = atts;
    141
    142     // When an AUTOINSTANCE element is found a params FeatureMap will
    143     // be prepared in order for the resource to be instantiated
    144     if (elementName.toUpperCase().equals("AUTOINSTANCE")){
    145       currentAutoinstanceParams = Factory.newFeatureMap();
    146     }// End if AUTOINSTANCE
    147
    148     // When a PARAN start element is found, the parameter would be instantiated
    149     // with a value and added to the autoinstanceParams
    150     if (elementName.toUpperCase().equals("PARAM")){
    151       // The autoinstanceParams should always be != null because of the fact
    152       // that PARAM is incuded into an AUTOINSTANCE element.
    153       // IF a AUTOINSTANCE starting element would be missing then the
    154       // parser would signal this later....
    155       if (currentAutoinstanceParams == null)
    156         currentAutoinstanceParams = Factory.newFeatureMap();
    157       // Take the param's name and value
    158       String paramName = currentAttributes.getValue("NAME");
    159       String paramStrValue = currentAttributes.getValue("VALUE");
    160       if (paramName == null)
    161         throw new GateRuntimeException ("Found in creole.xml a PARAM element" +
    162         " for resource "+ resourceData.getClassName()+ " without a NAME"+
    163         " attribute. Check the file and try again.");
    164       if (paramStrValue == null)
    165         throw new GateRuntimeException("Found in creole.xml a PARAM element"+
    166         " for resource "+ resourceData.getClassName()+ " without a VALUE"+
    167         " attribute. Check the file and try again.");
    168       // Add the paramname and its value to the autoinstanceParams
    169       currentAutoinstanceParams.put(paramName,paramStrValue);
    170     }// End if PARAM
    171
    172     // process attributes of parameter and GUI elements
    173     if(elementName.toUpperCase().equals("PARAMETER")) {
    174       if(DEBUG) {
    175         for(int i=0, len=currentAttributes.getLength(); i<len; i++) {
    176           Out.prln(currentAttributes.getLocalName(i));
    177           Out.prln(currentAttributes.getValue(i));
    178         }// End for
    179       }// End if
    180       currentParam.comment = currentAttributes.getValue("COMMENT");
    181       currentParam.defaultValueString = currentAttributes.getValue("DEFAULT");
    182       currentParam.optional =
    183         Boolean.valueOf(currentAttributes.getValue("OPTIONAL")).booleanValue();
    184       currentParam.name = currentAttributes.getValue("NAME");
    185       currentParam.runtime =
    186         Boolean.valueOf(currentAttributes.getValue("RUNTIME")).booleanValue();
    187       currentParam.itemClassName =
    188                                 currentAttributes.getValue("ITEM_CLASS_NAME");
    189       // read the suffixes and transform them to a Set of Strings
    190       String suffixes = currentAttributes.getValue("SUFFIXES");
    191       Set suffiexesSet = null;
    192       if (suffixes != null){
    193         suffiexesSet = new HashSet();
    194         StringTokenizer strTokenizer = new StringTokenizer(suffixes,";");
    195         while(strTokenizer.hasMoreTokens()){
    196            suffiexesSet.add(strTokenizer.nextToken());
    197         }// End while
    198       }// End if
    199       currentParam.suffixes = suffiexesSet;
    200     }else if(elementName.toUpperCase().equals("GUI")){
    201       String typeValue = currentAttributes.getValue("TYPE");
    202       if (typeValue != null){
    203         if (typeValue.toUpperCase().equals("LARGE"))
    204           resourceData.setGuiType(ResourceData.LARGE_GUI);
    205         if (typeValue.toUpperCase().equals("SMALL"))
    206           resourceData.setGuiType(ResourceData.SMALL_GUI);
    207       }// End if
    208     }// End if
    209
    210     // if there are any parameters awaiting addition to the list, add them
    211     // (note that they're not disjunctive or previous "/OR" would have got 'em)
    212     if(elementName.toUpperCase().equals("OR")) {
    213       if(! currentParamDisjunction.isEmpty()) {
    214         currentParamList.addAll(currentParamDisjunction);
    215         currentParamDisjunction = new ArrayList();
    216       }// End if
    217     }// End if
    218   } // startElement()
    219
    220   /** Utility function to throw exceptions on stack errors. */
    221   private void checkStack(String methodName, String elementName)
    222   throws GateSaxException {
    223     if(contentStack.isEmpty())
    224       throw new GateSaxException(
    225         methodName + " called for element " + elementName + " with empty stack"
    226       );
    227   } // checkStack
    228
    229   /** Called when the SAX parser encounts the end of an XML element.
    230     * This is where ResourceData objects get values set, and where
    231     * they are added to the CreoleRegister when we parsed their complete
    232     * metadata entries.
    233     */
    234   public void endElement (String uri, String qName, String elementName)
    235                                                     throws GateSaxException {
    236     if(DEBUG) Out.prln("endElement: " + elementName);
    237
    238     //////////////////////////////////////////////////////////////////
    239     if(elementName.toUpperCase().equals("RESOURCE")) {
    240       // check for validity of the resource data
    241       if(! resourceData.isValid())
    242         throw new GateSaxException(
    243           "Invalid resource data: " + resourceData.getValidityMessage()
    244         );
    245
    246       // add the new resource data object to the creole register
    247       register.put(resourceData.getClassName(), resourceData);
    248       // if the resource is auto-loading, try and load it
    249       if(resourceData.isAutoLoading())
    250         try {
    251           Class resClass = resourceData.getResourceClass();
    252 //          Resource res = Factory.createResource(
    253 //              resourceData.getClassName(), Factory.newFeatureMap()
    254 //          );
    255 //          resourceData.makeInstantiationPersistant(res);
    256         } catch(ClassNotFoundException e) {
    257           throw new GateSaxException(
    258             "Couldn't load autoloading resource: " +
    259             resourceData.getName() + "; problem was: " + e
    260           );
    261         }// End try
    262
    263       // if there are any parameters awaiting addition to the list, add them
    264       // (note that they're not disjunctive or the "/OR" would have got them)
    265       if(! currentParamDisjunction.isEmpty()) {
    266         currentParamList.addAll(currentParamDisjunction);
    267         currentParamDisjunction = new ArrayList();
    268       }// End if
    269
    270       // add the parameter list to the resource (and reinitialise it)
    271       resourceData.setParameterList(currentParamList);
    272       currentParamList = new ParameterList();
    273
    274       if(DEBUG) Out.println("added: " + resourceData);
    275       // Iterate through autoinstances and try to instanciate them
    276       if ( currentAutoinstances != null && !currentAutoinstances.isEmpty()){
    277         Iterator iter = currentAutoinstances.iterator();
    278         while (iter.hasNext()){
    279           FeatureMap autoinstanceParams = (FeatureMap) iter.next();
    280           iter.remove();
    281           // Try to create the resource.
    282           try {
    283             Resource res = Factory.createResource(
    284                               resourceData.getClassName(), autoinstanceParams);
    285             resourceData.makeInstantiationPersistant(res);
    286           } catch(ResourceInstantiationException e) {
    287             throw new GateSaxException(
    288               "Couldn't autoinstanciate resource: " +
    289               resourceData.getName() + "; problem was: " + e
    290             );
    291           }// End try
    292         }// End while
    293       }// End if
    294       currentAutoinstances = null;
    295     // End RESOURCE processing
    296     //////////////////////////////////////////////////////////////////
    297     } else if(elementName.toUpperCase().equals("AUTOINSTANCE")) {
    298       //checkStack("endElement", "AUTOINSTANCE");
    299       // Cash the autoinstance into the autoins
    300       if (currentAutoinstanceParams != null)
    301         currentAutoinstances.add(currentAutoinstanceParams);
    302     // End AUTOINSTANCE processing
    303     //////////////////////////////////////////////////////////////////
    304     } else if(elementName.toUpperCase().equals("PARAM")) {
    305     // End PARAM processing
    306     //////////////////////////////////////////////////////////////////
    307     } else if(elementName.toUpperCase().equals("NAME")) {
    308       checkStack("endElement", "NAME");
    309       resourceData.setName((String) contentStack.pop());
    310     // End NAME processing
    311     //////////////////////////////////////////////////////////////////
    312     } else if(elementName.toUpperCase().equals("JAR")) {
    313       checkStack("endElement", "JAR");
    314
    315       // add jar file name
    316       String jarFileName = (String) contentStack.pop();
    317       resourceData.setJarFileName(jarFileName);
    318
    319       // add jar file URL if there is one
    320       if(sourceUrl != null) {
    321         String sourceUrlName = sourceUrl.toExternalForm();
    322         String separator = "/";
    323
    324         if(sourceUrlName.endsWith(separator))
    325           separator = "";
    326         URL jarFileUrl = null;
    327
    328         try {
    329           jarFileUrl = new URL(sourceUrlName + separator + jarFileName);
    330           resourceData.setJarFileUrl(jarFileUrl);
    331
    332           // add the jar URL to the class loader
    333           if(DEBUG) Out.prln("adding URL to classloader: " + jarFileUrl);
    334           Gate.getClassLoader().addURL(jarFileUrl);
    335         } catch(MalformedURLException e) {
    336           throw new GateSaxException("bad URL " + jarFileUrl + e);
    337         }// End try
    338       }// End if
    339     // End JAR processing
    340     //////////////////////////////////////////////////////////////////
    341     } else if(elementName.toUpperCase().equals("XML")) {
    342       checkStack("endElement", "XML");
    343
    344       // add XML file name
    345       String xmlFileName = (String) contentStack.pop();
    346       resourceData.setXmlFileName(xmlFileName);
    347
    348       // add xml file URL if there is one
    349       if(sourceUrl != null) {
    350         String sourceUrlName = sourceUrl.toExternalForm();
    351         String separator = "/";
    352         if(sourceUrlName.endsWith(separator))
    353           separator = "";
    354         URL xmlFileUrl = null;
    355
    356         try {
    357           xmlFileUrl = new URL(sourceUrlName + separator + xmlFileName);
    358           resourceData.setXmlFileUrl(xmlFileUrl);
    359         } catch(MalformedURLException e) {
    360           throw new GateSaxException("bad URL " + xmlFileUrl + e);
    361         }// End try
    362       }// End if
    363     // End XML processing
    364     //////////////////////////////////////////////////////////////////
    365     } else if(elementName.toUpperCase().equals("CLASS")) {
    366       checkStack("endElement", "CLASS");
    367       resourceData.setClassName((String) contentStack.pop());
    368     // End CLASS processing
    369     //////////////////////////////////////////////////////////////////
    370     } else if(elementName.toUpperCase().equals("COMMENT")) {
    371       checkStack("endElement", "COMMENT");
    372       resourceData.setComment((String) contentStack.pop());
    373     // End COMMENT processing
    374     //////////////////////////////////////////////////////////////////
    375     } else if(elementName.toUpperCase().equals("INTERFACE")) {
    376       checkStack("endElement", "INTERFACE");
    377       resourceData.setInterfaceName((String) contentStack.pop());
    378     // End INTERFACE processing
    379     //////////////////////////////////////////////////////////////////
    380     } else if(elementName.toUpperCase().equals("ICON")) {
    381       checkStack("endElement", "ICON");
    382       resourceData.setIcon((String) contentStack.pop());
    383     // End ICON processing
    384     //////////////////////////////////////////////////////////////////
    385     } else if(elementName.toUpperCase().equals("OR")) {
    386       currentParamList.add(currentParamDisjunction);
    387       currentParamDisjunction = new ArrayList();
    388     // End OR processing
    389     //////////////////////////////////////////////////////////////////
    390     } else if(elementName.toUpperCase().equals("PARAMETER")) {
    391       checkStack("endElement", "PARAMETER");
    392       currentParam.typeName = (String) contentStack.pop();
    393       currentParamDisjunction.add(currentParam);
    394       if(DEBUG)
    395         Out.prln("added param: " + currentParam);
    396       currentParam = new Parameter();
    397     // End PARAMETER processing
    398     //////////////////////////////////////////////////////////////////
    399     } else if(elementName.toUpperCase().equals("AUTOLOAD")) {
    400       resourceData.setAutoLoading(true);
    401     // End AUTOLOAD processing
    402     //////////////////////////////////////////////////////////////////
    403     } else if(elementName.toUpperCase().equals("PRIVATE")) {
    404       resourceData.setPrivate(true);
    405     // End PRIVATE processing
    406     //////////////////////////////////////////////////////////////////
    407     } else if(elementName.toUpperCase().equals("TOOL")) {
    408       resourceData.setTool(true);
    409     // End TOOL processing
    410     //////////////////////////////////////////////////////////////////
    411     } else if(elementName.toUpperCase().equals("MAIN_VIEWER")) {
    412       resourceData.setIsMainView(true);
    413     // End MAIN_VIEWER processing
    414     //////////////////////////////////////////////////////////////////
    415     } else if(elementName.toUpperCase().equals("RESOURCE_DISPLAYED")){
    416       checkStack("endElement", "RESOURCE_DISPLAYED");
    417       String resourceDisplayed = (String) contentStack.pop();
    418       resourceData.setResourceDisplayed(resourceDisplayed);
    419       Class resourceDisplayedClass = null;
    420       try{
    421         resourceDisplayedClass = Gate.getClassLoader().
    422                                  loadClass(resourceDisplayed);
    423       } catch (ClassNotFoundException ex){
    424         throw new GateRuntimeException(
    425           "Couldn't get resource class from the resource name :" +
    426           resourceDisplayed + " " +ex );
    427       }// End try
    428     // End RESOURCE_DISPLAYED processing
    429     //////////////////////////////////////////////////////////////////
    430     } else if(elementName.toUpperCase().equals("ANNOTATION_TYPE_DISPLAYED")){
    431       checkStack("endElement", "ANNOTATION_TYPE_DISPLAYED");
    432       resourceData.setAnnotationTypeDisplayed((String) contentStack.pop());
    433     // End ANNOTATION_TYPE_DISPLAYED processing
    434     //////////////////////////////////////////////////////////////////
    435     } else if(elementName.toUpperCase().equals("GUI")) {
    436
    437     // End GUI processing
    438     //////////////////////////////////////////////////////////////////
    439     } else if(elementName.toUpperCase().equals("CREOLE")) {
    440     // End CREOLE processing
    441     //////////////////////////////////////////////////////////////////
    442     } else if(elementName.toUpperCase().equals("CREOLE-DIRECTORY")) {
    443     // End CREOLE-DIRECTORY processing
    444     //////////////////////////////////////////////////////////////////
    445     } else { // arbitrary elements get added as features of the resource data
    446       if(resourceData != null)
    447         resourceData.getFeatures().put(
    448           elementName.toUpperCase(),
    449           ((contentStack.isEmpty()) ? null : (String) contentStack.pop())
    450         );
    451     }
    452     //////////////////////////////////////////////////////////////////
    453
    454   } // endElement
    455
    456   /** Called when the SAX parser encounts text (PCDATA) in the XML doc */
    457   public void characters(char[] text, int start, int length)
    458   throws SAXException {
    459     // Get the trimmed text between elements
    460     String content = new String(text, start, length).trim();
    461     // If the entire text is empty or is made from whitespaces then we simply
    462     // return
    463     if (content.length() == 0) return;
    464     contentStack.push(content);
    465     if(DEBUG) Out.println(content);
    466   } // characters
    467
    468   /** Called when the SAX parser encounts white space */
    469   public void ignorableWhitespace(char ch[], int start, int length)
    470   throws SAXException {
    471   } // ignorableWhitespace
    472
    473   /** Called for parse errors. */
    474   public void error(SAXParseException ex) throws SAXException {
    475     _seh.error(ex);
    476   } // error
    477
    478   /** Called for fatal errors. */
    479   public void fatalError(SAXParseException ex) throws SAXException {
    480     _seh.fatalError(ex);
    481   } // fatalError
    482
    483   /** Called for warnings. */
    484   public void warning(SAXParseException ex) throws SAXException {
    485     _seh.warning(ex);
    486   } // warning
    487
    488 } // CreoleXmlHandler
    489

| CreoleXmlHandler |