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