Constraint.java
001 /*
002  *  Constraint.java - transducer class
003  *
004  *  Copyright (c) 1995-2012, 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, 24/07/98
013  *
014  *  $Id: Constraint.java 17597 2014-03-08 15:19:43Z markagreenwood $
015  */
016 
017 package gate.jape;
018 
019 import gate.Annotation;
020 import gate.AnnotationSet;
021 import gate.Factory;
022 import gate.FeatureMap;
023 import gate.creole.ontology.Ontology;
024 import gate.jape.constraint.AnnotationFeatureAccessor;
025 import gate.jape.constraint.ConstraintPredicate;
026 import gate.jape.constraint.EqualPredicate;
027 import gate.util.SimpleFeatureMapImpl;
028 
029 import java.io.Serializable;
030 import java.util.ArrayList;
031 import java.util.Collection;
032 import java.util.Iterator;
033 import java.util.List;
034 import java.util.Map;
035 
036 /**
037  * A set of predicates/expressions that apply to a single
038  {@link Annotation} type. It doesn't extend PatternElement, even
039  * though it has to "match", because a set of Constraint must be applied
040  * together in order to avoid doing separate selectAnnotations calls for
041  * each one.
042  <p>
043  <b>Matching Logic:</b>The matching function of a non-negated
044  * constraint can be defined as, "There exists an annotation for which
045  * all of the predicates are true." Thus, given a collection of
046  * annotations, only a single annotation must meet all of the predicates
047  * of the constraint in order for the match to be successful.
048  <p>
049  <b>Negation:</b> A negated constraint means strictly the opposite -
050  * "There does not exist an annotation for which any of these predicates
051  * are true." Negation does <b>not</b> mean "There exists an annotation
052  * for which all of these predicates are false." Thus, negation makes
053  * more intuitive sense when thought of as applying to a set of
054  * annotations rather than to an individual annotation.
055  */
056 public class Constraint implements JapeConstants, gate.creole.ANNIEConstants,
057                        Serializable, Cloneable {
058 
059   private static final long serialVersionUID = -7856281017609822483L;
060 
061   /** Construction from annot type string */
062   public Constraint(String annotType) {
063     this.annotType = annotType;
064   // Construction from annot type
065 
066   /**
067    * Construction from annot type and FeatureMap. Creates
068    {@link EqualPredicate}s for each feature in the map
069    */
070   public Constraint(String annotType, FeatureMap attrs) {
071     this(annotType);
072 
073     for(Map.Entry<Object, Object> entry : attrs.entrySet()) {
074       addAttribute(entry.getKey().toString(), entry.getValue());
075     }
076   // Construction from annot type and attribute sequence
077 
078   /** The type of annotation we're looking for. */
079   private String annotType;
080 
081   /** Are we negated? */
082   private boolean negated = false;
083 
084   /** Set negation. */
085   public void negate() {
086     negated = true;
087   }
088 
089   /** Change the sign of the negation flag. */
090   public void changeSign() {
091     negated = !negated;
092   }
093 
094   /** Access to negation flag. */
095   public boolean isNegated() {
096     return negated;
097   }
098 
099   /** Get the type of annotation we're looking for. */
100   public String getAnnotType() {
101     return annotType;
102   }
103 
104   /**
105    * The list of predicates that must match the annotation features.
106    */
107   private List<ConstraintPredicate> predicates = new ArrayList<ConstraintPredicate>();
108 
109   /** Get the attributes that must be present on the matched annotation. */
110   public List<ConstraintPredicate> getAttributeSeq() {
111     return predicates;
112   }
113 
114   /** Predicate that acts on class feature, if one is set. */
115   protected ConstraintPredicate ontLookupClassPred;
116 
117   /**
118    * FeatureMap that may contain ontology-related features and values
119    * pulled from any predicates that operate on those features
120    */
121   protected FeatureMap ontFeatureMap;
122 
123   /** Add an attribute. */
124   public void addAttribute(ConstraintPredicate attr) {
125     predicates.add(attr);
126 
127     // check for LOOKUP_CLASS_FEATURE_NAME and
128     // LOOKUP_ONTOLOGY_FEATURE_NAME
129     // predicates and store some information about them once so we don't
130     // have to look it up on every matches call.
131     if(attr.getAccessor() instanceof AnnotationFeatureAccessor) {
132       String featureName = (String)attr.getAccessor().getKey();
133       if(featureName.equals(LOOKUP_CLASS_FEATURE_NAME)) {
134         ontLookupClassPred = attr;
135         getOntFeatureMap().put(LOOKUP_CLASS_FEATURE_NAME,
136                 ontLookupClassPred.getValue());
137         if(!attr.getOperator().equals(ConstraintPredicate.EQUAL))
138           gate.util.Err
139                   .println("Warning: If an ontology is specified at runtime, "
140                          "Ontology class feature will be compared using "
141                          "ontology subsumption, not " + attr.getOperator());
142       }
143       else if(featureName.equals(LOOKUP_ONTOLOGY_FEATURE_NAME)) {
144         getOntFeatureMap().put(LOOKUP_ONTOLOGY_FEATURE_NAME, attr.getValue());
145       }
146     }
147 
148   // addAttribute
149 
150   /**
151    * Generate a FeatureMap to perform ontology-related compare.
152    */
153   protected FeatureMap getOntFeatureMap() {
154     if(ontFeatureMap == nullontFeatureMap = new SimpleFeatureMapImpl();
155 
156     return ontFeatureMap;
157   }
158 
159   /** Create and add an attribute. */
160   public void addAttribute(String name, Object value) {
161     ConstraintPredicate attr = Factory.getConstraintFactory().createPredicate(
162             name, value);
163     addAttribute(attr);
164   // addAttribute
165 
166   /**
167    * Add all predicates from the given collection to this object. Does
168    * not remove or replace any existing predicates.
169    *
170    @param attrs
171    */
172   public void addAttributes(Collection<ConstraintPredicate> attrs) {
173     // don't just call addAll because we want to check for
174     // ontology-related features
175     for(ConstraintPredicate pred : attrs) {
176       addAttribute(pred);
177     }
178   }
179 
180   /**
181    * Need cloning for processing of macro references. See comments on
182    <CODE>PatternElement.clone()</CODE>
183    */
184   @Override
185   public Object clone() {
186     Constraint newC = null;
187     try {
188       newC = (Constraint)super.clone();
189     }
190     catch(CloneNotSupportedException e) {
191       throw (new InternalError(e.toString()));
192     }
193     newC.annotType = annotType;
194     newC.predicates = new ArrayList<ConstraintPredicate>(predicates);
195     return newC;
196   // clone
197 
198   /**
199    * Returns a boolean value indicating whether this Constraint is
200    * equivalent to the given Constraint. If the given object is not a
201    * Constraint, compares the two objects using
202    <CODE>Object.equals()</CODE>.
203    */
204   @Override
205   public boolean equals(Object other) {
206     if(!(other instanceof Constraint)) return super.equals(other);
207     Constraint o = (Constraint)other;
208 
209     return (o.negated == negated && o.annotType.equals(annotType&& o.predicates
210             .equals(predicates));
211   }
212 
213   /**
214    * Returns an integer hash code for this object.
215    */
216   @Override
217   public int hashCode() {
218     int hashCode = negated ? 37 17;
219     hashCode = 37 * hashCode + annotType.hashCode();
220     hashCode = 37 * hashCode + predicates.hashCode();
221     return hashCode;
222   }
223 
224   /**
225    * Finish: replace dynamic data structures with Java arrays; called
226    * after parsing.
227    */
228   public void finish() {
229     /*
230      * if(attrs1 == null || attrs1.size() == 0) { attrs2 = new
231      * JdmAttribute[0]; attrs1 = null; return; } int attrsLen =
232      * attrs1.size(); attrs2 = new JdmAttribute[attrsLen];
233      *
234      * int i = 0; //for(Enumeration e = attrs1.getElements();
235      * e.hasMoreElements(); i++) { // attrs2[i] = (JdmAttribute)
236      * e.nextElement(); //} Iterator iter = attrs1.keySet().iterator();
237      * while(iter.hasNext()) { String name = (String) iter.next();
238      * Object value = attrs1.get(name); attrs2[i++] = new
239      * JdmAttribute(name, value); } attrs1 = null;
240      */
241   // finish
242 
243   /** Create a string representation of the object. */
244   @Override
245   public String toString() {
246     return getDisplayString("Constraint: ");
247   }
248 
249   /** Create a string representation of the object. */
250   public String getDisplayString(String prefix) {
251     StringBuffer buf = new StringBuffer(prefix);
252     if(negatedbuf.append("!");
253     buf.append(annotType);
254     if(!predicates.isEmpty()) {
255       buf.append("(");
256       buf.append(getAttributesString());
257       buf.append(")");
258     }
259     return buf.toString();
260   // toString
261 
262   /**
263    * Returns string representation of all the attributes that is
264    * appropriate for display.
265    */
266   public String getAttributesString() {
267     StringBuffer retVal = new StringBuffer();
268 
269     for(Iterator<ConstraintPredicate> iter = predicates.iterator(); iter
270             .hasNext();) {
271       ConstraintPredicate attr = iter.next();
272       retVal.append(attr);
273       if(iter.hasNext()) retVal.append(",");
274     }
275 
276     return retVal.toString();
277   }
278 
279   public String shortDesc() {
280     return getDisplayString("");
281   // shortDesc
282 
283   /**
284    * Invoke {@link #matches(Annotation, Ontology, AnnotationSet)} on all provided
285    * annotations.
286    *
287    @param annots collection of Annotations to test
288    @param ontology optional Ontology to compare ont-specific features
289    @return Collection of annotations which matches successfully
290    *         against predicates.
291    */
292   public List<Annotation> matches(Collection<Annotation> annots,
293           Ontology ontology, AnnotationSet context) {
294     List<Annotation> retVal = new ArrayList<Annotation>();
295 
296     if(annots == nullreturn retVal;
297 
298     for(Annotation annot : annots) {
299       if(matches(annot, ontology, context)) {
300         retVal.add(annot);
301         // small optimization - if constraint is negated and there is a
302         // match,
303         // don't bother checking all the other annots since a negated
304         // constraint
305         // fails if even a single annotation matches it.
306         if(negatedbreak;
307       }
308     }
309 
310     return retVal;
311   }
312 
313   /**
314    * Test if an annotation is of the proper type for this constraint and
315    * if it complies with the {@link ConstraintPredicate}s of this
316    * constraint.
317    *
318    @param annot an Annotation
319    @param ontologyLR optional ontology to use when comparing
320    *          ont-related features
321    @return <code>true</code> if the annotation is of the proper type
322    *         and matches all predicates. If the constraint is negated,
323    *         an annotation need only match a single predicate to return
324    *         true.
325    */
326   public final boolean matches(Annotation annot, Ontology ontologyLR, 
327           AnnotationSet context) {
328     if(annot == nullreturn false;
329     if(!annot.getType().equals(getAnnotType())) return false;
330 
331     // if no predicates, then we have a match based solely on the annot
332     // type.
333     if(predicates == null || predicates.isEmpty()) return true;
334 
335     for(ConstraintPredicate predicate : predicates) {
336       boolean successful = false;
337       try {
338         // do some special checking if this predicate deals with the
339         // ontology.  Note that we assume the operator for the predicate
340         // was ==. We issue a warning when the predicate is first set
341         // if it's anything else.
342         if(predicate == ontLookupClassPred && ontologyLR != null)
343           successful = annot.getFeatures().subsumes(ontologyLR, ontFeatureMap);
344         else successful = predicate.matches(annot, context);
345       catch(JapeException je) {
346         gate.util.Err.println(je.getMessage());
347       }
348       if(!successfulreturn false;
349       // else, keep checking the rest of the predicates
350     // checked all predicates
351     return true;
352   }
353 
354   /**
355    * Test if an annotation is of the proper type for this constraint and
356    * if it complies with the {@link ConstraintPredicate}s of this
357    * constraint.
358    *
359    @param annot a Annotation
360    @return <code>true</code> if the annotation is of the proper type
361    *         and matches all predicates. If the constraint is negated,
362    *         an annotation need only match a single predicate to return
363    *         true.
364    */
365   public boolean matches(Annotation annot, AnnotationSet context) {
366     return matches(annot, null, context);
367   }
368 
369 // class Constraint
370 
371 // $Log$
372 // Revision 1.14 2006/01/06 22:37:24 kwilliams
373 // Implement equals(Object) and hashCode() so we can usefully be put
374 // into Sets, HashMaps, etc.
375 //
376 // Revision 1.13 2006/01/06 22:03:04 kwilliams
377 // Define other constructors in terms of Constraint(String,FeatureMap)
378 //
379 // Revision 1.12 2005/07/15 15:37:32 valyt
380 // New toString() method from Ken Williams
381 //
382 // Revision 1.11 2005/01/11 13:51:36 ian
383 // Updating copyrights to 1998-2005 in preparation for v3.0
384 //
385 // Revision 1.10 2004/07/21 17:10:07 akshay
386 // Changed copyright from 1998-2001 to 1998-2004
387 //
388 // Revision 1.9 2004/03/25 13:01:14 valyt
389 // Imports optimisation throughout the Java sources
390 // (to get rid of annoying warnings in Eclipse)
391 //
392 // Revision 1.8 2001/09/13 12:09:49 kalina
393 // Removed completely the use of jgl.objectspace.Array and such.
394 // Instead all sources now use the new Collections, typically ArrayList.
395 // I ran the tests and I ran some documents and compared with keys.
396 // JAPE seems to work well (that's where it all was). If there are
397 // problems
398 // maybe look at those new structures first.
399 //
400 // Revision 1.7 2000/11/08 16:35:02 hamish
401 // formatting
402 //
403 // Revision 1.6 2000/10/26 10:45:30 oana
404 // Modified in the code style
405 //
406 // Revision 1.5 2000/10/16 16:44:33 oana
407 // Changed the comment of DEBUG variable
408 //
409 // Revision 1.4 2000/10/10 15:36:35 oana
410 // Changed System.out in Out and System.err in Err;
411 // Added the DEBUG variable seted on false;
412 // Added in the header the licence;
413 //
414 // Revision 1.3 2000/05/25 16:10:41 valyt
415 // JapeGUI is working
416 //
417 // Revision 1.2 2000/04/20 13:26:41 valyt
418 // Added the graph_drawing library.
419 // Creating of the NFSM and DFSM now works.
420 //
421 // Revision 1.1 2000/02/23 13:46:05 hamish
422 // added
423 //
424 // Revision 1.1.1.1 1999/02/03 16:23:01 hamish
425 // added gate2
426 //
427 // Revision 1.8 1998/11/05 13:36:30 kalina
428 // moved to use array of JdmAttributes for selectNextAnnotation instead
429 // of a sequence
430 //
431 // Revision 1.7 1998/11/01 22:35:56 kalina
432 // attribute seq hashtable mod
433 //
434 // Revision 1.6 1998/09/23 12:48:02 hamish
435 // negation added; noncontiguous BPEs disallowed
436 //
437 // Revision 1.5 1998/08/12 15:39:34 hamish
438 // added padding toString methods
439 //
440 // Revision 1.4 1998/07/31 13:12:14 mks
441 // done RHS stuff, not tested
442 //
443 // Revision 1.3 1998/07/30 11:05:15 mks
444 // more jape
445 //
446 // Revision 1.2 1998/07/29 11:06:55 hamish
447 // first compiling version
448 //
449 // Revision 1.1.1.1  1998/07/28 16:37:46  hamish
450 // gate2 lives