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 == null) return 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 < 0) return 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 = (SimpleFeatureMapImpl) aFeatureMap;
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 == null) return 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 == null) return this.subsumes(aFeatureMap);
252 // null is included in everything
253 if (aFeatureMap == null) return 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 = (Vector) mapListeners.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 == 0) return;
378 for (int i = 0; i < count; i++)
379 ((FeatureMapListener) listeners.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
|