SimpleFeatureMapImpl.java
001 /*
002  *  SimpleFeatureMapImpl.java
003  *
004  *  Copyright (c) 1995-2010, 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  *  Hamish Cunningham, 7/Feb/2000
013  *  borislav popov, 1/May/2002
014  *
015  *  $Id: SimpleFeatureMapImpl.java 12974 2010-08-18 20:29:38Z ian_roberts $
016  */
017 
018 package gate.util;
019 
020 import gate.FeatureMap;
021 import gate.creole.ontology.OClass;
022 import gate.creole.ontology.OConstants;
023 import gate.creole.ontology.Ontology;
024 import gate.event.FeatureMapListener;
025 
026 import java.util.Set;
027 import java.util.Vector;
028 
029 /** Simple case of features. */
030 public class SimpleFeatureMapImpl
031     extends SimpleMapImpl
032     implements FeatureMap, java.io.Serializable, java.lang.Cloneable,
033     gate.creole.ANNIEConstants
034 {
035   /** Debug flag */
036   private static final boolean DEBUG = false;
037 
038 
039  /** Freeze the serialization UID. */
040   static final long serialVersionUID = -2747241616127229116L;
041 
042   /**
043    * Test if <b>this</b> featureMap includes all features from aFeatureMap
044    *
045    * However, if aFeatureMap contains a feature whose value is equal to
046    * gate.creole.ANNIEConstants.LOOKUP_CLASS_FEATURE_NAME (which is normally
047    * "class"), then GATE will attempt to match that feature using an ontology
048    * which it will try to retreive from a feature in both the feature map
049    * through which this method is called and in aFeatureMap. If these do not return
050    * identical ontologies, or if
051    * either feature map does not contain an ontology, then
052    * matching will fail, and this method will return false. In summary,
053    * this method will not work normally when aFeatureMap contains a feature
054    * with the name "class".
055    *
056     @param aFeatureMap object which will be included or not in
057     <b>this</b> FeatureMap obj.If this param is null then it will return true.
058     @return <code>true</code> if aFeatureMap is incuded in <b>this</b> obj.
059     * and <code>false</code> if not.
060     */
061   public boolean subsumes(FeatureMap aFeatureMap){
062     // null is included in everything
063     if (aFeatureMap == nullreturn true;
064 
065     if (this.size() < aFeatureMap.size()) return false;
066 
067     SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl)aFeatureMap;
068 
069     Object key;
070     Object keyValueFromAFeatureMap;
071     Object keyValueFromThis;
072 
073     for (int i = 0; i < sfm.count; i++) {
074       key = sfm.theKeys[i];
075       keyValueFromAFeatureMap = sfm.theValues[i];
076       int v = super.getSubsumeKey(key);
077       if (v < 0return false;
078       keyValueFromThis = theValues[v];//was: get(key);
079 
080       if  ( (keyValueFromThis == null && keyValueFromAFeatureMap != null||
081             (keyValueFromThis != null && keyValueFromAFeatureMap == null)
082           return false;
083 
084       /*ontology aware subsume implementation
085       ontotext.bp*/
086       if ((keyValueFromThis != null&& (keyValueFromAFeatureMap != null)) {
087 // commented out as ontology subsumes is now explicitly called if
088 // an ontology is provided. <valyt>       
089 //        if ( key.equals(LOOKUP_CLASS_FEATURE_NAME) ) {
090 //          /* ontology aware processing */
091 //          Object sfmOntoObj = sfm.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
092 //          Object thisOntoObj = this.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
093 //          if (null!=sfmOntoObj && null!= thisOntoObj) {
094 //            if (sfmOntoObj.equals(thisOntoObj)) {
095 //              boolean doSubsume = ontologySubsume(
096 //                          sfmOntoObj.toString(),
097 //                          keyValueFromAFeatureMap.toString(),
098 //                          keyValueFromThis.toString());
099 //              if (!doSubsume ) {
100 //                return false;
101 //              }
102 //            } // if ontologies are with the same url
103 //          } //if not null objects
104 //          else {
105 //            // incomplete feature set: missing ontology feature
106 //            return false;
107 //          }
108 //        } else {
109           /* processing without ontology awareness */
110           if (!keyValueFromThis.equals(keyValueFromAFeatureMap)) return false;
111 //        }  // else
112       // if
113     // for
114 
115     return true;
116   }//subsumes()
117 
118    /** Tests if <b>this</b> featureMap object includes aFeatureMap features. <br>
119    * If the feature map contains <code>class</code> and (optionally) <code>ontology</code> features:<br>
120    * then the ontologyLR is used to provide ontology based subsume with respect to the subClassOf relations.
121    @param ontologyLR an ontology to be used for the subsume
122    @param aFeatureMap object which will be included  or not in  <b>this</b>
123    * FeatureMap obj.
124    @return <code>true</code> if <b>this</b> includes aFeatureMap
125    * and <code>false</code> if not.
126    */
127   public boolean subsumes(Ontology ontologyLR, FeatureMap aFeatureMap) {
128 
129     if (ontologyLR == null) {
130       return this.subsumes(aFeatureMap);
131     }
132 
133     if (aFeatureMap == null)
134       return true;
135 
136     if (this.size() < aFeatureMap.size())
137       return false;
138 
139     SimpleFeatureMapImpl sfm = (SimpleFeatureMapImplaFeatureMap;
140 
141     Object key;
142     Object keyValueFromAFeatureMap;
143     Object keyValueFromThis;
144 
145     for (int i = 0; i < sfm.count; i++) {
146       key = sfm.theKeys[i];
147       keyValueFromAFeatureMap = sfm.theValues[i];
148       int v = super.getSubsumeKey(key);
149       if (v < 0)
150         return false;
151       keyValueFromThis = theValues[v]//was: get(key);
152 
153       if ( (keyValueFromThis == null && keyValueFromAFeatureMap != null||
154           (keyValueFromThis != null && keyValueFromAFeatureMap == null)
155           )
156         return false;
157 
158       /*ontology aware subsume implementation based on the ontology LR
159           ontotext.bp*/
160       if ( (keyValueFromThis != null&& (keyValueFromAFeatureMap != null)) {
161 
162         if (key.equals(LOOKUP_CLASS_FEATURE_NAME)) {
163           // ontology aware processing
164 
165           try {
166             OClass superClass = getClassForURIOrName(ontologyLR, keyValueFromAFeatureMap.toString());
167             OClass subClass = getClassForURIOrName(ontologyLR, keyValueFromThis.toString());
168 
169             if(superClass == null || subClass == nullreturn false;
170             if (DEBUG) {
171               Out.prln("\nClass in rule: " + keyValueFromAFeatureMap.toString());
172               Out.prln("\nClass in annotation: " + keyValueFromThis.toString());
173               Out.prln("\nisSubClassOf: " + subClass.isSubClassOf(superClass, OConstants.Closure.TRANSITIVE_CLOSURE));
174             }
175 
176             return subClass.equals(superClass|| 
177                 subClass.isSubClassOf(superClass, 
178                         OConstants.Closure.TRANSITIVE_CLOSURE);
179           catch (Exception ex) {
180             throw new gate.util.GateRuntimeException(ex);
181           }
182         }
183         else {
184           /* processing without ontology awareness */
185           if (!keyValueFromThis.equals(keyValueFromAFeatureMap))
186             return false;
187         // else
188 
189       // if
190     // for
191 
192     return true;
193   //subsumes(ontology)
194   
195   /**
196    * Look up the given name in the given ontology.  First we try
197    * treating the name as a complete URI and attempt to find the
198    * matching OClass.  If this fails (either the name is not a URI
199    * or there is no class by that URI) then we try again, treating
200    * the name as local to the default namespace of the ontology.
201    @param ontologyLR the ontology
202    @param name the URI or local resource name to look up
203    @return the corresponding OClass, or <code>null</code> if no
204    * suitable class could be found.
205    */
206   protected OClass getClassForURIOrName(Ontology ontologyLR, String name) {
207     OClass cls = null;
208     try {
209       cls = ontologyLR.getOClass(ontologyLR.createOURI(name));
210     }
211     catch(Exception e) {
212       // do nothing, but leave cls == null
213     }
214     if(cls == null) {
215       try {
216         cls = ontologyLR.getOClass(ontologyLR.createOURIForName(name));
217       }
218       catch(Exception e) {
219         // do nothing, but leave cls == null
220       }
221     }
222     return cls;
223   }
224 
225 
226   /** Tests if <b>this</b> featureMap object includes aFeatureMap but only
227     * for the those features present in the aFeatureNamesSet.
228     *
229     * However, if aFeatureMap contains a feature whose value is equal to
230    * gate.creole.ANNIEConstants.LOOKUP_CLASS_FEATURE_NAME (which is normally
231    * "class"), then GATE will attempt to match that feature using an ontology
232    * which it will try to retreive from a feature in both the feature map
233    * through which this method is called and in aFeatureMap. If these do not return
234    * identical ontologies, or if
235    * either feature map does not contain an ontology, then
236    * matching will fail, and this method will return false. In summary,
237    * this method will not work normally when aFeatureMap contains a feature
238    * with the name "class" if that feature is also in aFeatureNamesSet.
239     *
240     @param aFeatureMap which will be included or not in <b>this</b>
241     * FeatureMap obj.If this param is null then it will return true.
242     @param aFeatureNamesSet is a set of strings representing the names of the
243     * features that would be considered for subsumes. If aFeatureNamesSet is
244     <b>null</b> then subsumes(FeatureMap) will be called.
245     @return <code>true</code> if all features present in the aFeaturesNameSet
246     * from aFeatureMap are included in <b>this</b> obj, or <code>false</code>
247     * otherwise.
248     */
249   public boolean subsumes(FeatureMap aFeatureMap, Set aFeatureNamesSet){
250     // This means that all features are taken into consideration.
251     if (aFeatureNamesSet == nullreturn this.subsumes(aFeatureMap);
252     // null is included in everything
253     if (aFeatureMap == nullreturn true;
254     // This means that subsumes is supressed.
255     if (aFeatureNamesSet.isEmpty()) return true;
256 
257     SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl)aFeatureMap;
258 
259     Object key;
260     Object keyValueFromAFeatureMap;
261     Object keyValueFromThis;
262 
263     for (int i = 0; i < sfm.count; i++) {
264       key = sfm.theKeys[i];
265 
266       if (!aFeatureNamesSet.contains(key))
267         continue;
268 
269       keyValueFromAFeatureMap = sfm.theValues[i];
270         keyValueFromThis = get(key);
271 
272       if  ( (keyValueFromThis == null && keyValueFromAFeatureMap != null||
273             (keyValueFromThis != null && keyValueFromAFeatureMap == null)
274           return false;
275 
276       if ((keyValueFromThis != null&& (keyValueFromAFeatureMap != null)) {
277 // Commented out as ontology subsumes is now explicitly called when an ontology
278 // is provided. <valyt>
279 //        if ( key.equals(LOOKUP_CLASS_FEATURE_NAME) ) {
280 //          /* ontology aware processing */
281 //          if (!aFeatureNamesSet.contains(LOOKUP_ONTOLOGY_FEATURE_NAME))
282 //            continue;
283 //
284 //          Object sfmOntoObj = sfm.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
285 //          Object thisOntoObj = this.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
286 //          if (null!=sfmOntoObj && null!= thisOntoObj) {
287 //            if (sfmOntoObj.equals(thisOntoObj)) {
288 //              if (! ontologySubsume(
289 //                          sfmOntoObj.toString(),
290 //                          keyValueFromAFeatureMap.toString(),
291 //                          keyValueFromThis.toString()))
292 //                return false;
293 //            } // if ontologies are with the same url
294 //          } //if not null objects
295 //          else {
296 //            // incomplete feature set: missing ontology feature
297 //            return false;
298 //          }
299 //        } else {
300           /*processing without ontology awareness*/
301           if (!keyValueFromThis.equals(keyValueFromAFeatureMap)) return false;
302 //        } //else
303       // if values not null
304     // for
305 
306     return true;
307   }// subsumes()
308 
309 
310   /**
311    * Overriden to fire events, so that the persistent objects
312    *  can keep track of what's updated
313    */
314   public Object put(Object key, Object value) {
315     Object result = super.put(key, value);
316     this.fireMapUpdatedEvent();
317     return result;
318   // put
319 
320   /**
321    * Overriden to fire events, so that the persistent objects
322    *  can keep track of what's updated
323    */
324   public Object remove(Object key) {
325     Object result = super.remove(key);
326     this.fireMapUpdatedEvent();
327     return result;
328   // remove
329 
330   public void clear() {
331     super.clear();
332     //tell the world if they're listening
333     this.fireMapUpdatedEvent();
334   // clear
335 
336   // Views
337   public Object clone() {
338     return super.clone();
339   //clone
340 
341   public boolean equals(Object o) {
342     return super.equals(o);
343   // equals
344 
345 //////////////////THE EVENT HANDLING CODE//////////////
346 //Needed so an annotation can listen to its features//
347 //and update correctly the database//////////////////
348   private transient Vector mapListeners;
349   /**
350    * Removes a gate listener
351    */
352   public synchronized void removeFeatureMapListener(FeatureMapListener l) {
353     if (mapListeners != null && mapListeners.contains(l)) {
354       Vector v = (VectormapListeners.clone();
355       v.removeElement(l);
356       mapListeners = v;
357     }
358   //removeFeatureMapListener
359   /**
360    * Adds a gate listener
361    */
362   public synchronized void addFeatureMapListener(FeatureMapListener l) {
363     Vector v = mapListeners == null new Vector(2(Vector)mapListeners.clone();
364     if (!v.contains(l)) {
365       v.addElement(l);
366       mapListeners = v;
367     }
368   //addFeatureMapListener
369 
370   /**
371    *
372    */
373   protected void fireMapUpdatedEvent () {
374     if (mapListeners != null) {
375       Vector listeners = mapListeners;
376       int count = listeners.size();
377       if (count == 0return;
378       for (int i = 0; i < count; i++)
379         ((FeatureMapListenerlisteners.elementAt(i)).featureMapUpdated();
380     }
381   }//fireMapUpdatedEvent
382 
383 //Commented out as ontology subsumes is now explicitly called when an ontology
384 //is provided. <valyt>
385 //  /**ontology enhanced subsume
386 //   * @param ontoUrl the url of the ontology to be used
387 //   * @return true if value1 subsumes value2 in the specified ontology */
388 //  protected boolean ontologySubsume(String ontoUrl,String value1,String value2) {
389 //    boolean result = false;
390 //    try {
391 //      URL url;
392 //      try {
393 //        url = new URL(ontoUrl);
394 //      } catch (MalformedURLException e){
395 //        throw new RuntimeException(
396 //        "\nin SimpleFeatureMapImpl on ontologySubsume()\n"
397 //        +e.getMessage()+"\n");
398 //      }
399 //
400 //      /* GET ONTOLOGY BY URL : a bit tricky reference
401 //      since the behaviour behind the getOntology method is
402 //      certainly static.
403 //      : should be temporary */
404 //      Ontology o = OntologyUtilities.getOntology(url);
405 //      OClass superClass = (OClass) o.getOResourceByName(value1);
406 //      OClass subClass = (OClass) o.getOResourceByName(value2);
407 //      if (subClass.equals(superClass))
408 //        return true;
409 //
410 //      if (subClass.isSubClassOf(superClass, OConstants.TRANSITIVE_CLOSURE))
411 //        return true;
412 //
413 //      //check for equivalency
414 //      Set<OClass> equiv = superClass.getEquivalentClasses();
415 //      result = equiv.contains(subClass);
416 //
417 //    } catch  (gate.creole.ResourceInstantiationException x) {
418 //      x.printStackTrace(Err.getPrintWriter());
419 //    }
420 //    return result;
421 //  } // ontologySubsume
422 
423 // class SimpleFeatureMapImpl