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