HashGazetteer.java
001 /**
002  * (c) Copyright Ontotext Lab, Sirma Group Corp 2004
003  */
004 
005 package com.ontotext.gate.gazetteer;
006 
007 import gate.AnnotationSet;
008 import gate.Factory;
009 import gate.FeatureMap;
010 import gate.Resource;
011 import gate.creole.ExecutionException;
012 import gate.creole.ResourceInstantiationException;
013 import gate.creole.gazetteer.AbstractGazetteer;
014 import gate.creole.gazetteer.GazetteerException;
015 import gate.creole.gazetteer.GazetteerList;
016 import gate.creole.gazetteer.GazetteerNode;
017 import gate.creole.gazetteer.LinearDefinition;
018 import gate.creole.gazetteer.LinearNode;
019 import gate.creole.gazetteer.Lookup;
020 import gate.creole.gazetteer.MappingNode;
021 import gate.creole.metadata.CreoleResource;
022 import gate.creole.metadata.HiddenCreoleParameter;
023 import gate.creole.metadata.Sharable;
024 import gate.util.InvalidOffsetException;
025 import gate.util.LuckyException;
026 
027 import java.util.ArrayList;
028 import java.util.HashMap;
029 import java.util.HashSet;
030 import java.util.Iterator;
031 import java.util.List;
032 import java.util.Map;
033 import java.util.Set;
034 
035 @CreoleResource(name="Hash Gazetteer", icon="gazetteer", comment="A list lookup component implemented by OntoText Lab. The licence information is also available in licence.ontotext.html in the lib folder of GATE", helpURL="http://www.ontotext.com/downloads/index.html#gazetteer")
036 public class HashGazetteer extends AbstractGazetteer {
037   private static final long serialVersionUID = -4603155688378104052L;
038 
039   private ArrayList<Lookup> categoryList;
040 
041   private Map<LinearNode, GazetteerList> listsByNode;
042 
043   private Map<String, List<Lookup>> mapsList[];
044 
045   private AnnotationSet annotationSet = null;
046 
047   @Override
048   @SuppressWarnings({"unchecked","rawtypes"})
049   public Resource init() throws ResourceInstantiationException {
050     if(mapsList != null) {
051       // this is a duplicate - nothing to do
052     else {
053       if(listsURL == null)
054         throw new ResourceInstantiationException(
055                 "No URL provided for gazetteer creation!");
056 
057       try {
058         mapsList = new HashMap[1000];
059         definition = new LinearDefinition();
060         definition.setURL(listsURL);
061         definition.load();
062         int i = definition.size();
063         listsByNode = definition.loadLists();
064         categoryList = new ArrayList<Lookup>(i + 1);
065         Iterator<LinearNode> iterator = definition.iterator();
066         int j = 0;
067         LinearNode linearnode;
068         for(; iterator.hasNext(); readList(linearnode)) {
069           linearnode = iterator.next();
070           fireStatusChanged("Reading " + linearnode.toString());
071           fireProgressChanged((++j * 100/ i);
072         }
073 
074         fireProcessFinished();
075       }
076       catch(Exception exception) {
077         throw new ResourceInstantiationException(exception);
078       }
079     }
080     return this;
081   }
082 
083   /**
084    * Re-initialize this gazetteer by re-loading the configuration.
085    */
086   @Override
087   public void reInit() throws ResourceInstantiationException {
088     mapsList = null;
089     categoryList = null;
090     init();
091   }
092 
093   @Override
094   public void execute() throws ExecutionException {
095     if(document == nullthrow new ExecutionException("Document is null!");
096     annotationSet = document.getAnnotations(annotationSetName);
097 
098     String s = document.getContent().toString() " ";
099     if(!super.caseSensitive.booleanValue()) {
100       s = s.toUpperCase();
101     }
102 
103     int documentLength = s.length();
104     int j = 0;
105     int k = 0;
106 
107     StringBuffer stringbuffer = new StringBuffer();
108     boolean prevIsSymbol = false;
109     boolean prevIsDigit = false;
110     boolean prevIsLetter = false;
111 
112     // TODO what does this do, as it is only ever set to false
113     boolean flag11 = false;
114 
115     String s3 = "";
116     int i1 = 0;
117     int j1 = 0;
118 
119     for(int position = 0; position < documentLength; position++) {
120       char c = s.charAt(position);
121       boolean currIsWhitespace = Character.isWhitespace(c);
122       if(currIsWhitespace && stringbuffer.length() == 0) {
123         j++;
124         prevIsLetter = prevIsDigit = prevIsSymbol = flag11 = false;
125         continue;
126       }
127       if(currIsWhitespace && prevIsSymbol && stringbuffer.length() == 1) {
128         j += 2;
129         prevIsLetter = prevIsDigit = prevIsSymbol = flag11 = false;
130         stringbuffer.delete(0, stringbuffer.length());
131         continue;
132       }
133       boolean currIsLetter = Character.isLetter(c);
134       boolean currIsDigit = Character.isDigit(c);
135       boolean currIsSymbol = !currIsWhitespace && !currIsLetter && !currIsDigit;
136       boolean currIsLowerCase = Character.isLowerCase(c);
137       if(k <= j
138               && (currIsWhitespace || currIsSymbol || flag11
139                       && !currIsLowerCase || !prevIsLetter && currIsLetter))
140         k = position;
141       boolean flag13 = prevIsLetter
142               && (currIsDigit || currIsSymbol || currIsWhitespace)
143               || prevIsLetter && currIsLetter && flag11 && !currIsLowerCase
144               || prevIsDigit
145               && (currIsLetter || currIsSymbol || currIsWhitespace)
146               || prevIsSymbol;
147       if(position == documentLength - 1flag13 = true;
148       if(flag13) {
149         boolean flag16 = !currIsSymbol && !currIsDigit;
150         if(position == documentLength - 1flag16 = true;
151         String word = normalizeWhitespace(stringbuffer.toString());
152         int k1 = word.length();
153         flag16 &= k1 - j1 > 1;
154         j1 = k1;
155         if(i1 != j || !word.equals(s3)) {
156           int wordLength = word.length();
157           if(wordLength > 0) {
158             boolean flag14 = annotate(word, j, position, wordLength);
159             if(flag14) {
160               s3 = word;
161               i1 = j;
162             }
163             if(!flag14 && flag16 || documentLength - == position) {
164               if(k <= jk = position;
165               j = k;
166               position = k - 1;
167               stringbuffer.delete(0, stringbuffer.length());
168               continue;
169             }
170           }
171         }
172       }
173       stringbuffer.append(c);
174       prevIsDigit = currIsDigit;
175       prevIsLetter = currIsLetter;
176       prevIsSymbol = currIsSymbol;
177     }
178 
179     fireProcessFinished();
180     fireStatusChanged("Hash Gazetteer processing finished!");
181   }
182 
183   @Override
184   public boolean add(String word, Lookup lookup1) {
185     if(!super.caseSensitive.booleanValue()) {
186       word = word.toUpperCase();
187     }
188     
189     String s2 = removeTrailingSymbols(word);
190     if(!s2.equals(word)) add(s2, lookup1);
191     String s3 = word + " ";
192 
193     List<Lookup> arraylist = null;
194     int j = s3.length();
195 
196     boolean prevIsLetter = false;
197     boolean prevIsDigit = false;
198     boolean prevIsLowercase = false;
199 
200     String s4 = "";
201     Map<String, List<Lookup>> hashmap = null;
202     for(int k = 0; k < j; k++) {
203       char c = s3.charAt(k);
204       boolean currIsWhitespace = Character.isWhitespace(c);
205       boolean currIsDigit = Character.isDigit(c);
206       boolean currIsLetter = Character.isLetter(c);
207       boolean currIsSymbol = !currIsWhitespace && !currIsDigit && !currIsLetter;
208       boolean currIsLowercase = Character.isLowerCase(c);
209       boolean flag18 = prevIsLetter
210               && (currIsDigit || currIsSymbol || currIsWhitespace)
211               || prevIsLetter && currIsLetter && prevIsLowercase
212               && !currIsLowercase || prevIsDigit
213               && (currIsLetter || currIsSymbol || currIsWhitespace);
214 
215       //if we are on the last character
216       if(k + == jflag18 = true;
217       
218       if(flag18) {
219         s4 = normalizeWhitespace(s3.substring(0, k));
220         int i = s4.length();
221         if(mapsList[i== null) {
222           hashmap = new HashMap<String, List<Lookup>>();
223           mapsList[i= hashmap;
224         }
225         else {
226           hashmap = mapsList[i];
227         }
228         if(!hashmap.containsKey(s4)) hashmap.put(s4, null);
229       }
230       prevIsDigit = currIsDigit;
231       prevIsLetter = currIsLetter;
232 
233       prevIsLowercase = currIsLowercase;
234 
235     }
236 
237     arraylist = hashmap.get(s4);
238     if(null == arraylist) {
239       arraylist = new ArrayList<Lookup>(1);
240       arraylist.add(lookup1);
241     }
242     else if(!arraylist.contains(lookup1)) arraylist.add(lookup1);
243     hashmap.put(s4, arraylist);
244     return true;
245   }
246 
247   @Override
248   public Set<Lookup> lookup(String s) {
249     Set<Lookup> set = null;
250     String s1 = normalizeWhitespace(s);
251     int i = s1.length();
252     if(mapsList.length < ireturn set;
253     Map<String, List<Lookup>> hashmap = mapsList[i];
254     if(hashmap == null) {
255       return set;
256     }
257     else {
258       Set<Lookup> hashset = new HashSet<Lookup>(hashmap.get(s1));
259       return hashset;
260     }
261   }
262 
263   private boolean annotate(String word, int i, int documentPosition, int wordLength) {
264     if(wordLength >= mapsList.lengthreturn false;
265     Map<String, List<Lookup>> hashmap = mapsList[wordLength];
266     if(hashmap == nullreturn false;
267     if(!hashmap.containsKey(word)) return false;
268     List<Lookup> arraylist = hashmap.get(word);
269 
270     // TODO shouldn't this return false if arraylist is null?
271 
272     if(null != arraylist) {
273       for(Iterator<Lookup> iterator = arraylist.iterator(); iterator.hasNext();) {
274         Lookup lookup1 = iterator.next();
275         FeatureMap featuremap = Factory.newFeatureMap();
276         featuremap.put("majorType", lookup1.majorType);
277         if(null != lookup1.oClass && null != lookup1.ontology) {
278           featuremap.put("class", lookup1.oClass);
279           featuremap.put("ontology", lookup1.ontology);
280         }
281         if(null != lookup1.minorType) {
282           featuremap.put("minorType", lookup1.minorType);
283           if(null != lookup1.languages)
284             featuremap.put("language", lookup1.languages);
285         }
286         try {
287           annotationSet.add(new Long(i)new Long(documentPosition)"Lookup", featuremap);
288         }
289         catch(InvalidOffsetException invalidoffsetexception) {
290           throw new LuckyException(invalidoffsetexception.toString());
291         }
292       }
293 
294     }
295 
296     return true;
297   }
298 
299   /**
300    * Removes a string from the gazetteer
301    *
302    @param s the item to remove
303    @return true if the operation was successful
304    */
305   @Override
306   public boolean remove(String s) {
307 
308     String s1 = a(s);
309     int i = s1.length();
310     if(i > mapsList.lengthreturn false;
311     Map<String, List<Lookup>> hashmap = mapsList[i];
312     if(hashmap == nullreturn false;
313     if(hashmap.containsKey(s1)) {
314       hashmap.remove(s1);
315       return true;
316     }
317     return false;
318   }
319 
320   /**
321    * Works backwards through the String parameter removing each
322    * character until it encounters a letter, digit, or whitespace at
323    * which point it returns the truncated string.
324    *
325    @param s the String you wish to remove trailing symbols from
326    @return the truncated String that now ends in a letter, digit, or
327    *         whitespace character
328    */
329   private String removeTrailingSymbols(String s) {
330     for(int i = s.length() 1; i >= 0; i--) {
331       char c = s.charAt(i);
332       if(!Character.isLetter(c&& !Character.isDigit(c)
333               && !Character.isWhitespace(c))
334         s = s.substring(0, i);
335       else return s;
336     }
337 
338     return s;
339   }
340 
341   /**
342    * Normalizes the whitespace within the String instance by replacing
343    * any sequence of one or more whitespace characters with a single
344    * space. Not that any leading/trailing whitespace is also removed.
345    *
346    @param s the String to normalize
347    @return the normalized String
348    */
349   private String normalizeWhitespace(String s) {
350 
351     // this seems to be the same as String.replaceAll("\\s+", " ")
352 
353     StringBuffer stringbuffer = new StringBuffer();
354     s = s.trim();
355     char ac[] = s.toCharArray();
356     int i = s.length();
357     boolean prevWasWhitespace = false;
358     for(int j = 0; j < i; j++) {
359       char c = ac[j];
360 
361       boolean currIsWhitespace = Character.isWhitespace(c);
362 
363       if(currIsWhitespace && !prevWasWhitespace)
364         stringbuffer.append(' ');
365       else if(!currIsWhitespacestringbuffer.append(c);
366 
367       prevWasWhitespace = currIsWhitespace;
368     }
369 
370     return stringbuffer.toString();
371   }
372 
373   private String a(String s) {
374     StringBuffer stringbuffer = new StringBuffer();
375     boolean allLettersUppercase = true;
376     s = s.trim();
377     char ac[] = s.toCharArray();
378     int i = s.length();
379     if(i <= 1return s;
380     
381     char firstCharacter = ac[0];
382     stringbuffer.append(firstCharacter);
383     boolean flag2 = true;
384     boolean prevIsLetter = Character.isLetter(firstCharacter);
385     boolean prevNotLetterOrDigit = !Character.isLetterOrDigit(firstCharacter);
386 
387     boolean flag10 = true;
388     char c2 = 'p';
389 
390     for(int j = 1; j < i; j++) {
391       char currentCharacter = ac[j];
392       boolean currNotLetterOrDigit = !Character.isLetterOrDigit(currentCharacter);
393       boolean currIsWhitespace = Character.isWhitespace(currentCharacter);
394       boolean currIsLetter = Character.isLetter(currentCharacter);
395       boolean currIsDigit = Character.isDigit(currentCharacter);
396       
397       if(flag2) {
398         if(prevNotLetterOrDigit && currIsWhitespacecontinue;
399         flag2 = prevIsLetter && currNotLetterOrDigit || prevNotLetterOrDigit
400                 && currIsLetter;
401         if(currNotLetterOrDigit) {
402           if(c2 == 'p'c2 = currentCharacter;
403           flag2 = flag10 = c2 == currentCharacter;
404         }
405         if(j > && !flag2 && stringbuffer.length() 0) {
406           char c3 = stringbuffer.charAt(stringbuffer.length() 1);
407           stringbuffer.deleteCharAt(stringbuffer.length() 1);
408           stringbuffer.append(Character.toLowerCase(c3));
409         }
410       }
411       
412       if(currIsLetter || currIsDigit) {
413         if(currIsLetterallLettersUppercase &= Character.isUpperCase(currentCharacter);
414         if(!flag10currentCharacter = Character.toLowerCase(currentCharacter);
415         stringbuffer.append(currentCharacter);
416       }
417       else if(!flag2flag10 = false;
418       
419       prevIsLetter = currIsLetter;
420       prevNotLetterOrDigit = currNotLetterOrDigit;
421     }
422 
423     String s1 = stringbuffer.toString();
424     if(allLettersUppercases1 = s1.toUpperCase();
425     return s1;
426   }
427 
428   private void readList(LinearNode linearnodethrows GazetteerException {
429 
430     if(linearnode == null)
431       throw new GazetteerException("LinearNode node is null");
432 
433     GazetteerList gazetteerlist = listsByNode.get(linearnode);
434     if(gazetteerlist == null)
435       throw new GazetteerException("gazetteer list not found by node");
436 
437     String s = linearnode.getList();
438     String majorType = linearnode.getMajorType();
439     String minorType = linearnode.getMinorType();
440     String language = linearnode.getLanguage();
441 
442     Lookup lookup1 = new Lookup(s, majorType, minorType, language);
443 
444     if(mappingDefinition != null) {
445       MappingNode mappingnode = mappingDefinition.getNodeByList(s);
446       if(null != mappingnode) {
447         lookup1.oClass = mappingnode.getClassID();
448         lookup1.ontology = mappingnode.getOntologyID();
449       }
450     }
451 
452     lookup1.list = s;
453     categoryList.add(lookup1);
454 
455     Iterator<GazetteerNode> iterator = gazetteerlist.iterator();
456     String normalisedWord = null;
457 
458     for(; iterator.hasNext(); add(normalisedWord, lookup1)) {
459       String word = iterator.next().toString();
460       int wordLength = word.length();
461       for(int j = 0; j < wordLength; j++) {
462         if(j + != wordLength && !Character.isWhitespace(word.charAt(j))) continue;
463         if(j + == wordLengthj = wordLength;
464         normalisedWord = word.substring(0, j).trim();
465       }
466     }
467   }
468   
469   @Override
470   @HiddenCreoleParameter
471   public void setWholeWordsOnly(Boolean wholeWordsOnly) {
472     super.setWholeWordsOnly(wholeWordsOnly);
473   }
474   
475   @Override
476   @HiddenCreoleParameter
477   public void setLongestMatchOnly(Boolean longestMatchOnly) {
478     super.setLongestMatchOnly(longestMatchOnly);
479   }
480 
481   /**
482    * For internal use by the duplication mechanism.
483    */
484   @Sharable
485   public void setMapsList(Map<String, List<Lookup>> mapsList[]) {
486     this.mapsList = mapsList;
487   }
488 
489   /**
490    * For internal use by the duplication mechanism.
491    */
492   public Map<String, List<Lookup>>[] getMapsList() {
493     return mapsList;
494   }
495 
496   /**
497    * For internal use by the duplication mechanism.
498    */
499   @Sharable
500   public void setCategoryList(ArrayList<Lookup> categoryList) {
501     this.categoryList = categoryList;
502   }
503 
504   /**
505    * For internal use by the duplication mechanism.
506    */
507   public ArrayList<Lookup> getCategoryList() {
508     return categoryList;
509   }
510 }