1   /*
2    *  AnnotationSchema.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   *  Cristian URSU, 27/Sept/2000
12   *
13   *  $Id: AnnotationSchema.java,v 1.19 2002/03/06 17:15:39 kalina Exp $
14   */
15  package gate.creole;
16  
17  import java.util.*;
18  import java.net.*;
19  import java.io.*;
20  
21  import gate.util.*;
22  import gate.*;
23  
24  import org.xml.sax.*;
25  import javax.xml.parsers.*;
26  import org.jdom.input.*;
27  import org.jdom.*;
28  
29  /** This class handles annotation schemas.An annotation schema is a
30    * representation of an annotation, together with its types and their
31    * attributes, values and types.
32    */
33  public class AnnotationSchema extends AbstractLanguageResource{
34    public static final String FILE_URL_PARAM_NAME = "xmlFileUrl";
35  
36    /** Debug flag */
37    private static final boolean DEBUG = false;
38  
39    /** A map between XSchema types and Java Types */
40    private static Map xSchema2JavaMap;
41  
42    /** A map between JAva types and XSchema */
43    private static Map java2xSchemaMap;
44  
45    /** Parser for the XSchema source files */
46    private static DocumentBuilder xmlParser;
47  
48    /** This sets up two Maps between XSchema types and their coresponding
49      * Java types + a DOM xml parser
50      */
51    private static void setUpStaticData()
52    throws ResourceInstantiationException
53    {
54      xSchema2JavaMap = new HashMap();
55      java2xSchemaMap = new HashMap();
56  
57      xSchema2JavaMap.put("string",   String.class.getName());
58      xSchema2JavaMap.put("integer",  Integer.class.getName());
59      xSchema2JavaMap.put("int",      Integer.class.getName() );
60      xSchema2JavaMap.put("boolean",  Boolean.class.getName());
61      xSchema2JavaMap.put("float",    Float.class.getName());
62      xSchema2JavaMap.put("double",   Double.class.getName());
63      xSchema2JavaMap.put("short",    Short.class.getName());
64      xSchema2JavaMap.put("byte",     Byte.class.getName());
65  
66      java2xSchemaMap.put(String.class.getName(),   "string");
67      java2xSchemaMap.put(Integer.class.getName(),  "integer");
68      java2xSchemaMap.put(Boolean.class.getName(),  "boolean");
69      java2xSchemaMap.put(Float.class.getName(),    "float");
70      java2xSchemaMap.put(Double.class.getName(),   "double");
71      java2xSchemaMap.put(Short.class.getName(),    "short");
72      java2xSchemaMap.put(Byte.class.getName(),     "byte");
73  
74      // Get an XML parser
75      try {
76        // Get a parser factory.
77        DocumentBuilderFactory domBuilderFactory =
78                                            DocumentBuilderFactory.newInstance();
79        // Set up the factory to create the appropriate type of parser
80        // A non validating one
81        domBuilderFactory.setValidating(false);
82        // A non namesapace aware one
83        domBuilderFactory.setNamespaceAware(true);
84  
85        // Create the DOM parser
86        xmlParser = domBuilderFactory.newDocumentBuilder();
87  
88      } catch(ParserConfigurationException e) {
89        throw new ResourceInstantiationException(
90          "Couldn't create annotation schema parser: " + e
91        );
92      }//End try
93    } //setUpStaticData
94  
95    /** The name of the annotation */
96    protected String annotationName = null;
97  
98    /** Returns the value of annotation name */
99    public String getAnnotationName(){
100     return annotationName;
101   } // getAnnotationName
102 
103   /** Sets the annotation name */
104   public void setAnnotationName(String annotationName) {
105     this.annotationName = annotationName;
106   } // setAnnotationName
107 
108   /** Schemas for the attributes */
109   protected Set featureSchemaSet = null;
110 
111   /** Constructs an annotation schema. */
112   public AnnotationSchema(){
113   } // AnnotationSchema
114 
115   /** Returns the feature schema set */
116   public Set getFeatureSchemaSet(){
117     return featureSchemaSet;
118   } // getAttributeSchemas
119 
120   /** Sets the feature schema set */
121   public void setFeatureSchemaSet(Set featureSchemaSet) {
122     this.featureSchemaSet = featureSchemaSet;
123   } // setFeatureSchemaSet
124 
125   /** @return a FeatureSchema object from featureSchemaSet, given a
126     * feature name.It will return null if the feature name is not found.
127     */
128   public FeatureSchema getFeatureSchema(String featureName) {
129     Iterator fsIterator = featureSchemaSet.iterator();
130     while (fsIterator.hasNext()) {
131       FeatureSchema fs = (FeatureSchema) fsIterator.next();
132       if (fs.getFeatureName().equals(featureName) )
133         return fs;
134     }
135     return null;
136   } // getFeatureSchema
137 
138   /** Initialise this resource, and return it. If the schema XML source file
139     * URL has been set, it will construct itself from that file.
140     */
141   public Resource init() throws ResourceInstantiationException {
142     // set up the static data if it's not there already
143     if(xSchema2JavaMap == null || java2xSchemaMap == null || xmlParser == null)
144       setUpStaticData();
145 
146     // parse the XML file if we have its URL
147     if(xmlFileUrl != null)
148       fromXSchema(xmlFileUrl);
149 
150     return this;
151   } // init()
152 
153   /** The xml file URL of the resource */
154   protected URL xmlFileUrl;
155 
156   /** Set method for the resource xml file URL */
157   public void setXmlFileUrl(URL xmlFileUrl) { this.xmlFileUrl = xmlFileUrl; }
158 
159   /** Get method for the resource xml file URL */
160   public URL getXmlFileUrl() { return xmlFileUrl; }
161 
162   /** Creates an AnnotationSchema object from an XSchema file
163     * @param anXSchemaURL the URL where to find the XSchema file
164     */
165   public void fromXSchema(URL anXSchemaURL)
166   throws ResourceInstantiationException {
167     try {
168       // Parse the document and create the DOM structure
169       org.w3c.dom.Document dom =
170                 xmlParser.parse(anXSchemaURL.toString());
171       org.jdom.Document jDom = buildJdomFromDom(dom);
172       // don't need dom anymore
173       dom = null;
174       // Use JDOM
175       workWithJDom(jDom);
176     } catch (SAXException e){
177       throw new ResourceInstantiationException(
178         "couldn't parse annotation schema file: " + e
179       );
180     } catch (IOException e) {
181       throw new ResourceInstantiationException(
182         "couldn't open annotation schema file: " + e
183       );
184     }// End try
185   } // fromXSchema
186 
187   /** Creates an AnnotationSchema object from an XSchema file
188     * @param anXSchemaInputStream the Input Stream containing the XSchema file
189     */
190   public void fromXSchema(InputStream anXSchemaInputStream)
191   throws ResourceInstantiationException
192   {
193     try {
194       // Parse the document and create the DOM structure
195       org.w3c.dom.Document dom =
196                 xmlParser.parse(anXSchemaInputStream);
197       org.jdom.Document jDom = buildJdomFromDom(dom);
198       // don't need dom anymore
199       dom = null;
200       // Use JDOM
201       workWithJDom(jDom);
202     } catch (SAXException e){
203       throw new ResourceInstantiationException(
204         "couldn't parse annotation schema stream: " + e
205       );
206     } catch (IOException e) {
207       throw new ResourceInstantiationException(
208         "couldn't open annotation schema stream: " + e
209       );
210     }// End try
211   } // end fromXSchema
212 
213   /** This method builds a JDom structure from a W3C Dom one.Of course that can
214     * be considered a waist of time, but a JDOM structure is more flexible than
215     * a DOM one.
216     * @param aDom W3C dom structure
217     * @return {@link  org.jdom.Document}
218     */
219   private org.jdom.Document buildJdomFromDom(org.w3c.dom.Document aDom){
220     org.jdom.Document jDom = null;
221     // Create a new jDOM BUILDER
222     DOMBuilder jDomBuilder = new DOMBuilder();
223     // Create a JDOM structure from the dom one
224     jDom = jDomBuilder.build(aDom);
225     // Don't need dom anymore, but we don't decide that here.
226     return jDom;
227   } // buildJdomFromDom
228 
229   /** This method uses the JDom structure for our XSchema needs. What it does is
230     * to add semantics to the XML elements defined in XSchema. In the end we need
231     * to construct an AnnotationSchema object form an XSchema file.
232     *
233     * @param jDom the JDOM structure containing the XSchema document. It must not
234     * be <b>null<b>
235     */
236   private void workWithJDom(org.jdom.Document jDom){
237     // Use the jDom structure the way we want
238     org.jdom.Element rootElement = jDom.getRootElement();
239     // get all children elements from the rootElement
240     List rootElementChildrenList = rootElement.getChildren("element");
241     Iterator rootElementChildrenIterator = rootElementChildrenList.iterator();
242     while (rootElementChildrenIterator.hasNext()){
243       org.jdom.Element childElement =
244                         (org.jdom.Element) rootElementChildrenIterator.next();
245       createAnnotationSchemaObject(childElement);
246     }//end while
247   } // workWithJdom
248 
249   /** This method creates an AnnotationSchema object fom an org.jdom.Element
250     * @param anElement is an XSchema element element
251     */
252   private void createAnnotationSchemaObject(org.jdom.Element anElement){
253     // Get the value of the name attribute. If this attribute doesn't exists
254     // then it will receive a default one.
255     annotationName = anElement.getAttributeValue("name");
256     if (annotationName == null)
257         annotationName = "UnknownElement";
258     // See if this element has a complexType element inside it
259     org.jdom.Element complexTypeElement = anElement.getChild("complexType");
260     if (complexTypeElement != null){
261       List complexTypeCildrenList = complexTypeElement.getChildren("attribute");
262       Iterator complexTypeCildrenIterator = complexTypeCildrenList.iterator();
263       if (complexTypeCildrenIterator.hasNext())
264         featureSchemaSet = new HashSet();
265       while (complexTypeCildrenIterator.hasNext()) {
266         org.jdom.Element childElement =
267                     (org.jdom.Element) complexTypeCildrenIterator.next();
268         createAndAddFeatureSchemaObject(childElement);
269       }// end while
270     }// end if
271   } // createAnnoatationSchemaObject
272 
273   /** This method creates and adds a FeatureSchema object to the current
274     * AnnotationSchema one.
275     * @param anElement is an XSchema attribute element
276     */
277   public void createAndAddFeatureSchemaObject(org.jdom.Element
278                                                           anAttributeElement) {
279     String featureName = null;
280     String featureType = null;
281     String featureUse  = null;
282     String featureValue = null;
283     Set    featurePermissibleValuesSet = null;
284 
285     // Get the value of the name attribute. If this attribute doesn't exists
286     // then it will receive a default one.
287     featureName = anAttributeElement.getAttributeValue("name");
288     if (featureName == null)
289       featureName = "UnknownFeature";
290 
291     // See if it has a type attribute associated
292     featureType = anAttributeElement.getAttributeValue("type");
293     if (featureType != null)
294       // Set it to the corresponding Java type
295       featureType = (String) xSchema2JavaMap.get(featureType);
296 
297     // Get the value of use attribute
298     featureUse = anAttributeElement.getAttributeValue("use");
299     if (featureUse == null)
300       // Set it to the default value
301       featureUse = "optional";
302 
303     // Get the value of value attribute
304     featureValue = anAttributeElement.getAttributeValue("value");
305     if (featureValue == null)
306       featureValue = "";
307 
308     // Let's check if it has a simpleType element inside
309     org.jdom.Element simpleTypeElement  =
310                                   anAttributeElement.getChild("simpleType");
311 
312     // If it has (!= null) then check to see if it has a restrictionElement
313     if (simpleTypeElement != null) {
314       org.jdom.Element restrictionElement =
315                               simpleTypeElement.getChild("restriction");
316       if (restrictionElement != null) {
317         // Get the type attribute for restriction element
318         featureType = restrictionElement.getAttributeValue("base");
319 
320         // Check to see if that attribute was present. getAttributeValue will
321         // return null if it wasn't present
322         if (featureType == null)
323           // If it wasn't present then set it to default type (string)
324           featureType =  (String) xSchema2JavaMap.get("string");
325         else
326           // Set it to the corresponding Java type
327           featureType = (String) xSchema2JavaMap.get(featureType);
328 
329         // Check to see if there are any enumeration elements inside
330         List enumerationElementChildrenList =
331                                  restrictionElement.getChildren("enumeration");
332         Iterator enumerationChildrenIterator =
333                                 enumerationElementChildrenList.iterator();
334 
335         // Check if there is any enumeration element in the list
336         if (enumerationChildrenIterator.hasNext())
337             featurePermissibleValuesSet = new HashSet();
338         while (enumerationChildrenIterator.hasNext()) {
339           org.jdom.Element enumerationElement =
340                         (org.jdom.Element) enumerationChildrenIterator.next();
341           String permissibleValue =
342                             enumerationElement.getAttributeValue("value");
343           // Add that value to the featureSchema possible values set.
344           featurePermissibleValuesSet.add(permissibleValue);
345         }// end while
346       }// end if( restrictionElement != null)
347     }// end if (simpleTypeElement != null)
348 
349     // If it doesn't have a simpleTypeElement inside and featureType is null or
350     // it wasn't recognised, then we set the default type to string.
351     if (simpleTypeElement == null && featureType == null )
352       featureType =  (String) xSchema2JavaMap.get("string");
353 
354     // Create an add a featureSchema object
355     FeatureSchema featureSchema = new FeatureSchema(
356                                                    featureName,
357                                                    featureType,
358                                                    featureValue,
359                                                    featureUse,
360                                                    featurePermissibleValuesSet);
361     featureSchemaSet.add(featureSchema);
362   } // createAndAddFeatureSchemaObject
363 
364   /** @return a String containing the XSchema document representing
365     *  an AnnotationSchema object.
366     */
367   public String toXSchema(){
368     StringBuffer schemaString = new StringBuffer();
369     schemaString.append("<?xml version=\"1.0\"?>\n" +
370                    "<schema xmlns=\"http://www.w3.org/2000/10/XMLSchema\">\n"+
371                    " <element name=\"" + annotationName + "\"");
372 
373     if (featureSchemaSet == null)
374       schemaString.append("/>\n");
375     else {
376       schemaString.append(">\n  <complexType>\n");
377       Iterator featureSchemaSetIterator = featureSchemaSet.iterator();
378       while (featureSchemaSetIterator.hasNext()){
379         FeatureSchema fs = (FeatureSchema) featureSchemaSetIterator.next();
380         schemaString.append("   " + fs.toXSchema(java2xSchemaMap));
381       }// end while
382       schemaString.append("  </complexType>\n");
383       schemaString.append(" </element>\n");
384     }// end if else
385     schemaString.append("</schema>\n");
386     return schemaString.toString();
387   }// toXSchema
388 } // AnnotationSchema
389 
390 
391