Coreferencer.java
001 /*
002  *  Coreferencer.java
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  *  Marin Dimitrov, 18/Dec/2001
013  *
014  *  $Id: Coreferencer.java 17813 2014-04-11 12:54:36Z markagreenwood $
015  */
016 
017 package gate.creole.coref;
018 
019 import gate.Annotation;
020 import gate.AnnotationSet;
021 import gate.Document;
022 import gate.FeatureMap;
023 import gate.ProcessingResource;
024 import gate.Resource;
025 import gate.creole.AbstractLanguageAnalyser;
026 import gate.creole.ExecutionException;
027 import gate.creole.ResourceInstantiationException;
028 import gate.creole.metadata.CreoleParameter;
029 import gate.creole.metadata.CreoleResource;
030 import gate.creole.metadata.Optional;
031 import gate.creole.metadata.RunTime;
032 import gate.util.GateRuntimeException;
033 import gate.util.SimpleFeatureMapImpl;
034 
035 import java.util.ArrayList;
036 import java.util.HashMap;
037 import java.util.Iterator;
038 import java.util.List;
039 import java.util.Map;
040 
041 import org.apache.log4j.Logger;
042 
043 @CreoleResource(name="ANNIE Pronominal Coreferencer", comment="Pronominal Coreference resolution component.", helpURL="http://gate.ac.uk/userguide/sec:annie:pronom-coref", icon="pronominal-coreferencer")
044 public class Coreferencer extends AbstractLanguageAnalyser implements
045                                                           ProcessingResource {
046 
047   private static final long serialVersionUID = -2343178168872843239L;
048 
049   public static final String COREF_DOCUMENT_PARAMETER_NAME = "document";
050 
051   public static final String COREF_ANN_SET_PARAMETER_NAME = "annotationSetName";
052 
053   public static final String COREF_TYPE_FEATURE_NAME = "ENTITY_MENTION_TYPE";
054 
055   public static final String COREF_ANTECEDENT_FEATURE_NAME = "antecedent_offset";
056 
057   /** --- */
058   private static final boolean DEBUG = false;
059 
060   private static final Logger log = Logger.getLogger(Coreferencer.class);
061   
062   /** --- */
063   private PronominalCoref pronominalModule;
064 
065   /** --- */
066   public Coreferencer() {
067     this.pronominalModule = new PronominalCoref();
068   }
069 
070   /** Initialise this resource, and return it. */
071   @Override
072   public Resource init() throws ResourceInstantiationException {
073 
074     // load all submodules
075     pronominalModule.init();
076 
077     return this;
078   // init()
079 
080   /**
081    * Reinitialises the processing resource. After calling this method
082    * the resource should be in the state it is after calling init. If
083    * the resource depends on external resources (such as rules files)
084    * then the resource will re-read those resources. If the data used to
085    * create the resource has changed since the resource has been created
086    * then the resource will change too after calling reInit().
087    */
088   @Override
089   public void reInit() throws ResourceInstantiationException {
090     init();
091   // reInit()
092 
093   /** Set the document to run on. */
094   @Override
095   public void setDocument(Document newDocument) {
096 
097     // Assert.assertNotNull(newDocument);
098 
099     this.pronominalModule.setDocument(newDocument);
100     super.setDocument(newDocument);
101   }
102   
103   @Override
104   public void cleanup() {
105     super.cleanup();
106     pronominalModule.cleanup();
107   }
108 
109   /** --- */
110   @RunTime
111   @Optional
112   @CreoleParameter(comment="The annotation set to be used for the generated annotations")
113   public void setAnnotationSetName(String annotationSetName) {
114     this.pronominalModule.setAnnotationSetName(annotationSetName);
115   }
116 
117   /** --- */
118   public String getAnnotationSetName() {
119     return this.pronominalModule.getAnnotationSetName();
120   }
121 
122   /** --- */
123   @RunTime
124   @Optional
125   @CreoleParameter(comment="Whether or not to resolve it pronouns", defaultValue="false")
126   public void setResolveIt(Boolean newValue) {
127     this.pronominalModule.setResolveIt(newValue);
128   }
129 
130   /** --- */
131   public Boolean getResolveIt() {
132     return this.pronominalModule.getResolveIt();
133   }
134 
135   /**
136    * This method runs the coreferencer. It assumes that all the needed
137    * parameters are set. If they are not, an exception will be fired.
138    */
139   @Override
140   public void execute() throws ExecutionException {
141 
142     fireStatusChanged("Pronominal Coreferencer processing: "
143             + document.getName());
144     this.pronominalModule.execute();
145     generateCorefChains();
146     fireStatusChanged("Pronominal Coreferencer completed");
147   }
148 
149   /** --- */
150   private void generateCorefChains() throws GateRuntimeException {
151 
152     // 1. get the resolved corefs
153     Map<Annotation,Annotation> ana2ant = this.pronominalModule.getResolvedAnaphora();
154 
155     // 2. get the outout annotation set
156     String asName = getAnnotationSetName();
157     AnnotationSet outputSet = null;
158 
159     if(null == asName || asName.equals("")) {
160       outputSet = getDocument().getAnnotations();
161     }
162     else {
163       outputSet = getDocument().getAnnotations(asName);
164     }
165 
166     // 3. generate new annotations
167     Iterator<Map.Entry<Annotation, Annotation>> it = ana2ant.entrySet().iterator();
168     while(it.hasNext()) {
169       Map.Entry<Annotation,Annotation> currLink = it.next();
170       Annotation anaphor = currLink.getKey();
171       Annotation antecedent = currLink.getValue();
172 
173       if(DEBUG) {
174         AnnotationSet corefSet = getDocument().getAnnotations("COREF");
175         Long antOffset = new Long(0);
176 
177         if(null != antecedent) {
178           antOffset = antecedent.getStartNode().getOffset();
179         }
180 
181         FeatureMap features = new SimpleFeatureMapImpl();
182         features.put("antecedent", antOffset);
183         corefSet.add(anaphor.getStartNode(), anaphor.getEndNode()"COREF",
184                 features);
185       }
186 
187       // do we have antecedent?
188       if(null == antecedent) {
189         continue;
190       }
191 
192       // get the ortho-matches of the antecedent
193       
194       Object matchesObj =
195               antecedent.getFeatures().get(ANNOTATION_COREF_FEATURE_NAME);
196       @SuppressWarnings("unchecked")
197       List<Integer> matches =
198               matchesObj instanceof List ? (List<Integer>)matchesObj : null;
199       if(matchesObj != null && matches == null) {
200         log.warn("Illegal value for " + ANNOTATION_COREF_FEATURE_NAME
201                 " feature was ignored.");
202 
203       }
204         
205       if(null == matches) {
206         matches = new ArrayList<Integer>();
207         matches.add(antecedent.getId());
208         antecedent.getFeatures().put(ANNOTATION_COREF_FEATURE_NAME, matches);
209         // check if the document has a list of matches
210         // if yes, simply add the new list to it
211         // if not, create it and add the list of matches to it
212         if(document.getFeatures().containsKey(DOCUMENT_COREF_FEATURE_NAME)) {
213           @SuppressWarnings("unchecked")
214           Map<String,List<List<Integer>>> matchesMap = (Map<String,List<List<Integer>>>)document.getFeatures().get(
215                   DOCUMENT_COREF_FEATURE_NAME);
216           List<List<Integer>> matchesList = matchesMap.get(getAnnotationSetName());
217           if(matchesList == null) {
218             matchesList = new ArrayList<List<Integer>>();
219             matchesMap.put(getAnnotationSetName(), matchesList);
220           }
221           matchesList.add(matches);
222         }
223         else {
224           Map<String,List<List<Integer>>> matchesMap = new HashMap<String,List<List<Integer>>>();
225           List<List<Integer>> matchesList = new ArrayList<List<Integer>>();
226           matchesMap.put(getAnnotationSetName(), matchesList);
227           matchesList.add(matches);
228           document.getFeatures().put(DOCUMENT_COREF_FEATURE_NAME, matchesMap);
229         }// if else
230       }// if matches == null
231 
232       FeatureMap features = new SimpleFeatureMapImpl();
233       features.put(COREF_TYPE_FEATURE_NAME, "PRONOUN");
234       features.put(ANNOTATION_COREF_FEATURE_NAME, matches);
235       features.put(COREF_ANTECEDENT_FEATURE_NAME, antecedent.getStartNode()
236               .getOffset());
237 
238       //see if the annotation we want to add already exists
239       AnnotationSet existing = outputSet.get(antecedent.getType(), anaphor
240               .getStartNode().getOffset(), anaphor.getEndNode().getOffset());
241 
242       if(existing.size() 0) {
243         //if it exists simply update the existing annotation
244         Annotation annot = existing.iterator().next();
245         annot.getFeatures().putAll(features);
246         matches.add(annot.getId());
247       }
248       else {
249         //if it doesn't exist create a new annotation
250         matches.add(outputSet.add(anaphor.getStartNode(), anaphor.getEndNode(),
251                 antecedent.getType(), features));
252       }
253     }
254   }
255 
256   public String getInanimatedEntityTypes() {
257     return this.pronominalModule.getInanimatedEntityTypes();
258   }
259 
260   @RunTime
261   @Optional
262   @CreoleParameter(comment="List of annotation types for non animated entities", defaultValue="Organization;Location")
263   public void setInanimatedEntityTypes(String inanimatedEntityTypes) {
264     this.pronominalModule.setInanimatedEntityTypes(inanimatedEntityTypes);
265   }
266 
267 }