| SimpleFeatureMapImpl.java |
1 /*
2 * SimpleFeatureMapImpl.java
3 *
4 * Copyright (c) 1998-2004, 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.35 2004/09/27 16:55:37 mike Exp $
15 */
16
17 package gate.util;
18
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.Set;
22 import java.util.Vector;
23
24 import com.ontotext.gate.ontology.OntologyImpl;
25
26 import gate.FeatureMap;
27 import gate.creole.ontology.Taxonomy;
28 import gate.event.FeatureMapListener;
29
30 /** Simple case of features. */
31 //>>> DAM: was (derived from HashMap)
32 /*
33 public class SimpleFeatureMapImpl extends HashMap implements FeatureMap
34 */
35 //=== DAM: FeatArray optimization, now derived from SimpleMapImpl
36 public class SimpleFeatureMapImpl
37 extends SimpleMapImpl
38 // extends HashMap
39 implements FeatureMap, java.io.Serializable, java.lang.Cloneable,
40 gate.creole.ANNIEConstants
41 //>>> DAM: end
42 {
43 /** Debug flag */
44 private static final boolean DEBUG = false;
45
46
47 /** Freeze the serialization UID. */
48 static final long serialVersionUID = -2747241616127229116L;
49
50 /**
51 * Test if <b>this</b> featureMap includes all features from aFeatureMap
52 *
53 * However, if aFeatureMap contains a feature whose value is equal to
54 * gate.creole.ANNIEConstants.LOOKUP_CLASS_FEATURE_NAME (which is normally
55 * "class"), then GATE will attempt to match that feature using an ontology
56 * which it will try to retreive from a feature in both the feature map
57 * through which this method is called and in aFeatureMap. If these do not return
58 * identical ontologies, or if
59 * either feature map does not contain an ontology, then
60 * matching will fail, and this method will return false. In summary,
61 * this method will not work normally when aFeatureMap contains a feature
62 * with the name "class".
63 *
64 * @param aFeatureMap object which will be included or not in
65 * <b>this</b> FeatureMap obj.If this param is null then it will return true.
66 * @return <code>true</code> if aFeatureMap is incuded in <b>this</b> obj.
67 * and <code>false</code> if not.
68 */
69 public boolean subsumes(FeatureMap aFeatureMap){
70 // null is included in everything
71 if (aFeatureMap == null) return true;
72
73 if (this.size() < aFeatureMap.size()) return false;
74
75 SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl)aFeatureMap;
76
77 Object key;
78 Object keyValueFromAFeatureMap;
79 Object keyValueFromThis;
80
81 for (int i = 0; i < sfm.count; i++) {
82 key = sfm.theKeys[i];
83 keyValueFromAFeatureMap = sfm.theValues[i];
84 int v = super.getSubsumeKey(key);
85 if (v < 0) return false;
86 keyValueFromThis = theValues[v];//was: get(key);
87
88 if ( (keyValueFromThis == null && keyValueFromAFeatureMap != null) ||
89 (keyValueFromThis != null && keyValueFromAFeatureMap == null)
90 ) return false;
91
92 /*ontology aware subsume implementation
93 ontotext.bp*/
94 if ((keyValueFromThis != null) && (keyValueFromAFeatureMap != null)) {
95
96 if ( key.equals(LOOKUP_CLASS_FEATURE_NAME) ) {
97 /* ontology aware processing */
98 Object sfmOntoObj = sfm.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
99 Object thisOntoObj = this.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
100 if (null!=sfmOntoObj && null!= thisOntoObj) {
101 if (sfmOntoObj.equals(thisOntoObj)) {
102 boolean doSubsume = ontologySubsume(
103 sfmOntoObj.toString(),
104 keyValueFromAFeatureMap.toString(),
105 keyValueFromThis.toString());
106 if (!doSubsume ) {
107 return false;
108 }
109 } // if ontologies are with the same url
110 } //if not null objects
111 else {
112 // incomplete feature set: missing ontology feature
113 return false;
114 }
115 } else {
116 /* processing without ontology awareness */
117 if (!keyValueFromThis.equals(keyValueFromAFeatureMap)) return false;
118 } // else
119
120 } // if
121 } // for
122
123 return true;
124 }//subsumes()
125
126 /** Tests if <b>this</b> featureMap object includes aFeatureMap features. <br>
127 * If the feature map contains <code>class</code> and (optionally) <code>ontology</code> features:<br>
128 * then the ontologyLR is used to provide ontology based subsume with respect to the subClassOf relations.
129 * @param ontologyLR an ontology to be used for the subsume
130 * @param aFeatureMap object which will be included or not in <b>this</b>
131 * FeatureMap obj.
132 * @return <code>true</code> if <b>this</b> includes aFeatureMap
133 * and <code>false</code> if not.
134 */
135 public boolean subsumes(Taxonomy ontologyLR, FeatureMap aFeatureMap) {
136
137 if (ontologyLR == null) {
138 return this.subsumes(aFeatureMap);
139 }
140
141 if (aFeatureMap == null)
142 return true;
143
144 if (this.size() < aFeatureMap.size())
145 return false;
146
147 SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl) aFeatureMap;
148
149 Object key;
150 Object keyValueFromAFeatureMap;
151 Object keyValueFromThis;
152
153 for (int i = 0; i < sfm.count; i++) {
154 key = sfm.theKeys[i];
155 keyValueFromAFeatureMap = sfm.theValues[i];
156 int v = super.getSubsumeKey(key);
157 if (v < 0)
158 return false;
159 keyValueFromThis = theValues[v]; //was: get(key);
160
161 if ( (keyValueFromThis == null && keyValueFromAFeatureMap != null) ||
162 (keyValueFromThis != null && keyValueFromAFeatureMap == null)
163 )
164 return false;
165
166 /*ontology aware subsume implementation based on the ontology LR
167 ontotext.bp*/
168 if ( (keyValueFromThis != null) && (keyValueFromAFeatureMap != null)) {
169
170 if (key.equals(LOOKUP_CLASS_FEATURE_NAME)) {
171 // ontology aware processing
172
173 try {
174
175 if (DEBUG) {
176 Out.prln("\nClass in rule: " + keyValueFromAFeatureMap.toString());
177 Out.prln("\nClass in annotation: " + keyValueFromThis.toString());
178 Out.prln("\nisSubClassOf: " +
179 ontologyLR.isSubClassOf(keyValueFromAFeatureMap.toString(),
180 keyValueFromThis.toString()));
181 }
182
183 return ontologyLR.isSubClassOf(keyValueFromAFeatureMap.toString(),
184 keyValueFromThis.toString());
185 } catch (Exception ex) {
186 throw new gate.util.GateRuntimeException(ex);
187 }
188 }
189 else {
190 /* processing without ontology awareness */
191 if (!keyValueFromThis.equals(keyValueFromAFeatureMap))
192 return false;
193 } // else
194
195 } // if
196 } // for
197
198 return true;
199 } //subsumes(ontology)
200
201
202 /** Tests if <b>this</b> featureMap object includes aFeatureMap but only
203 * for the those features present in the aFeatureNamesSet.
204 *
205 * However, if aFeatureMap contains a feature whose value is equal to
206 * gate.creole.ANNIEConstants.LOOKUP_CLASS_FEATURE_NAME (which is normally
207 * "class"), then GATE will attempt to match that feature using an ontology
208 * which it will try to retreive from a feature in both the feature map
209 * through which this method is called and in aFeatureMap. If these do not return
210 * identical ontologies, or if
211 * either feature map does not contain an ontology, then
212 * matching will fail, and this method will return false. In summary,
213 * this method will not work normally when aFeatureMap contains a feature
214 * with the name "class" if that feature is also in aFeatureNamesSet.
215 *
216 * @param aFeatureMap which will be included or not in <b>this</b>
217 * FeatureMap obj.If this param is null then it will return true.
218 * @param aFeatureNamesSet is a set of strings representing the names of the
219 * features that would be considered for subsumes. If aFeatureNamesSet is
220 * <b>null</b> then subsumes(FeatureMap) will be called.
221 * @return <code>true</code> if all features present in the aFeaturesNameSet
222 * from aFeatureMap are included in <b>this</b> obj, or <code>false</code>
223 * otherwise.
224 */
225 public boolean subsumes(FeatureMap aFeatureMap, Set aFeatureNamesSet){
226 // This means that all features are taken into consideration.
227 if (aFeatureNamesSet == null) return this.subsumes(aFeatureMap);
228 // null is included in everything
229 if (aFeatureMap == null) return true;
230 // This means that subsumes is supressed.
231 if (aFeatureNamesSet.isEmpty()) return true;
232
233 SimpleFeatureMapImpl sfm = (SimpleFeatureMapImpl)aFeatureMap;
234
235 Object key;
236 Object keyValueFromAFeatureMap;
237 Object keyValueFromThis;
238
239 for (int i = 0; i < sfm.count; i++) {
240 key = sfm.theKeys[i];
241
242 if (!aFeatureNamesSet.contains(key))
243 continue;
244
245 keyValueFromAFeatureMap = sfm.theValues[i];
246 keyValueFromThis = get(key);
247
248 if ( (keyValueFromThis == null && keyValueFromAFeatureMap != null) ||
249 (keyValueFromThis != null && keyValueFromAFeatureMap == null)
250 ) return false;
251
252 if ((keyValueFromThis != null) && (keyValueFromAFeatureMap != null)) {
253 if ( key.equals(LOOKUP_CLASS_FEATURE_NAME) ) {
254 /* ontology aware processing */
255 if (!aFeatureNamesSet.contains(LOOKUP_ONTOLOGY_FEATURE_NAME))
256 continue;
257
258 Object sfmOntoObj = sfm.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
259 Object thisOntoObj = this.get(LOOKUP_ONTOLOGY_FEATURE_NAME);
260 if (null!=sfmOntoObj && null!= thisOntoObj) {
261 if (sfmOntoObj.equals(thisOntoObj)) {
262 if (! ontologySubsume(
263 sfmOntoObj.toString(),
264 keyValueFromAFeatureMap.toString(),
265 keyValueFromThis.toString()))
266 return false;
267 } // if ontologies are with the same url
268 } //if not null objects
269 else {
270 // incomplete feature set: missing ontology feature
271 return false;
272 }
273 } else {
274 /*processing without ontology awareness*/
275 if (!keyValueFromThis.equals(keyValueFromAFeatureMap)) return false;
276 } //else
277 } // if values not null
278 } // for
279
280 return true;
281 }// subsumes()
282
283
284 /**
285 * Overriden to fire events, so that the persistent objects
286 * can keep track of what's updated
287 */
288 public Object put(Object key, Object value) {
289 Object result = super.put(key, value);
290 this.fireMapUpdatedEvent();
291 return result;
292 } // put
293
294 /**
295 * Overriden to fire events, so that the persistent objects
296 * can keep track of what's updated
297 */
298 public Object remove(Object key) {
299 Object result = super.remove(key);
300 this.fireMapUpdatedEvent();
301 return result;
302 } // remove
303
304 public void clear() {
305 super.clear();
306 //tell the world if they're listening
307 this.fireMapUpdatedEvent();
308 } // clear
309
310 // Views
311 public Object clone() {
312 return super.clone();
313 } //clone
314
315 public boolean equals(Object o) {
316 return super.equals(o);
317 } // equals
318
319 //////////////////THE EVENT HANDLING CODE//////////////
320 //Needed so an annotation can listen to its features//
321 //and update correctly the database//////////////////
322 private transient Vector mapListeners;
323 /**
324 * Removes a gate listener
325 */
326 public synchronized void removeFeatureMapListener(FeatureMapListener l) {
327 if (mapListeners != null && mapListeners.contains(l)) {
328 Vector v = (Vector) mapListeners.clone();
329 v.removeElement(l);
330 mapListeners = v;
331 }
332 } //removeFeatureMapListener
333 /**
334 * Adds a gate listener
335 */
336 public synchronized void addFeatureMapListener(FeatureMapListener l) {
337 Vector v = mapListeners == null ? new Vector(2) : (Vector)mapListeners.clone();
338 if (!v.contains(l)) {
339 v.addElement(l);
340 mapListeners = v;
341 }
342 } //addFeatureMapListener
343
344 /**
345 *
346 */
347 protected void fireMapUpdatedEvent () {
348 if (mapListeners != null) {
349 Vector listeners = mapListeners;
350 int count = listeners.size();
351 if (count == 0) return;
352 for (int i = 0; i < count; i++)
353 ((FeatureMapListener) listeners.elementAt(i)).featureMapUpdated();
354 }
355 }//fireMapUpdatedEvent
356
357
358 /**ontology enhanced subsume
359 * @param ontoUrl the url of the ontology to be used
360 * @return true if value1 subsumes value2 in the specified ontology */
361 protected boolean ontologySubsume(String ontoUrl,String value1,String value2) {
362 boolean result = false;
363 try {
364 URL url;
365 try {
366 url = new URL(ontoUrl);
367 } catch (MalformedURLException e){
368 throw new RuntimeException(
369 "\nin SimpleFeatureMapImpl on ontologySubsume()\n"
370 +e.getMessage()+"\n");
371 }
372
373 /* GET ONTOLOGY BY URL : a bit tricky reference
374 since the behaviour behind the getOntology method is
375 certainly static.
376 : should be temporary */
377 Taxonomy o = new OntologyImpl().getOntology(url);
378
379 result = o.isSubClassOf(value1, value2);
380
381 } catch (gate.creole.ResourceInstantiationException x) {
382 x.printStackTrace(Err.getPrintWriter());
383 } catch (gate.creole.ontology.NoSuchClosureTypeException clex) {
384 clex.printStackTrace(Err.getPrintWriter());
385 }
386 return result;
387 } // ontologySubsume
388
389 } // class SimpleFeatureMapImpl
390
391