|
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 |
|