1   /*
2    *  ConfigXmlHandler.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, 9/Nov/2000
12   *
13   *  $Id: ConfigXmlHandler.java,v 1.11 2001/11/08 14:46:58 hamish Exp $
14   */
15  
16  package gate.config;
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  
25  import gate.*;
26  import gate.util.*;
27  import gate.xml.SimpleErrorHandler;
28  
29  ////// rem later?
30  import gate.creole.*;
31  
32  
33  /** This is a SAX handler for processing <CODE>gate.xml</CODE> files.
34    */
35  public class ConfigXmlHandler 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 SystemData systemData;
46  
47    /** The current element's attribute list */
48    private Attributes currentAttributes;
49  
50    /** A feature map representation of the current element's attribute list */
51    private FeatureMap currentAttributeMap;
52  
53    /** Debug flag */
54    private static final boolean DEBUG = false;
55  
56    /** The source URL of the config file being parsed. */
57    private URL sourceUrl;
58  
59    /** This object indicates what to do when the parser encounts an error*/
60    private SimpleErrorHandler _seh = new SimpleErrorHandler();
61  
62    /** Construction */
63    public ConfigXmlHandler(URL configUrl) {
64      this.register = Gate.getCreoleRegister();
65      this.sourceUrl = configUrl;
66    } // construction
67  
68    /** The register object that we add CREOLE directories to during parsing.
69      */
70    private CreoleRegister register;
71  
72    /** Called when the SAX parser encounts the beginning of the XML document */
73    public void startDocument() throws GateSaxException {
74      if(DEBUG) Out.prln("start document");
75    } // startDocument
76  
77    /** Called when the SAX parser encounts the end of the XML document */
78    public void endDocument() throws GateSaxException {
79      if(DEBUG) Out.prln("end document");
80      if(! contentStack.isEmpty()) {
81        StringBuffer errorMessage =
82          new StringBuffer("document ended but element stack not empty:");
83        while(! contentStack.isEmpty())
84          errorMessage.append(Strings.getNl()+"  "+(String) contentStack.pop());
85        throw new GateSaxException(errorMessage.toString());
86      }
87    } // endDocument
88  
89    /** A verbose method for Attributes*/
90    private String attributes2String(Attributes atts){
91      StringBuffer strBuf = new StringBuffer("");
92      if (atts == null) return strBuf.toString();
93      for (int i = 0; i < atts.getLength(); i++) {
94       String attName  = atts.getQName(i);
95       String attValue = atts.getValue(i);
96       strBuf.append(" ");
97       strBuf.append(attName);
98       strBuf.append("=");
99       strBuf.append(attValue);
100     }// End for
101     return strBuf.toString();
102   }// attributes2String()
103 
104   /** Called when the SAX parser encounts the beginning of an XML element */
105   public void startElement (
106     String uri, String qName, String elementName, Attributes atts
107   ) {
108     if(DEBUG) {
109       Out.pr("startElement: ");
110       Out.println(
111         elementName + " " +
112         attributes2String(atts)
113       );
114     }
115 
116     // record the attributes of this element for endElement()
117     currentAttributes = atts;
118     currentAttributeMap = attributeListToParameterList();
119 
120     // if it's a SYSTEM, create a new one and set its name
121     if(elementName.toUpperCase().equals("SYSTEM")) {
122       systemData = new SystemData();
123       for(int i=0, len=currentAttributes.getLength(); i<len; i++) {
124         if(currentAttributes.getQName(i).toUpperCase().equals("NAME"))
125           systemData.systemName = currentAttributes.getValue(i);
126       }
127     } else if(elementName.toUpperCase().equals("DBCONFIG")) {
128       DataStoreRegister.addConfig(currentAttributeMap);
129     } else if(elementName.toUpperCase().equals(Gate.getUserConfigElement())) {
130       Gate.getUserConfig().putAll(currentAttributeMap);
131     }
132 
133   } // startElement
134 
135   /** Utility function to throw exceptions on stack errors. */
136   private void checkStack(String methodName, String elementName)
137   throws GateSaxException {
138     if(contentStack.isEmpty())
139       throw new GateSaxException(
140         methodName + " called for element " + elementName + " with empty stack"
141       );
142   } // checkStack
143 
144   /** Called when the SAX parser encounts the end of an XML element.
145     * This is actions happen.
146     */
147   public void endElement (String uri, String qName, String elementName)
148                                                       throws GateSaxException {
149     if(DEBUG) Out.prln("endElement: " + elementName);
150 
151     //////////////////////////////////////////////////////////////////
152     if(elementName.toUpperCase().equals("GATE")) {
153 
154     //////////////////////////////////////////////////////////////////
155     } else if(elementName.toUpperCase().equals("CREOLE-DIRECTORY")) {
156       String dirUrlName = (String) contentStack.pop();
157       try {
158         register.addDirectory(new URL(dirUrlName));
159       } catch(MalformedURLException e) {
160         throw new GateSaxException("bad URL " + dirUrlName + e);
161       }
162 
163     //////////////////////////////////////////////////////////////////
164     } else if(elementName.toUpperCase().equals("SYSTEM")) {
165 // check we got correct params on systemData?
166       systemData.createSystem();
167 
168     //////////////////////////////////////////////////////////////////
169     } else if(elementName.toUpperCase().equals("CONTROLLER")) {
170       systemData.controllerTypeName = (String) contentStack.pop();
171 
172     //////////////////////////////////////////////////////////////////
173     } else if(elementName.toUpperCase().equals("LR")) {
174       // create an LR and add it to the SystemData
175       createResource((String) contentStack.pop(), systemData.lrList);
176 
177     //////////////////////////////////////////////////////////////////
178     } else if(elementName.toUpperCase().equals("PR")) {
179       // create a PR and add it to the SystemData
180       createResource((String) contentStack.pop(), systemData.prList);
181 
182     //////////////////////////////////////////////////////////////////
183     } else if(elementName.toUpperCase().equals("DBCONFIG")) {
184       // these are empty elements with attributes; nothing to do here
185 
186     //////////////////////////////////////////////////////////////////
187     }else if(elementName.toUpperCase().equals("GATECONFIG")) {
188       // these are empty elements with attributes; nothing to do here
189 
190     //////////////////////////////////////////////////////////////////
191     } else {
192       throw new GateSaxException(
193         "Unknown config data element: " + elementName +
194         "; encountered while parsing " + sourceUrl
195       );
196     }
197     //////////////////////////////////////////////////////////////////
198 
199   } // endElement
200 
201   /** Called when the SAX parser encounts text (PCDATA) in the XML doc */
202   public void characters(char[] text, int start, int length)
203   throws SAXException {
204     // Get the trimmed text between elements
205     String content = new String(text, start, length).trim();
206     // If the entire text is empty or is made from whitespaces then we simply
207     // return
208     if (content.length() == 0) return;
209     contentStack.push(content);
210     if(DEBUG) Out.println(content);
211   } // characters
212 
213   /** Utility method to create a resource and add to appropriate list.
214    *  Parameters for the resource are pulled out of the current attribute
215    *  list.
216    */
217   protected void createResource(String resourceTypeName, List resourceList)
218   throws GateSaxException
219   {
220     if(DEBUG) Out.prln(resourceTypeName + ": " + currentAttributeMap);
221     try {
222       resourceList.add(
223         Factory.createResource(
224           resourceTypeName, currentAttributeMap
225         )
226       );
227     } catch(ResourceInstantiationException e) {
228       throw new GateSaxException(
229         "Couldn't create resource for SYSTEM: " +
230         systemData.systemName + "; problem was: " + Strings.getNl() + e
231       );
232     }
233   } // createResource
234 
235   /** Utility method to convert the current SAX attribute list to a
236    *  FeatureMap
237    */
238   protected FeatureMap attributeListToParameterList() {
239     FeatureMap params = Factory.newFeatureMap();
240 
241     // for each attribute of this element, add it to the param list
242     for(int i=0, len=currentAttributes.getLength(); i<len; i++) {
243       params.put(
244         currentAttributes.getQName(i), currentAttributes.getValue(i)
245       );
246     }
247 
248     return params;
249   } // attributeListToParameterList
250 
251   /** Called when the SAX parser encounts white space */
252   public void ignorableWhitespace(char ch[], int start, int length)
253   throws SAXException {
254   } // ignorableWhitespace
255 
256   /** Called for parse errors. */
257   public void error(SAXParseException ex) throws SAXException {
258     _seh.error(ex);
259   } // error
260 
261   /** Called for fatal errors. */
262   public void fatalError(SAXParseException ex) throws SAXException {
263     _seh.fatalError(ex);
264   } // fatalError
265 
266   /** Called for warnings. */
267   public void warning(SAXParseException ex) throws SAXException {
268     _seh.warning(ex);
269   } // warning
270 
271 } // ConfigXmlHandler
272