CreoleXmlHandler.java
001 /*
002  *  CreoleXmlHandler.java
003  *
004  *  Copyright (c) 1995-2013, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 1/Sept/2000
013  *
014  *  $Id: CreoleXmlHandler.java 17530 2014-03-04 15:57:43Z markagreenwood $
015  */
016 
017 package gate.creole;
018 
019 import gate.CreoleRegister;
020 import gate.Factory;
021 import gate.FeatureMap;
022 import gate.Gate;
023 import gate.GateConstants;
024 import gate.util.GateRuntimeException;
025 import gate.util.GateSaxException;
026 import gate.util.Out;
027 import gate.util.Strings;
028 import gate.xml.SimpleErrorHandler;
029 
030 import java.net.MalformedURLException;
031 import java.net.URL;
032 import java.util.ArrayList;
033 import java.util.HashSet;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.Set;
037 import java.util.SortedMap;
038 import java.util.Stack;
039 import java.util.StringTokenizer;
040 import java.util.TreeMap;
041 
042 import org.xml.sax.Attributes;
043 import org.xml.sax.SAXException;
044 import org.xml.sax.SAXParseException;
045 import org.xml.sax.helpers.DefaultHandler;
046 
047 /** This is a SAX handler for processing <CODE>creole.xml</CODE> files.
048   * It would have been better to write it using DOM or JDOM but....
049   * Resource data objects are created and added to the CREOLE register.
050   * URLs for resource JAR files are added to the GATE class loader.
051   */
052 public class CreoleXmlHandler extends DefaultHandler {
053 
054   /** A stack to stuff PCDATA onto for reading back at element ends.
055    *  (Probably redundant to have a stack as we only push one item
056    *  onto it. Probably. Ok, so I should check, but a) it works, b)
057    *  I'm bald already and c) life is short.)
058    */
059   private Stack<String> contentStack = new Stack<String>();
060 
061   /** The current resource data object */
062   private ResourceData resourceData;
063 
064   /** The current parameter list */
065   private ParameterList currentParamList = new ParameterList();
066 
067   /**
068    * The current parameter disjunction.  This is a map where each key
069    * is a "priority" and the value is a list of parameters tagged with
070    * that priority.  This map is flattened into a single list when
071    * all the parameters in a single disjunction have been processed,
072    * such that parameters with a smaller priority value are listed
073    * ahead of those with a larger value, and those with no explicit
074    * priority are listed last of all.  Parameters at the same priority
075    * are listed in document order.  This is not so useful when writing
076    * creole.xml files by hand but is necessary to ensure a predictable
077    * order when using CreoleParameter annotations.  The GATE developer
078    * GUI offers the first listed (i.e. highest priority) parameter for
079    * each disjunction as the default option in the resource parameters
080    * dialog box. 
081    */
082   private SortedMap<Integer, List<Parameter>> currentParamDisjunction =
083     new TreeMap<Integer, List<Parameter>>();
084 
085   /** The current parameter */
086   private Parameter currentParam;
087 
088   /** The current element's attribute list */
089   private Attributes currentAttributes;
090 
091   /** Debug flag */
092   private static final boolean DEBUG = false;
093 
094   /** The source URL of the directory file being parsed. */
095   private URL sourceUrl;
096   
097   /**
098    * The URL to the creole.xml file being parsed.
099    */
100   private URL creoleFileUrl;
101 
102   /** This object indicates what to do when the parser encounts an error*/
103   private SimpleErrorHandler _seh = new SimpleErrorHandler();
104 
105   /** This field represents the params map required for autoinstantiation
106     * Its a map from param name to param value.
107     */
108   private FeatureMap currentAutoinstanceParams = null;
109 
110   /** This field holds autoinstanceParams describing the resource that
111     * needs to be instantiated
112     */
113   private List<FeatureMap> currentAutoinstances = null;
114 
115 
116   /** This is used to capture all data within two tags before calling the actual characters method */
117   private StringBuffer contentBuffer = new StringBuffer("");
118 
119   /** This is a variable that shows if characters have been read */
120   private boolean readCharacterStatus = false;
121 
122   /** Construction */
123   public CreoleXmlHandler(CreoleRegister register, URL directoryUrl, 
124           URL creoleFileUrl) {
125     this.register = register;
126     this.sourceUrl = directoryUrl;
127     this.creoleFileUrl = creoleFileUrl;
128     currentParam = new Parameter(this.creoleFileUrl);
129   // construction
130 
131   /** The register object that we add ResourceData objects to during parsing.
132     */
133   private CreoleRegister register;
134 
135   /** Called when the SAX parser encounts the beginning of the XML document */
136   @Override
137   public void startDocument() throws GateSaxException {
138     if(DEBUGOut.prln("start document");
139   // startDocument
140 
141   /** Called when the SAX parser encounts the end of the XML document */
142   @Override
143   public void endDocument() throws GateSaxException {
144     if(DEBUGOut.prln("end document");
145     if(! contentStack.isEmpty()) {
146       StringBuffer errorMessage =
147         new StringBuffer("document ended but element stack not empty:");
148       while(! contentStack.isEmpty())
149         errorMessage.append(Strings.getNl()+"  "+contentStack.pop());
150       throw new GateSaxException(errorMessage.toString());
151     }
152   // endDocument
153 
154   /** A verboase method for Attributes*/
155   private String attributes2String(Attributes atts){
156     StringBuffer strBuf = new StringBuffer("");
157     if (atts == nullreturn strBuf.toString();
158     for (int i = 0; i < atts.getLength(); i++) {
159      String attName  = atts.getQName(i);
160      String attValue = atts.getValue(i);
161      strBuf.append(" ");
162      strBuf.append(attName);
163      strBuf.append("=");
164      strBuf.append(attValue);
165     }// End for
166     return strBuf.toString();
167   }// attributes2String()
168 
169   /** Called when the SAX parser encounts the beginning of an XML element */
170   @Override
171   public void startElement (String uri, String qName, String elementName,
172                                                              Attributes attsthrows SAXException {
173 
174     // call characterActions
175     if(readCharacterStatus) {
176       readCharacterStatus = false;
177       charactersAction(new String(contentBuffer).toCharArray(),0,contentBuffer.length());
178     }
179 
180     if(DEBUG) {
181       Out.pr("startElement: ");
182       Out.println(
183         elementName + " " +
184         attributes2String(atts)
185       );
186     }
187 
188     // create a new ResourceData when it's a RESOURCE element
189     if(elementName.toUpperCase().equals("RESOURCE")) {
190       resourceData = new ResourceData();
191       resourceData.setFeatures(Factory.newFeatureMap());
192       currentAutoinstances = new ArrayList<FeatureMap>();
193     }// End if RESOURCE
194 
195     // record the attributes of this element
196     currentAttributes = atts;
197 
198     // When an AUTOINSTANCE element is found a params FeatureMap will
199     // be prepared in order for the resource to be instantiated
200     if (elementName.toUpperCase().equals("AUTOINSTANCE")){
201       currentAutoinstanceParams = Factory.newFeatureMap();
202     }// End if AUTOINSTANCE
203 
204     //When an HIDDEN-AUTOINSTANCE element is found a params FeatureMap will
205     // be prepared in order for the resource to be instantiated
206     if (elementName.toUpperCase().equals("HIDDEN-AUTOINSTANCE")){
207       currentAutoinstanceParams = Factory.newFeatureMap();
208       Gate.setHiddenAttribute(currentAutoinstanceParams, true);
209     }// End if AUTOINSTANCE
210     
211     // When a PARAN start element is found, the parameter would be instantiated
212     // with a value and added to the autoinstanceParams
213     if (elementName.toUpperCase().equals("PARAM")){
214       // The autoinstanceParams should always be != null because of the fact
215       // that PARAM is incuded into an AUTOINSTANCE element.
216       // IF a AUTOINSTANCE starting element would be missing then the
217       // parser would signal this later....
218       if (currentAutoinstanceParams == null)
219         currentAutoinstanceParams = Factory.newFeatureMap();
220       // Take the param's name and value
221       String paramName = currentAttributes.getValue("NAME");
222       String paramStrValue = currentAttributes.getValue("VALUE");
223       if (paramName == null)
224         throw new GateRuntimeException ("Found in creole.xml a PARAM element" +
225         " for resource "+ resourceData.getClassName()" without a NAME"+
226         " attribute. Check the file and try again.");
227       if (paramStrValue == null)
228         throw new GateRuntimeException("Found in creole.xml a PARAM element"+
229         " for resource "+ resourceData.getClassName()" without a VALUE"+
230         " attribute. Check the file and try again.");
231       // Add the paramname and its value to the autoinstanceParams
232       currentAutoinstanceParams.put(paramName,paramStrValue);
233     }// End if PARAM
234 
235     // process attributes of parameter and GUI elements
236     if(elementName.toUpperCase().equals("PARAMETER")) {
237       if(DEBUG) {
238         for(int i=0, len=currentAttributes.getLength(); i<len; i++) {
239           Out.prln(currentAttributes.getLocalName(i));
240           Out.prln(currentAttributes.getValue(i));
241         }// End for
242       }// End if
243       currentParam.comment = currentAttributes.getValue("COMMENT");
244       currentParam.helpURL = currentAttributes.getValue("HELPURL");
245       currentParam.defaultValueString = currentAttributes.getValue("DEFAULT");
246       currentParam.optional =
247         Boolean.valueOf(currentAttributes.getValue("OPTIONAL")).booleanValue();
248       currentParam.name = currentAttributes.getValue("NAME");
249       currentParam.runtime =
250         Boolean.valueOf(currentAttributes.getValue("RUNTIME")).booleanValue();
251       currentParam.itemClassName =
252                                 currentAttributes.getValue("ITEM_CLASS_NAME");
253       // read the suffixes and transform them to a Set of Strings
254       String suffixes = currentAttributes.getValue("SUFFIXES");
255       Set<String> suffiexesSet = null;
256       if (suffixes != null){
257         suffiexesSet = new HashSet<String>();
258         StringTokenizer strTokenizer = new StringTokenizer(suffixes,";");
259         while(strTokenizer.hasMoreTokens()){
260            suffiexesSet.add(strTokenizer.nextToken());
261         }// End while
262       }// End if
263       currentParam.suffixes = suffiexesSet;
264     }else if(elementName.toUpperCase().equals("GUI")){
265       String typeValue = currentAttributes.getValue("TYPE");
266       if (typeValue != null){
267         if (typeValue.toUpperCase().equals("LARGE"))
268           resourceData.setGuiType(ResourceData.LARGE_GUI);
269         if (typeValue.toUpperCase().equals("SMALL"))
270           resourceData.setGuiType(ResourceData.SMALL_GUI);
271       }// End if
272     }// End if
273 
274     // if there are any parameters awaiting addition to the list, add them
275     // (note that they're not disjunctive or previous "/OR" would have got 'em)
276     if(elementName.toUpperCase().equals("OR")) {
277       if(! currentParamDisjunction.isEmpty()) {
278         currentParamList.addAll(currentFlattenedDisjunction());
279         currentParamDisjunction.clear();
280       }// End if
281     }// End if
282   // startElement()
283 
284   /** Utility function to throw exceptions on stack errors. */
285   private void checkStack(String methodName, String elementName)
286   throws GateSaxException {
287     if(contentStack.isEmpty())
288       throw new GateSaxException(
289         methodName + " called for element " + elementName + " with empty stack"
290       );
291   // checkStack
292 
293   /** Called when the SAX parser encounts the end of an XML element.
294     * This is where ResourceData objects get values set, and where
295     * they are added to the CreoleRegister when we parsed their complete
296     * metadata entries.
297     */
298   @Override
299   public void endElement (String uri, String qName, String elementName)
300                                                     throws GateSaxException, SAXException {
301     // call characterActions
302     if(readCharacterStatus) {
303       readCharacterStatus = false;
304       charactersAction(new String(contentBuffer).toCharArray(),0,contentBuffer.length());
305     }
306 
307     if(DEBUGOut.prln("endElement: " + elementName);
308 
309     //////////////////////////////////////////////////////////////////
310     if(elementName.toUpperCase().equals("RESOURCE")) {
311       // check for validity of the resource data
312       if(! resourceData.isValid())
313         throw new GateSaxException(
314           "Invalid resource data: " + resourceData.getValidityMessage()
315         );
316 
317       //set the URL to the creole.xml file on the resource data object
318       resourceData.setXmlFileUrl(creoleFileUrl);
319       // add the new resource data object to the creole register
320       register.put(resourceData.getClassName(), resourceData);
321       // if the resource is auto-loading, try and load it
322       if(resourceData.isAutoLoading())
323         try {
324           @SuppressWarnings("unused")
325           Class<?> resClass = resourceData.getResourceClass();
326 //          Resource res = Factory.createResource(
327 //              resourceData.getClassName(), Factory.newFeatureMap()
328 //          );
329 //          resourceData.makeInstantiationPersistant(res);
330         catch(ClassNotFoundException e) {
331           throw new GateSaxException(
332             "Couldn't load autoloading resource: " +
333             resourceData.getName() "; problem was: " + e
334           );
335         }// End try
336 
337       // if there are any parameters awaiting addition to the list, add them
338       // (note that they're not disjunctive or the "/OR" would have got them)
339       if(! currentParamDisjunction.isEmpty()) {
340         currentParamList.addAll(currentFlattenedDisjunction());
341         currentParamDisjunction.clear();
342       }// End if
343 
344       // add the parameter list to the resource (and reinitialise it)
345       resourceData.setParameterList(currentParamList);
346       currentParamList = new ParameterList();
347       
348       // final initialization of the ResourceData
349       try {
350         resourceData.init();
351       catch(Exception ex) {
352         throw new GateSaxException(
353             "Couldn't initialize ResourceData for "
354             + resourceData.getName(), ex);
355       }
356 
357       if(DEBUGOut.println("added: " + resourceData);
358       // Iterate through autoinstances and try to instanciate them
359       if currentAutoinstances != null && !currentAutoinstances.isEmpty()){
360         ResourceData rd = Gate.getCreoleRegister().get(resourceData.getClassName());
361         ParameterList existingParameters = null;
362         if (rd.getReferenceCount() 1) {
363           // we aren't going to redefine a resource but we do need to use the
364           // parameters from the new instance so we get the right base URL and
365           // default values etc.
366           existingParameters = rd.getParameterList();
367           rd.setParameterList(resourceData.getParameterList());
368         }
369         
370         try {
371           Iterator<FeatureMap> iter = currentAutoinstances.iterator();
372           while (iter.hasNext()){
373             FeatureMap autoinstanceParams = iter.next();
374             iter.remove();
375             FeatureMap autoinstanceFeatures = null;
376             //if the hidden attribute was set in the parameters, create a feature 
377             //map and move the hidden attribute there.
378             if(Gate.getHiddenAttribute(autoinstanceParams)){
379               autoinstanceFeatures = Factory.newFeatureMap();
380               Gate.setHiddenAttribute(autoinstanceFeatures, true);
381               autoinstanceParams.remove(GateConstants.HIDDEN_FEATURE_KEY);
382             }
383             
384             // Try to create the resource.
385             try {
386               // Resource res = 
387               Factory.createResource(
388                                 resourceData.getClassName(), autoinstanceParams, 
389                                 autoinstanceFeatures);
390               //resourceData.makeInstantiationPersistant(res);
391               // all resource instantiations are persistent
392             catch(ResourceInstantiationException e) {
393               throw new GateSaxException(
394                 "Couldn't auto-instantiate resource: " +
395                 resourceData.getName() "; problem was: " + e
396               );
397             }// End try
398           }// End while
399         }
400         finally {
401           // if we played around with the parameters of an already loaded
402           // resource then put them back before we break something
403           if (existingParameters != null)
404             rd.setParameterList(existingParameters);
405         }
406       }// End if
407       currentAutoinstances = null;
408     // End RESOURCE processing
409     //////////////////////////////////////////////////////////////////
410     else if(elementName.toUpperCase().equals("AUTOINSTANCE"||
411             elementName.toUpperCase().equals("HIDDEN-AUTOINSTANCE")) {
412       //checkStack("endElement", "AUTOINSTANCE");
413       // Cache the auto-instance into the autoins
414       if (currentAutoinstanceParams != null)
415         currentAutoinstances.add(currentAutoinstanceParams);
416     // End AUTOINSTANCE processing
417     //////////////////////////////////////////////////////////////////
418     else if(elementName.toUpperCase().equals("PARAM")) {
419     // End PARAM processing
420     //////////////////////////////////////////////////////////////////
421     else if(elementName.toUpperCase().equals("NAME")) {
422       checkStack("endElement""NAME");
423       resourceData.setName(contentStack.pop());
424     // End NAME processing
425     //////////////////////////////////////////////////////////////////
426     else if (elementName.toUpperCase().equals("IVY")) {
427       if (!contentStack.isEmpty()) contentStack.pop();
428     // End IVY processing
429     //////////////////////////////////////////////////////////////////
430     else if (elementName.toUpperCase().equals("REQUIRES")) {
431       if (!contentStack.isEmpty()) contentStack.pop();
432     // End REQUIRES processing
433     //////////////////////////////////////////////////////////////////
434     else if(elementName.toUpperCase().equals("JAR")) {
435       checkStack("endElement""JAR");
436 
437       // add jar file name
438       String jarFileName = contentStack.pop();
439       if(resourceData != null) {
440         resourceData.setJarFileName(jarFileName);
441       }
442 
443       // add jar file URL if there is one
444       if(sourceUrl != null) {
445         String sourceUrlName = sourceUrl.toExternalForm();
446         String separator = "/";
447 
448         if(sourceUrlName.endsWith(separator))
449           separator = "";
450         URL jarFileUrl = null;
451 
452         try {
453           jarFileUrl = new URL(sourceUrlName + separator + jarFileName);
454           if(resourceData != null) {
455             resourceData.setJarFileUrl(jarFileUrl);
456           }
457 
458           // We no longer need to add the jar URL to the class loader, as this
459           // is done before the SAX parse
460         catch(MalformedURLException e) {
461           throw new GateSaxException("bad URL " + jarFileUrl + e);
462         }// End try
463       }// End if
464     // End JAR processing
465     //////////////////////////////////////////////////////////////////
466     else if(elementName.toUpperCase().equals("CLASS")) {
467       checkStack("endElement""CLASS");
468       resourceData.setClassName(contentStack.pop());
469     // End CLASS processing
470     //////////////////////////////////////////////////////////////////
471     else if(elementName.toUpperCase().equals("COMMENT")) {
472       checkStack("endElement""COMMENT");
473       resourceData.setComment(contentStack.pop());
474     // End COMMENT processing
475     //////////////////////////////////////////////////////////////////
476     else if(elementName.toUpperCase().equals("HELPURL")) {
477       checkStack("endElement""HELPURL");
478       resourceData.setHelpURL(contentStack.pop());
479     // End HELPURL processing
480     //////////////////////////////////////////////////////////////////
481     else if(elementName.toUpperCase().equals("INTERFACE")) {
482       checkStack("endElement""INTERFACE");
483       resourceData.setInterfaceName(contentStack.pop());
484     // End INTERFACE processing
485     //////////////////////////////////////////////////////////////////
486     else if(elementName.toUpperCase().equals("ICON")) {
487       checkStack("endElement""ICON");
488       resourceData.setIcon(contentStack.pop());
489     // End ICON processing
490     //////////////////////////////////////////////////////////////////
491     else if(elementName.toUpperCase().equals("OR")) {
492       currentParamList.add(currentFlattenedDisjunction());
493       currentParamDisjunction.clear();
494     // End OR processing
495     //////////////////////////////////////////////////////////////////
496     else if(elementName.toUpperCase().equals("PARAMETER")) {
497       checkStack("endElement""PARAMETER");
498       currentParam.typeName = contentStack.pop();
499       String priorityStr = currentAttributes.getValue("PRIORITY");
500       // if no priority specified, assume lowest (i.e. parameters with an
501       // explicit priority come ahead of those without).
502       Integer priority = Integer.MAX_VALUE;
503       try {
504         if(priorityStr != nullpriority = Integer.valueOf(priorityStr);
505       }
506       catch(NumberFormatException nfe) {
507         throw new GateRuntimeException ("Found in creole.xml a PARAM element" +
508                 " for resource "+ resourceData.getClassName()" with a non-numeric"+
509                 " PRIORITY attribute. Check the file and try again.");
510       }
511       List<Parameter> paramList = currentParamDisjunction.get(priority);
512       if(paramList == null) {
513         paramList = new ArrayList<Parameter>();
514         currentParamDisjunction.put(priority, paramList);
515       }
516       paramList.add(currentParam);
517       if(DEBUG)
518         Out.prln("added param: " + currentParam);
519       currentParam = new Parameter(creoleFileUrl);
520     // End PARAMETER processing
521     //////////////////////////////////////////////////////////////////
522     else if(elementName.toUpperCase().equals("AUTOLOAD")) {
523       resourceData.setAutoLoading(true);
524     // End AUTOLOAD processing
525     //////////////////////////////////////////////////////////////////
526     else if(elementName.toUpperCase().equals("PRIVATE")) {
527       resourceData.setPrivate(true);
528     // End PRIVATE processing
529     //////////////////////////////////////////////////////////////////
530     else if(elementName.toUpperCase().equals("TOOL")) {
531       resourceData.setTool(true);
532     // End TOOL processing
533     //////////////////////////////////////////////////////////////////
534     else if(elementName.toUpperCase().equals("MAIN_VIEWER")) {
535       resourceData.setIsMainView(true);
536     // End MAIN_VIEWER processing
537     //////////////////////////////////////////////////////////////////
538     else if(elementName.toUpperCase().equals("RESOURCE_DISPLAYED")){
539       checkStack("endElement""RESOURCE_DISPLAYED");
540       String resourceDisplayed = contentStack.pop();
541       resourceData.setResourceDisplayed(resourceDisplayed);
542       try{
543         @SuppressWarnings("unused")
544         Class<?> resourceDisplayedClass = Gate.getClassLoader().
545                                  loadClass(resourceDisplayed);
546       catch (ClassNotFoundException ex){
547         throw new GateRuntimeException(
548           "Couldn't get resource class from the resource name :" +
549           resourceDisplayed + " " +ex );
550       }// End try
551     // End RESOURCE_DISPLAYED processing
552     //////////////////////////////////////////////////////////////////
553     else if(elementName.toUpperCase().equals("ANNOTATION_TYPE_DISPLAYED")){
554       checkStack("endElement""ANNOTATION_TYPE_DISPLAYED");
555       resourceData.setAnnotationTypeDisplayed(contentStack.pop());
556     // End ANNOTATION_TYPE_DISPLAYED processing
557     //////////////////////////////////////////////////////////////////
558     else if(elementName.toUpperCase().equals("GUI")) {
559 
560     // End GUI processing
561     //////////////////////////////////////////////////////////////////
562     else if(elementName.toUpperCase().equals("CREOLE")) {
563     // End CREOLE processing
564     //////////////////////////////////////////////////////////////////
565     else if(elementName.toUpperCase().equals("CREOLE-DIRECTORY")) {
566     // End CREOLE-DIRECTORY processing
567     //////////////////////////////////////////////////////////////////
568     else // arbitrary elements get added as features of the resource data
569       if(resourceData != null)
570         resourceData.getFeatures().put(
571           elementName.toUpperCase(),
572           ((contentStack.isEmpty()) null : contentStack.pop())
573         );
574     }
575     //////////////////////////////////////////////////////////////////
576 
577   // endElement
578 
579   /** Called when the SAX parser encounts text (PCDATA) in the XML doc */
580   @Override
581   public void characters(char [] text,int start,int lengththrows SAXException {
582     if(!readCharacterStatus) {
583       contentBuffer = new StringBuffer(new String(text,start,length));
584     else {
585       contentBuffer.append(new String(text,start,length));
586     }
587     readCharacterStatus = true;
588   }
589 
590   /**
591    * This method is called when all characters between specific tags have been read completely
592    */
593   public void charactersAction(char[] text, int start, int length)
594   throws SAXException {
595     // Get the trimmed text between elements
596     String content = new String(text, start, length).trim();
597     // If the entire text is empty or is made from whitespaces then we simply
598     // return
599     if (content.length() == 0return;
600     contentStack.push(content);
601     if(DEBUGOut.println(content);
602   // characters
603   
604   /**
605    * Flatten the currentParamDisjunction map into a single list
606    * ordered by priority.
607    */
608   protected List<Parameter> currentFlattenedDisjunction() {
609     List<Parameter> listToReturn = new ArrayList<Parameter>();
610     for(List<Parameter> l : currentParamDisjunction.values()) {
611       listToReturn.addAll(l);
612     }
613     return listToReturn;
614   }
615 
616   /** Called when the SAX parser encounts white space */
617   @Override
618   public void ignorableWhitespace(char ch[]int start, int length)
619   throws SAXException {
620   // ignorableWhitespace
621 
622   /** Called for parse errors. */
623   @Override
624   public void error(SAXParseException exthrows SAXException {
625     _seh.error(ex);
626   // error
627 
628   /** Called for fatal errors. */
629   @Override
630   public void fatalError(SAXParseException exthrows SAXException {
631     _seh.fatalError(ex);
632   // fatalError
633 
634   /** Called for warnings. */
635   @Override
636   public void warning(SAXParseException exthrows SAXException {
637     _seh.warning(ex);
638   // warning
639 
640 // CreoleXmlHandler