1   /*
2    * TaxonomyImpl.java
3    * Copyright:    Copyright (c) 2001, OntoText Lab.
4    * Company:      OntoText Lab.
5    * borislav popov 02/2002 */
6   
7   package com.ontotext.gate.ontology;
8   
9   import java.util.*;
10  import java.net.*;
11  import gate.creole.*;
12  import gate.creole.ontology.*;
13  import gate.*;
14  import gate.event.*;
15  import gate.util.*;
16  
17  /**An Ontology Implementation Class
18    * @author borislav popozv*/
19  public class TaxonomyImpl
20  extends gate.creole.AbstractLanguageResource implements Taxonomy {
21  
22    /**denotes a direct closure(no transitivity)*/
23    public static final byte DIRECT_CLOSURE = 0;
24    /**denotes atransitive closure */
25    public static final byte TRANSITIVE_CLOSURE = 1;
26  
27    /** Object Modification Listeners */
28    private static Set listeners = new HashSet();
29  
30    private String label;
31    private URL url;
32    private String sourceURI;
33    private String version;
34    private String id;
35    private String comment;
36    private Map classesByName = new HashMap();
37    private Set classes = new HashSet();
38    private Set tops;
39    private String name;
40    protected long lastGeneratedId = 0;
41  
42    /**
43     * Adds an object modification listener.
44     * @param listener listener to be added.
45     */
46    public static void addObjectModificationListener(ObjectModificationListener listener) {
47      if ( null==listener )
48        throw new IllegalArgumentException(
49          "The object modification listener should not be [null].");
50      listeners.add(listener);
51    }
52  
53    /**Fires an object modification event.
54     * @param event the event to be fired   */
55    protected static void fireObjectModificationEvent(Object source) {
56      ObjectModificationEvent event = new ObjectModificationEvent(source,
57        ObjectModificationEvent.OBJECT_MODIFIED,ObjectModificationEvent.OBJECT_MODIFIED);
58      ArrayList ll = new ArrayList(listeners);
59      for (int i = 0 ; i < ll.size(); i++ ) {
60        ((ObjectModificationListener)ll.get(i)).objectModified(event);
61      }
62    }
63  
64    /* this method might cause performance problems
65    and should be updated when naso's idea with the ids
66    associated with documents is realized.*/
67    public Taxonomy getOntology(URL someUrl)throws ResourceInstantiationException{
68  
69      List lrs = Gate.getCreoleRegister().getLrInstances(
70          "com.ontotext.gate.ontology.DAMLOntology");
71  
72      Taxonomy result = null;
73      Taxonomy tempo = null;
74  
75      /* unpack the gate:path urls to absolute form*/
76      if (-1 != someUrl.getProtocol().indexOf("gate")) {
77        someUrl = gate.util.protocols.gate.Handler.class.getResource(
78                      Files.getResourcePath() + someUrl.getPath());
79      }// if
80  
81  
82      /*iterate through the list of lrs and search for the wanted url
83      :this is a temporary solution*/
84      for (int i = 0 ; i < lrs.size() ; i++ ) {
85         tempo = (Taxonomy) lrs.get(i);
86         if (tempo.getURL().equals(someUrl)) {
87          result = tempo;
88          break;
89         }
90      }
91      if ( null == result ) {
92        FeatureMap fm = Factory.newFeatureMap();
93        fm.put("URL",someUrl);
94  
95        try {
96          result = (Taxonomy)Factory.createResource(
97              "com.ontotext.gate.ontology.DAMLOntology",
98              fm
99            );
100       } catch (Exception e) {
101         throw new ResourceInstantiationException(e);
102       }
103     }
104     return result;
105   }// getOntology(url)
106 
107 
108   /** Whether the ontology has been modified
109    *  switches to true when null-ing and reinfering the
110    *  subclasses and super classes and tops */
111   protected boolean nullBuffers = false;
112 
113   /**Whether the ontology has been modified after loading.
114    * once it became true it stays true till a save takes place*/
115   protected boolean modified = false;
116 
117   /** Initialises this resource, and returns it. */
118   public Resource init() throws ResourceInstantiationException {
119     if (null == url )
120       throw new ResourceInstantiationException("URL not set (null).");
121 
122     load();
123     return this;
124   } // init()
125 
126   public URL getURL() {
127     return url;
128   }
129 
130   public void setURL(URL aUrl) {
131     url = aUrl;
132     if ( null == url ) {
133       throw new GateRuntimeException("Ontology URL set to null.");
134     }
135     /* unpack the gate:path urls to absolute form*/
136     if (-1 != url.getProtocol().indexOf("gate")) {
137       url = gate.util.protocols.gate.Handler.class.getResource(
138                     Files.getResourcePath() + url.getPath());
139     }// if
140   }// void setURL(URL)
141 
142   /**Sets the label of the ontology
143    * @param theLabel the label to be set
144    */
145   public void setLabel(String theLabel) {
146     label = theLabel;
147   }
148 
149   /** Retrieves the label of the ontology
150    *  @return the label of the ontology */
151   public String getLabel() {
152     return label;
153   }
154 
155 
156   public void load() throws ResourceInstantiationException {
157     throw new UnsupportedOperationException(
158       "OntologyImpl does not support load().\nRefer to DAMLOntology.");
159   }
160 
161   public void store() throws ResourceInstantiationException  {
162     throw new UnsupportedOperationException(
163       "OntologyImpl does not support store().\nRefer to DAMLOntology.");
164   }
165 
166   public void setSourceURI(String theURI) {
167     this.modified = true;
168     sourceURI = theURI;
169     if (-1==sourceURI.indexOf('#')){
170       sourceURI = sourceURI+'#';
171     }
172     fireObjectModificationEvent(this);
173   }
174 
175   public String getSourceURI(){
176     return sourceURI;
177   }
178 
179   public void setVersion(String theVersion) {
180     this.modified = true;
181     version = theVersion;
182     fireObjectModificationEvent(this);
183   }
184 
185   public String getVersion() {
186     return version;
187   }
188 
189   public String getId() {
190     return id;
191   }
192 
193   public void setId(String theID){
194     this.modified = true;
195     id = theID;
196     fireObjectModificationEvent(this);
197   }
198 
199   public String getComment() {
200     return comment;
201   }
202 
203   public void setComment(String theComment) {
204     this.modified = true;
205     comment = theComment;
206     fireObjectModificationEvent(this);
207   }
208 
209 
210   public TClass createClass(String aName, String aComment) {
211     this.modified = true;
212     TClass theClass
213       = new TClassImpl(Long.toString(++lastGeneratedId),aName,aComment,this);
214     addClass(theClass);
215     nullBuffers = true;
216     fireObjectModificationEvent(this);
217     return theClass;
218   }
219 
220   /**
221    * note: if a class is deleted and there aresome subclasses of this class
222    * which lack any other super classes : then they become top classes.
223    * this could be changed on request or made optional.
224    * @param theClass the class to be removed    */
225   public void removeClass(TClass theClass) {
226     try {
227       this.modified = true;
228       Iterator superi = theClass.getSuperClasses(TClass.DIRECT_CLOSURE).iterator();
229       while ( superi.hasNext() ) {
230         TClass sc = (TClass)superi.next();
231         sc.removeSubClass(theClass);
232       } // while supers
233       Iterator subi = theClass.getSubClasses(TClass.DIRECT_CLOSURE).iterator();
234       while(subi.hasNext()) {
235         TClass sc = (TClass) subi.next();
236         sc.removeSuperClass(theClass);
237       } // while subs
238 
239       classes.remove(theClass);
240       classesByName.remove(theClass.getName());
241       nullBuffers = true;
242       fireObjectModificationEvent(this);
243 
244     } catch (NoSuchClosureTypeException x) {
245       throw new GateRuntimeException(x.getMessage()) ;
246     }
247   }
248 
249   public void addClass(TClass theClass) {
250     this.modified = true;
251     classes.add(theClass);
252     classesByName.put(theClass.getName(),theClass);
253     nullBuffers = true;
254     fireObjectModificationEvent(this);
255 
256   }
257 
258   public TClass getClassByName(String theName) {
259     return (TClass) classesByName.get(theName);
260   }
261 
262   public boolean containsClassByName(String theName) {
263     return classesByName.containsKey(theName);
264   }
265 
266   public Set getClasses() {
267     return classes;
268   }
269 
270   public Iterator getClasses(Comparator comp) {
271     /**@todo: to be implemented */
272     return null;
273   }
274 
275   private void determineTops() {
276     tops = new HashSet();
277     TClass currentClass;
278     Iterator citer = classes.iterator();
279     while (citer.hasNext()) {
280       currentClass = (TClass)citer.next();
281       if (currentClass.isTopClass()) {
282         tops.add(currentClass);
283       }
284     } //while citer
285   } // determineTops();
286 
287   public Set getTopClasses() {
288     if ( nullBuffers ) {
289       reinfer();
290     } // if nullBuffers
291     if (null == tops) {
292       determineTops();
293     }
294 
295     return new HashSet(tops);
296   }
297 
298   /** calculates the taxonomic distance between two classes.
299    *  note that the method is relatively big, but in case similar
300    *  methods are developed for graph traversal, some parts of this
301    *  method would naturally become separate methods/members.
302    *
303    *  @param class1 the first class
304    *  @param class2 the second class */
305   public int getTaxonomicDistance(TClass class1, TClass class2) {
306     int result=0;
307     ArrayList root = new ArrayList();
308     TClass c;
309 
310     /* */
311     ArrayList supers1 = class1.getSuperClassesVSDistance();
312     ArrayList supers2 = class2.getSuperClassesVSDistance();
313 
314     /* test if class1-2 are sub/super of each other */
315     for ( int i1 = 0; i1<supers1.size(); i1++ ) {
316       if (((Set)supers1.get(i1)).contains(class2)) {
317         result = i1 +1 ;
318         break;
319       }
320     } // for i1
321     for ( int i2 = 0; i2<supers2.size(); i2++ ) {
322       if (((Set)supers2.get(i2)).contains(class1)) {
323         result = i2 +1 ;
324         break;
325       }
326     } // for i2
327 
328 
329     /*find common classes/nodes*/
330     if ( 0 == result ) {
331       for ( int i1 = 0; i1<supers1.size(); i1++ ) {
332 
333         for ( int i2 = 0; i2<supers2.size(); i2++) {
334 
335           Set s1 = (Set)supers1.get(i1);
336           Set s2 = (Set)supers2.get(i2);
337 
338           Iterator i3 = s1.iterator();
339 
340           while ( i3.hasNext() ) {
341             c = (TClass)i3.next();
342             if (s2.contains(c)) {
343               result = i1 + i2 + 2;
344               i1 = supers1.size();
345               i2 = supers2.size();
346               break;
347             }
348 
349           } // while i3
350 
351         } //for i2
352 
353       } // for i1
354     } // if result is zero
355 
356 
357 
358     return result;
359   }
360 
361 
362   /**
363    * Compares the id,uri and url of the ontology.
364    * @param o another ontology to compare with
365    * @return true if id,uri and url match
366    */
367   public boolean equals ( Object o ) {
368     boolean result = false;
369     if (o instanceof Taxonomy) {
370       Taxonomy onto = (Taxonomy) o;
371       result = true;
372       if (null != this.getId() & null != onto.getId())
373         result &= this.getId().equals(onto.getId());
374       else {
375         /* check if both ids are null; if so, consider the ontologies
376         partially equal*/
377         result = this.getId() == onto.getId();
378       }
379 
380       if (null != this.getURL() & null != onto.getURL())
381         result &= this.getURL().equals(onto.getURL());
382       else
383         result = this.getURL() == onto.getURL();
384 
385       if (null != this.getSourceURI() & null != onto.getSourceURI())
386         result &= this.getSourceURI().equals(onto.getSourceURI());
387       else
388         result = this.getSourceURI() == onto.getSourceURI();
389     }
390     return result ;
391   } // equals
392 
393   public String toString() {
394     return getName();
395   }
396 
397   /**Called when the ontology has been modified to re-infer
398    * all sub/super classes, tops, etc.
399    * currently could be implemented simpler but
400    * this implementation could be useful in the future*/
401   protected void reinfer() {
402     tops = null;
403   }  //reinfer
404 
405   public void setModified(boolean isModified) {
406     modified = isModified;
407     if (modified) fireObjectModificationEvent(this);
408   }
409 
410   public boolean isModified() {
411     return modified;
412   }
413 
414 
415   /** Check for subclass relation with transitive closure
416    * @param cls1 the first class
417    * @param cls2 the second class
418    */
419   public boolean isSubClassOf(String cls1, String cls2)
420       throws gate.creole.ontology.NoSuchClosureTypeException {
421 
422     boolean result = false;
423     TClass c1 = getClassByName(cls1);
424     TClass c2 = getClassByName(cls2);
425 
426     if (null != c1 && null != c2) {
427       if (c1.equals(c2)) {
428         result = true;
429       }
430       else {
431         Set subs1;
432         subs1 = c1.getSubClasses(TClass.TRANSITIVE_CLOSURE);
433         if (subs1.contains(c2))
434           result = true;
435       } // else
436     } // if not null classes
437     return result;
438   }
439 
440 
441   /** Check for subclass relation with direct closure
442    * @param cls1 the first class
443    * @param cls2 the second class
444   */
445   public boolean isDirectSubClassOf(String cls1, String cls2)
446       throws gate.creole.ontology.NoSuchClosureTypeException {
447 
448     boolean result = false;
449     TClass c1 = getClassByName(cls1);
450     TClass c2 = getClassByName(cls2);
451 
452     if (null != c1 && null != c2) {
453       if (c1.equals(c2)) {
454         result = true;
455       }
456       else {
457         Set subs1;
458         subs1 = c1.getSubClasses(TClass.DIRECT_CLOSURE);
459         if (subs1.contains(c2))
460           result = true;
461       } // else
462     } // if not null classes
463     return result;
464   }
465 
466 } // Taxonomy