1   /*
2    *  SimpleFeatureMapImpl.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   *  Hamish Cunningham, 7/Feb/2000
12   *  borislav popov, 1/May/2002
13   *
14   *  $Id: SimpleFeatureMapImpl.java,v 1.26 2002/05/01 09:32:04 nasso Exp $
15   */
16  
17  package gate.util;
18  
19  import java.util.*;
20  import java.net.*;
21  
22  import gate.*;
23  import gate.event.*;
24  //import gate.creole.*;
25  import gate.creole.ontology.*;
26  
27  import com.ontotext.gate.ontology.*;
28  
29  /** Simple case of features. */
30  //>>> DAM: was (derived from HashMap)
31  /*
32  public class SimpleFeatureMapImpl  extends HashMap implements FeatureMap
33  */
34  //=== DAM: FeatArray optimization, now derived from SimpleMapImpl
35  public class SimpleFeatureMapImpl
36      extends SimpleMapImpl
37  //    extends HashMap
38      implements FeatureMap, java.io.Serializable, java.lang.Cloneable,
39      gate.creole.ANNIEConstants
40  //>>> DAM: end
41  {
42    /** Debug flag */
43    private static final boolean DEBUG = false;
44  
45  
46   /** Freeze the serialization UID. */
47    static final long serialVersionUID = -2747241616127229116L;
48  
49    /** Test if <b>this</b> featureMap includes all features from aFeatureMap
50      * @param aFeatureMap object which will be included or not in
51      * <b>this</b> FeatureMap obj.If this param is null then it will return true.
52      * @return <code>true</code> if aFeatureMap is incuded in <b>this</b> obj.
53      * and <code>false</code> if not.
54      */
55    public boolean subsumes(FeatureMap aFeatureMap){
56      // null is included in everything
57      if (aFeatureMap == null) return true;
58  
59      if (this.size() < aFeatureMap.size()) return false;
60  
61      SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl)aFeatureMap;
62  
63      Object key;
64      Object keyValueFromAFeatureMap;
65      Object keyValueFromThis;
66  
67      for (int i = 0; i < sfm.count; i++) {
68        key = sfm.theKeys[i];
69        keyValueFromAFeatureMap = sfm.theValues[i];
70        int v = super.getSubsumeKey(key);
71        if (v < 0) return false;
72        keyValueFromThis = theValues[v];//was: get(key);
73  
74        if  ( (keyValueFromThis == null && keyValueFromAFeatureMap != null) ||
75              (keyValueFromThis != null && keyValueFromAFeatureMap == null)
76            ) return false;
77  
78        /*ontology aware subsume implementation
79        ontotext.bp*/
80        if ((keyValueFromThis != null) && (keyValueFromAFeatureMap != null)) {
81  
82          if ( key.equals(LOOKUP_CLASS_FEATURE_NAME) ) {
83            /* ontology aware processing */
84            Object sfmOntoObj = sfm.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
85            Object thisOntoObj = this.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
86            if (null!=sfmOntoObj && null!= thisOntoObj) {
87              if (sfmOntoObj.equals(thisOntoObj)) {
88                boolean doSubsume = ontologySubsume(
89                            sfmOntoObj.toString(),
90                            keyValueFromAFeatureMap.toString(),
91                            keyValueFromThis.toString());
92                if (!doSubsume ) {
93                  return false;
94                }
95              } // if ontologies are with the same url
96            } //if not null objects
97          } else {
98            /* processing without ontology awareness */
99            if (!keyValueFromThis.equals(keyValueFromAFeatureMap)) return false;
100         }  // else
101 
102       } // if
103     } // for
104 
105     return true;
106   }//subsumes()
107 
108   /** Tests if <b>this</b> featureMap object includes aFeatureMap but only
109     * for the those features present in the aFeatureNamesSet.
110     * @param aFeatureMap which will be included or not in <b>this</b>
111     * FeatureMap obj.If this param is null then it will return true.
112     * @param aFeatureNamesSet is a set of strings representing the names of the
113     * features that would be considered for subsumes. If aFeatureNamesSet is
114     * <b>null</b> then subsumes(FeatureMap) will be called.
115     * @return <code>true</code> if all features present in the aFeaturesNameSet
116     * from aFeatureMap are included in <b>this</b> obj, or <code>false</code>
117     * otherwise.
118     */
119   public boolean subsumes(FeatureMap aFeatureMap, Set aFeatureNamesSet){
120     // This means that all features are taken into consideration.
121     if (aFeatureNamesSet == null) return this.subsumes(aFeatureMap);
122     // null is included in everything
123     if (aFeatureMap == null) return true;
124     // This means that subsumes is supressed.
125     if (aFeatureNamesSet.isEmpty()) return true;
126 
127     SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl)aFeatureMap;
128 
129     Object key;
130     Object keyValueFromAFeatureMap;
131     Object keyValueFromThis;
132 
133     for (int i = 0; i < sfm.count; i++) {
134       key = sfm.theKeys[i];
135 
136       if (!aFeatureNamesSet.contains(key))
137         continue;
138 
139       keyValueFromAFeatureMap = sfm.theValues[i];
140         keyValueFromThis = get(key);
141 
142       if  ( (keyValueFromThis == null && keyValueFromAFeatureMap != null) ||
143             (keyValueFromThis != null && keyValueFromAFeatureMap == null)
144           ) return false;
145 
146       if ((keyValueFromThis != null) && (keyValueFromAFeatureMap != null)) {
147         if ( key.equals(LOOKUP_CLASS_FEATURE_NAME) ) {
148           /* ontology aware processing */
149           if (!aFeatureNamesSet.contains(LOOKUP_ONTOLOGY_FEATURE_NAME))
150             continue;
151 
152           Object sfmOntoObj = sfm.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
153           Object thisOntoObj = this.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
154           if (null!=sfmOntoObj && null!= thisOntoObj) {
155             if (sfmOntoObj.equals(thisOntoObj)) {
156               if (! ontologySubsume(
157                           sfmOntoObj.toString(),
158                           keyValueFromAFeatureMap.toString(),
159                           keyValueFromThis.toString()))
160                 return false;
161             } // if ontologies are with the same url
162           } //if not null objects
163         } else {
164           /*processing without ontology awareness*/
165           if (!keyValueFromThis.equals(keyValueFromAFeatureMap)) return false;
166         } //else
167       } // if values not null
168     } // for
169 
170     return true;
171   }// subsumes()
172 
173 
174   /**
175    * Overriden to fire events, so that the persistent objects
176    *  can keep track of what's updated
177    */
178   public Object put(Object key, Object value) {
179     Object result = super.put(key, value);
180     this.fireMapUpdatedEvent();
181     return result;
182   } // put
183 
184   /**
185    * Overriden to fire events, so that the persistent objects
186    *  can keep track of what's updated
187    */
188   public Object remove(Object key) {
189     Object result = super.remove(key);
190     this.fireMapUpdatedEvent();
191     return result;
192   } // remove
193 
194   public void clear() {
195     super.clear();
196     //tell the world if they're listening
197     this.fireMapUpdatedEvent();
198   } // clear
199 
200   // Views
201   public Object clone() {
202     return super.clone();
203   } //clone
204 
205   public boolean equals(Object o) {
206     return super.equals(o);
207   } // equals
208 
209 //////////////////THE EVENT HANDLING CODE//////////////
210 //Needed so an annotation can listen to its features//
211 //and update correctly the database//////////////////
212   private transient Vector mapListeners;
213   /**
214    * Removes a gate listener
215    */
216   public synchronized void removeFeatureMapListener(FeatureMapListener l) {
217     if (mapListeners != null && mapListeners.contains(l)) {
218       Vector v = (Vector) mapListeners.clone();
219       v.removeElement(l);
220       mapListeners = v;
221     }
222   } //removeFeatureMapListener
223   /**
224    * Adds a gate listener
225    */
226   public synchronized void addFeatureMapListener(FeatureMapListener l) {
227     Vector v = mapListeners == null ? new Vector(2) : (Vector)mapListeners.clone();
228     if (!v.contains(l)) {
229       v.addElement(l);
230       mapListeners = v;
231     }
232   } //addFeatureMapListener
233   /**
234    *
235    * @param e
236    */
237   protected void fireMapUpdatedEvent () {
238     if (mapListeners != null) {
239       Vector listeners = mapListeners;
240       int count = listeners.size();
241       if (count == 0) return;
242       for (int i = 0; i < count; i++)
243         ((FeatureMapListener) listeners.elementAt(i)).featureMapUpdated();
244     }
245   }//fireMapUpdatedEvent
246 
247 
248   /**ontology enhanced subsume
249    * @param ontoUrl the url of the ontology to be used
250    * @return true if value1 subsumes value2 in the specified ontology */
251   protected boolean ontologySubsume(String ontoUrl,String value1,String value2) {
252     boolean result = false;
253     try {
254       URL url;
255       try {
256         url = new URL(ontoUrl);
257       } catch (MalformedURLException e){
258         throw new RuntimeException(
259         "\nin SimpleFeatureMapImpl on ontologySubsume()\n"
260         +e.getMessage()+"\n");
261       }
262 
263       /* GET ONTOLOGY BY URL : a bit tricky reference
264       since the behaviour behind the getOntology method is
265       certainly static.
266       : should be temporary */
267       Ontology o = new OntologyImpl().getOntology(url);
268 
269       OClass c1 = o.getClassByName(value1);
270       OClass c2 = o.getClassByName(value2);
271 
272       if (null!= c1 && null!= c2) {
273         if (c1.equals(c2)) {
274           result = true;
275         } else {
276           Set subs1;
277           try {
278             subs1 = c1.getSubClasses(OClass.TRANSITIVE_CLOSURE);
279           } catch (gate.creole.ontology.NoSuchClosureTypeException x) {
280             throw new gate.creole.ResourceInstantiationException(x);
281           }
282           if (subs1.contains(c2))
283             result = true;
284         } // else
285       } // if not null classes
286     } catch  (gate.creole.ResourceInstantiationException x) {
287       x.printStackTrace(Err.getPrintWriter());
288     }
289     return result;
290   } // ontologySubsume
291 
292 } // class SimpleFeatureMapImpl
293 
294