AnnotationSchema.java
001 /*
002  *  AnnotationSchema.java
003  *
004  *  Copyright (c) 1995-2012, 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  *  Cristian URSU, 27/Sept/2000
013  *
014  *  $Id: AnnotationSchema.java 19272 2016-05-05 08:51:53Z markagreenwood $
015  */
016 package gate.creole;
017 
018 import gate.Factory;
019 import gate.FeatureMap;
020 import gate.Resource;
021 import gate.creole.metadata.CreoleParameter;
022 import gate.creole.metadata.CreoleResource;
023 
024 import java.io.InputStream;
025 import java.net.URL;
026 import java.util.HashMap;
027 import java.util.HashSet;
028 import java.util.Iterator;
029 import java.util.LinkedHashSet;
030 import java.util.List;
031 import java.util.Map;
032 import java.util.Set;
033 
034 import org.jdom.JDOMException;
035 import org.jdom.Namespace;
036 import org.jdom.input.SAXBuilder;
037 
038 /** This class handles annotation schemas.An annotation schema is a
039   * representation of an annotation, together with its types and their
040   * attributes, values and types.
041   */
042 @CreoleResource(name="Annotation Schema", comment="An annotation type and its features.", helpURL="http://gate.ac.uk/userguide/sec:corpora:schemas")
043 public class AnnotationSchema extends AbstractLanguageResource{
044   
045   private static final long serialVersionUID = 3499202938128514949L;
046 
047   public static final String FILE_URL_PARAM_NAME = "xmlFileUrl";
048 
049   /** A map between XSchema types and Java Types */
050   private static Map<String, Class<?>> xSchema2JavaMap;
051 
052   /** A map between Java types and XSchema */
053   private static Map<Class<?>, String> java2xSchemaMap;
054 
055   /** This sets up two Maps between XSchema types and their corresponding
056     * Java types + a DOM xml parser
057     */
058   private static void setUpStaticData()
059   throws ResourceInstantiationException
060   {
061     xSchema2JavaMap = new HashMap<String, Class<?>>();
062     java2xSchemaMap = new HashMap<Class<?>, String>();
063 
064     xSchema2JavaMap.put("string",   String.class);
065     xSchema2JavaMap.put("integer",  Integer.class);
066     xSchema2JavaMap.put("int",      Integer.class);
067     xSchema2JavaMap.put("boolean",  Boolean.class);
068     xSchema2JavaMap.put("float",    Float.class);
069     xSchema2JavaMap.put("double",   Double.class);
070     xSchema2JavaMap.put("short",    Short.class);
071     xSchema2JavaMap.put("byte",     Byte.class);
072 
073     java2xSchemaMap.put(String.class,   "string");
074     java2xSchemaMap.put(Integer.class,  "integer");
075     java2xSchemaMap.put(Boolean.class,  "boolean");
076     java2xSchemaMap.put(Float.class,    "float");
077     java2xSchemaMap.put(Double.class,   "double");
078     java2xSchemaMap.put(Short.class,    "short");
079     java2xSchemaMap.put(Byte.class,     "byte");
080   //setUpStaticData
081 
082   /** The name of the annotation */
083   protected String annotationName = null;
084 
085   /** Returns the value of annotation name */
086   public String getAnnotationName(){
087     return annotationName;
088   // getAnnotationName
089 
090   /** Sets the annotation name */
091   public void setAnnotationName(String annotationName) {
092     this.annotationName = annotationName;
093   // setAnnotationName
094 
095   /** Schemas for the attributes */
096   protected Set<FeatureSchema> featureSchemaSet = null;
097 
098   /** Constructs an annotation schema. */
099   public AnnotationSchema(){
100   // AnnotationSchema
101 
102   /** Returns the feature schema set */
103   public Set<FeatureSchema> getFeatureSchemaSet(){
104     return featureSchemaSet;
105   // getAttributeSchemas
106 
107   /** Sets the feature schema set */
108   public void setFeatureSchemaSet(Set<FeatureSchema> featureSchemaSet) {
109     this.featureSchemaSet = featureSchemaSet;
110   // setFeatureSchemaSet
111 
112   /** @return a FeatureSchema object from featureSchemaSet, given a
113     * feature name.It will return null if the feature name is not found.
114     */
115   public FeatureSchema getFeatureSchema(String featureName) {
116     if(featureSchemaSet == nullreturn null;
117     for(FeatureSchema fs : featureSchemaSet){
118       if (fs.getFeatureName().equals(featureName)) return fs;      
119     }
120     return null;
121   // getFeatureSchema
122 
123   /** Initialise this resource, and return it. If the schema XML source file
124     * URL has been set, it will construct itself from that file.
125     */
126   @Override
127   public Resource init() throws ResourceInstantiationException {
128     // set up the static data if it's not there already
129     if(xSchema2JavaMap == null || java2xSchemaMap == null)
130       setUpStaticData();
131 
132     // parse the XML file if we have its URL
133     if(xmlFileUrl != null) {
134       fromXSchema(xmlFileUrl);
135 
136       if(annotationName == null) {
137         Factory.deleteResource(this);
138 
139         if(lastIncluded != nullreturn lastIncluded;
140 
141         throw new ResourceInstantiationException(
142                 "The specified XML Schema doesn't define any annotation types");
143       }
144     }
145 
146     return this;
147   // init()
148 
149   /** The xml file URL of the resource */
150   protected URL xmlFileUrl;
151   
152   private transient AnnotationSchema lastIncluded = null;
153 
154   /** Set method for the resource xml file URL */
155   @CreoleParameter(comment="The url to the definition file", suffixes="xml;xsd")
156   public void setXmlFileUrl(URL xmlFileUrl) { this.xmlFileUrl = xmlFileUrl; }
157 
158   /** Get method for the resource xml file URL */
159   public URL getXmlFileUrl() { return xmlFileUrl; }
160 
161   /** Creates an AnnotationSchema object from an XSchema file
162     @param anXSchemaURL the URL where to find the XSchema file
163     */
164   public void fromXSchema(URL anXSchemaURL)
165               throws ResourceInstantiationException {
166     org.jdom.Document jDom = null;
167     SAXBuilder saxBuilder = new SAXBuilder(false);
168     try {
169     try{
170       jDom = saxBuilder.build(anXSchemaURL);
171     }catch(JDOMException je){
172       throw new ResourceInstantiationException(je);
173     }
174     catch (java.io.IOException ex) {
175       throw new ResourceInstantiationException(ex);
176     }
177     workWithJDom(jDom);
178   // fromXSchema
179 
180   /** Creates an AnnotationSchema object from an XSchema file
181     @param anXSchemaInputStream the Input Stream containing the XSchema file
182     */
183   public void fromXSchema(InputStream anXSchemaInputStream)
184               throws ResourceInstantiationException {
185     org.jdom.Document jDom = null;
186     SAXBuilder saxBuilder = new SAXBuilder(false);
187     try {
188     try{
189       jDom = saxBuilder.build(anXSchemaInputStream);
190     }catch(JDOMException je){
191       throw new ResourceInstantiationException(je);
192     }
193     catch (java.io.IOException ex) {
194       throw new ResourceInstantiationException(ex);
195     }
196     workWithJDom(jDom);
197   // end fromXSchema
198 
199   /** This method uses the JDom structure for our XSchema needs. What it does is
200     * to add semantics to the XML elements defined in XSchema. In the end we need
201     * to construct an AnnotationSchema object form an XSchema file.
202     *
203     @param jDom the JDOM structure containing the XSchema document. It must not
204     * be <b>null<b>
205     */
206   private void workWithJDom(org.jdom.Document jDomthrows ResourceInstantiationException {
207     // Use the jDom structure the way we want
208     org.jdom.Element rootElement = jDom.getRootElement();
209     Namespace namespace = rootElement.getNamespace();
210     
211     // get all children elements from the rootElement
212     List<?> rootElementChildrenList =
213             rootElement.getChildren("element", namespace);
214     if(rootElementChildrenList.size() 1)
215       throw new ResourceInstantiationException(
216               "Each Annotation must be defined in a separate XML Schema file");
217     Iterator<?> rootElementChildrenIterator = rootElementChildrenList.iterator();
218     while(rootElementChildrenIterator.hasNext()) {
219       org.jdom.Element childElement =
220               (org.jdom.Element)rootElementChildrenIterator.next();
221       createAnnotationSchemaObject(childElement, namespace);
222     }// end while
223     
224     rootElementChildrenList = rootElement.getChildren("include", namespace);
225     rootElementChildrenIterator = rootElementChildrenList.iterator();
226     while(rootElementChildrenIterator.hasNext()) {
227       org.jdom.Element childElement =
228               (org.jdom.Element)rootElementChildrenIterator.next();
229 
230       try {
231         String url = childElement.getAttributeValue("schemaLocation");
232         FeatureMap params = Factory.newFeatureMap();
233         params.put("xmlFileUrl"new URL(xmlFileUrl, url));
234 
235         lastIncluded =
236                 (AnnotationSchema)Factory.createResource(
237                         "gate.creole.AnnotationSchema", params);
238       catch(Exception e) {
239         throw new ResourceInstantiationException(e);
240       }
241     }
242   // workWithJdom
243 
244   /** This method creates an AnnotationSchema object fom an org.jdom.Element
245     @param anElement is an XSchema element element
246     */
247   private void createAnnotationSchemaObject(org.jdom.Element anElement, Namespace namespace){
248     // Get the value of the name attribute. If this attribute doesn't exists
249     // then it will receive a default one.
250     annotationName = anElement.getAttributeValue("name");
251     if (annotationName == null)
252         annotationName = "UnknownElement";
253     // See if this element has a complexType element inside it
254     org.jdom.Element complexTypeElement = anElement.getChild("complexType",
255                                                              namespace);
256     if (complexTypeElement != null){
257       List<?> complexTypeCildrenList = complexTypeElement.getChildren("attribute",
258                                                                    namespace);
259       Iterator<?> complexTypeCildrenIterator = complexTypeCildrenList.iterator();
260       if (complexTypeCildrenIterator.hasNext())
261         featureSchemaSet = new LinkedHashSet<FeatureSchema>();
262       while (complexTypeCildrenIterator.hasNext()) {
263         org.jdom.Element childElement =
264                     (org.jdom.ElementcomplexTypeCildrenIterator.next();
265         createAndAddFeatureSchemaObject(childElement, namespace);
266       }// end while
267     }// end if
268   // createAnnoatationSchemaObject
269 
270   /** This method creates and adds a FeatureSchema object to the current
271     * AnnotationSchema one.
272     @param anAttributeElement is an XSchema attribute element
273     */
274   public void createAndAddFeatureSchemaObject(org.jdom.Element
275                                                           anAttributeElement, Namespace namespace) {
276     String featureName = null;
277     Class<?> featureType = null;
278     String featureUse  = null;
279     String featureValue = null;
280     Set<String> featurePermittedValuesSet = null;
281 
282     // Get the value of the name attribute. If this attribute doesn't exists
283     // then it will receive a default one.
284     featureName = anAttributeElement.getAttributeValue("name");
285     if (featureName == null)
286       featureName = "UnknownFeature";
287 
288     // See if it has a type attribute associated
289     String featureTypeName = anAttributeElement.getAttributeValue("type");
290     if (featureTypeName != null)
291       // Set it to the corresponding Java type
292       featureType = xSchema2JavaMap.get(featureTypeName);
293     
294     // Get the value of use attribute
295     featureUse = anAttributeElement.getAttributeValue("use");
296     if (featureUse == null)
297       // Set it to the default value
298       featureUse = "optional";
299 
300     // Get the value of value attribute
301     featureValue = anAttributeElement.getAttributeValue("value");
302     if (featureValue == null)
303       featureValue = "";
304 
305     // Let's check if it has a simpleType element inside
306     org.jdom.Element simpleTypeElement  =
307                                   anAttributeElement.getChild("simpleType",
308                                                               namespace);
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                                                          namespace);
315       if (restrictionElement != null) {
316         // Get the type attribute for restriction element
317         featureTypeName = restrictionElement.getAttributeValue("base");
318         if (featureTypeName == nullfeatureTypeName = "string";
319         // Set it to the corresponding Java type
320         featureType =  xSchema2JavaMap.get(featureTypeName);
321 
322         // Check to see if there are any enumeration elements inside
323         List<?> enumerationElementChildrenList =
324                                  restrictionElement.getChildren("enumeration",
325                                                                 namespace);
326         Iterator<?> enumerationChildrenIterator =
327                                 enumerationElementChildrenList.iterator();
328 
329         // Check if there is any enumeration element in the list
330         if (enumerationChildrenIterator.hasNext())
331             featurePermittedValuesSet = new HashSet<String>();
332         while (enumerationChildrenIterator.hasNext()) {
333           org.jdom.Element enumerationElement =
334                         (org.jdom.ElementenumerationChildrenIterator.next();
335           String permissibleValue =
336                             enumerationElement.getAttributeValue("value");
337           // Add that value to the featureSchema possible values set.
338           featurePermittedValuesSet.add(permissibleValue);
339         }// end while
340       }// end if( restrictionElement != null)
341     }// end if (simpleTypeElement != null)
342 
343     // If it doesn't have a simpleTypeElement inside and featureType is null or
344     // it wasn't recognised, then we set the default type to string.
345     if (simpleTypeElement == null && featureType == null )
346       featureType = xSchema2JavaMap.get("string");
347 
348     // Create an add a featureSchema object
349     FeatureSchema featureSchema = new FeatureSchema(
350                                                    featureName,
351                                                    //if for some reason we've ended up with a null type default to String
352                                                    featureType != null ? featureType : String.class, 
353                                                    featureValue,
354                                                    featureUse,
355                                                    featurePermittedValuesSet);
356     featureSchemaSet.add(featureSchema);
357   // createAndAddFeatureSchemaObject
358 
359   /** @return a String containing the XSchema document representing
360     *  an AnnotationSchema object.
361     */
362   public String toXSchema(){
363     StringBuffer schemaString = new StringBuffer();
364     schemaString.append("<?xml version=\"1.0\"?>\n" +
365                    "<schema xmlns=\"http://www.w3.org/2000/10/XMLSchema\">\n"+
366                    " <element name=\"" + annotationName + "\"");
367 
368     if (featureSchemaSet == null)
369       schemaString.append("/>\n");
370     else {
371       schemaString.append(">\n  <complexType>\n");
372       for(FeatureSchema fs : featureSchemaSet){
373         schemaString.append(fs.toXSchema(java2xSchemaMap));
374       }
375       schemaString.append("  </complexType>\n");
376       schemaString.append(" </element>\n");
377     }// end if else
378     schemaString.append("</schema>\n");
379     return schemaString.toString();
380   }// toXSchema
381 // AnnotationSchema
382