GazetteerList.java
001 /*
002  *  GazetteerList.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  *  borislav popov 02/2002
013  *
014  *  $Id: GazetteerList.java 17648 2014-03-13 11:16:47Z markagreenwood $
015  */
016 
017 package gate.creole.gazetteer;
018 
019 import java.io.*;
020 import java.net.URL;
021 import java.util.*;
022 import java.util.regex.Pattern;
023 
024 import org.apache.commons.io.IOUtils;
025 
026 import gate.creole.ResourceInstantiationException;
027 import gate.util.BomStrippingInputStreamReader;
028 import gate.util.Files;
029 import gate.util.GateRuntimeException;
030 
031 /**
032  * Gazetteer List provides the means for uploading, managing and storing
033  * the data in the gazetteer list files.
034  */
035 public class GazetteerList extends gate.creole.AbstractLanguageResource
036                                                                        implements
037                                                                        List<GazetteerNode> {
038 
039   private static final long serialVersionUID = -812795422822719315L;
040 
041   /** indicates list representation of the gazetteer list */
042   public final static int LIST_MODE = 0;
043 
044   /** indicates representation of the gaz list as a single string */
045   public final static int STRING_MODE = 1;
046 
047   /** the url of this list */
048   private URL url;
049 
050   /** the encoding of the list */
051   private String encoding = "UTF-8";
052 
053   /**
054    * indicates the current mode of the gazetteer list(e.g.
055    * STRING_MODE,LIST_MODE)
056    */
057   private int mode = 0;
058 
059   /**
060    * flag indicating whether the list has been modified after
061    * loading/storing
062    */
063   private boolean isModified = false;
064 
065   /** the entries of this list */
066   private List<GazetteerNode> entries = new ArrayList<GazetteerNode>();
067 
068   /** the content of this list */
069   private String content = null;
070 
071   /**
072    * the separator used to delimit feature name-value pairs in gazetteer
073    * lists
074    */
075   private String separator;
076 
077   /** create a new gazetteer list */
078   public GazetteerList() {
079   }
080 
081   /** @return true if the list has been modified after load/store */
082   @Override
083   public boolean isModified() {
084     return isModified;
085   }
086 
087   /**
088    * Sets the modified status of the current list
089    
090    @param modified is modified flag
091    */
092   public void setModified(boolean modified) {
093     isModified = modified;
094   }
095 
096   /**
097    * Retrieves the current mode of the gaz list
098    
099    @return the current mode
100    */
101   public int getMode() {
102     return mode;
103   }
104 
105   /**
106    * Sets mode of the gazetteer list
107    
108    @param m the mode to be set
109    */
110   public void setMode(int m) {
111     if(m != mode) {
112       switch(m) {
113         case LIST_MODE: {
114           mode = m;
115           updateContent(content);
116           break;
117         // LIST_MODE
118         case STRING_MODE: {
119           content = this.toString();
120           mode = m;
121           break;
122         // STRING_MODE
123         default{
124           throw new gate.util.GateRuntimeException("Invalid Mode =" + mode
125                   "\nValid modes are:\nLIST_MODE = " + LIST_MODE
126                   "\nSTRING_MODE = " + STRING_MODE);
127         // default
128       // switch
129     // only if different from the current
130   // setMode(int)
131 
132   /**
133    * Sets the encoding of the list
134    
135    @param encod the encoding to be set
136    */
137   public void setEncoding(String encod) {
138     encoding = encod;
139   }
140 
141   /**
142    * Gets the encoding of the list
143    
144    @return the encoding of the list
145    */
146   public String getEncoding() {
147     return encoding;
148   }
149 
150   /**
151    * Loads a gazetteer list
152    
153    @throws ResourceInstantiationException when the resource cannot be
154    *           created
155    */
156   public void load() throws ResourceInstantiationException {
157     load(false);
158   }
159 
160   /**
161    * Loads a gazetteer list
162    
163    @param isOrdered true if the feature maps used should be ordered
164    @throws ResourceInstantiationException when the resource cannot be
165    *           created
166    */
167   @SuppressWarnings("resource")
168   public void load(boolean isOrderedthrows ResourceInstantiationException {
169     BufferedReader listReader = null;
170     
171     try {
172       if(null == url) {
173         throw new ResourceInstantiationException("URL not specified (null).");
174       }
175 
176       listReader =
177               new BomStrippingInputStreamReader((url).openStream(), encoding);
178       String line;
179       int linenr = 0;
180       Pattern emptyPattern = Pattern.compile("\\s*");
181       while(null != (line = listReader.readLine())) {
182         linenr++;
183         if(emptyPattern.matcher(line).matches()) {
184           // skip empty line
185           continue;
186         }
187         GazetteerNode node = null;
188         try {
189           node = new GazetteerNode(line, separator, isOrdered);
190         catch(Exception ex) {
191           throw new GateRuntimeException("Could not read gazetteer entry "
192                   + linenr + " from URL " + getURL() ": " + ex.getMessage(),
193                   ex);
194         }
195         
196         entries.add(node);
197       // while
198 
199       listReader.close();
200     catch(Exception x) {
201       throw new ResourceInstantiationException(x.getClass() ":"
202               + x.getMessage(),x);
203     }
204     finally {
205       IOUtils.closeQuietly(listReader);
206     }
207     isModified = false;
208   // load ()
209 
210   /**
211    * Stores the list to the specified url
212    
213    @throws ResourceInstantiationException
214    */
215   public void store() throws ResourceInstantiationException {
216     try {
217       if(null == url) {
218         throw new ResourceInstantiationException("URL not specified (null)");
219       }
220 
221       File fileo = Files.fileFromURL(url);
222 
223       fileo.delete();
224       OutputStreamWriter listWriter =
225               new OutputStreamWriter(new FileOutputStream(fileo), encoding);
226       // BufferedWriter listWriter = new BufferedWriter(new
227       // FileWriter(fileo));
228       Iterator<GazetteerNode> iter = entries.iterator();
229       while(iter.hasNext()) {
230         listWriter.write(iter.next().toString());
231         listWriter.write(13);
232         listWriter.write(10);
233       }
234       listWriter.close();
235     catch(Exception x) {
236       throw new ResourceInstantiationException(x.getClass() ":"
237               + x.getMessage());
238     }
239     isModified = false;
240   // store()
241 
242   /**
243    * Sets the URL of the list
244    
245    @param theUrl the URL of the List
246    */
247   public void setURL(URL theUrl) {
248     url = theUrl;
249     isModified = true;
250   }
251 
252   /**
253    * Gets the URL of the list
254    
255    @return the URL of the list
256    */
257   public URL getURL() {
258     return url;
259   }
260 
261   /**
262    @return the seperator
263    */
264   public String getSeparator() {
265     return separator;
266   }
267 
268   /**
269    @param separator the separator to set
270    */
271   public void setSeparator(String separator) {
272     this.separator = separator;
273   }
274 
275   /*--------------implementation of java.util.List--------------------*/
276   @Override
277   public int size() {
278     return entries.size();
279   }
280 
281   @Override
282   public boolean isEmpty() {
283     return (== entries.size());
284   }
285 
286   @Override
287   public boolean contains(Object o) {
288     return entries.contains(o);
289   // contains()
290 
291   /**
292    * Gets an iterator over the list. It is not dangerous if the iterator
293    * is modified since there are no dependencies of entries to other
294    * members
295    */
296   @Override
297   public Iterator<GazetteerNode> iterator() {
298     return entries.iterator();
299   }
300 
301   @Override
302   public Object[] toArray() {
303     return entries.toArray();
304   }
305 
306   @Override
307   public <T> T[] toArray(T[] a) {
308     return entries.toArray(a);
309   }
310 
311   @Override
312   public boolean add(GazetteerNode o) {
313     boolean result = entries.add(o);
314     isModified |= result;
315     return result;
316   // add()
317 
318   @Override
319   public boolean remove(Object o) {
320     boolean result = entries.remove(o);
321     isModified |= result;
322     return result;
323   }
324 
325   @Override
326   public boolean containsAll(Collection<?> c) {
327     return entries.containsAll(c);
328   }
329 
330   /**
331    * Adds entire collection
332    
333    @param c a collection to be addded
334    @return true if all the elements where Strings and all are
335    *         sucessfully added
336    */
337   @Override
338   public boolean addAll(Collection<? extends GazetteerNode> c) {
339     Iterator<? extends GazetteerNode> iter = c.iterator();
340     GazetteerNode o;
341     boolean result = false;
342 
343     while(iter.hasNext()) {
344       o = iter.next();
345       result |= entries.add(o);
346     // while
347     isModified |= result;
348 
349     return result;
350   // addAll(Collection)
351 
352   @Override
353   public boolean addAll(int index, Collection<? extends GazetteerNode> c) {
354     boolean result = entries.addAll(index, c);
355     isModified |= result;
356     return result;
357   // addAll(int,Collection)
358 
359   @Override
360   public boolean removeAll(Collection<?> c) {
361     boolean result = entries.removeAll(c);
362     isModified |= result;
363     return result;
364   }
365 
366   @Override
367   public boolean retainAll(Collection<?> c) {
368     boolean result = entries.retainAll(c);
369     isModified |= result;
370     return result;
371   }
372 
373   @Override
374   public void clear() {
375     if(< entries.size()) isModified = true;
376     entries.clear();
377   }
378 
379   @Override
380   public int hashCode() {
381     final int prime = 31;
382     int result = 1;
383     result = prime * result + ((entries == null: entries.hashCode());
384     return result;
385   }
386 
387   @Override
388   public boolean equals(Object obj) {
389     if(this == objreturn true;
390     if(obj == nullreturn false;
391     if(getClass() != obj.getClass()) return false;
392     GazetteerList other = (GazetteerList)obj;
393     if(entries == null) {
394       if(other.entries != nullreturn false;
395     else if(!entries.equals(other.entries)) return false;
396     return true;
397   }
398 
399   @Override
400   public GazetteerNode get(int index) {
401     return entries.get(index);
402   }
403 
404   @Override
405   public GazetteerNode set(int index, GazetteerNode element) {
406     isModified = true;
407     return entries.set(index, element);
408   }
409 
410   @Override
411   public void add(int index, GazetteerNode element) {
412     isModified = true;
413     entries.add(index, element);
414   }
415 
416   @Override
417   public GazetteerNode remove(int index) {
418     int size = entries.size();
419     GazetteerNode result = entries.remove(index);
420     isModified |= (size != entries.size());
421     return result;
422   }
423 
424   @Override
425   public int indexOf(Object o) {
426     return entries.indexOf(o);
427   }
428 
429   @Override
430   public int lastIndexOf(Object o) {
431     return entries.lastIndexOf(o);
432   }
433 
434   @Override
435   public ListIterator<GazetteerNode> listIterator() {
436     return entries.listIterator();
437   }
438 
439   @Override
440   public ListIterator<GazetteerNode> listIterator(int index) {
441     return entries.listIterator(index);
442   }
443 
444   @Override
445   public List<GazetteerNode> subList(int fromIndex, int toIndex) {
446     return entries.subList(fromIndex, toIndex);
447   }
448 
449   /**
450    * Retrieves the string representation of the gaz list according to
451    * its mode. If {@link #LIST_MODE} then all the entries are dumped
452    * sequentially to a string. If {@link #STRING_MODE} then the content
453    * (a string) of the gaz list is retrieved.
454    
455    @return the string representation of the gaz list
456    */
457   @Override
458   public String toString() {
459     String stres = null;
460     switch(mode) {
461       case LIST_MODE: {
462         StringBuffer result = new StringBuffer();
463         String entry = null;
464         for(int i = 0; i < entries.size(); i++) {
465           GazetteerNode node = entries.get(i);
466           entry = node.getEntry().trim();
467           if(entry.length() 0) {
468             result.append(entry);
469             Map<String,Object> featureMap = node.getFeatureMap();
470             if(featureMap != null && (featureMap.size() 0)) {
471               result.append(node.featureMapToString(featureMap));
472             }
473             result.append("\n");
474           }// if
475         }// for
476         stres = result.toString();
477         break;
478       }
479       case STRING_MODE: {
480         stres = content;
481         break;
482       }
483       default{
484         throw new gate.util.GateRuntimeException("Invalid Mode =" + mode
485                 "\nValid modes are:\nLIST_MODE = " + LIST_MODE
486                 "\nSTRING_MODE = " + STRING_MODE);
487       }
488     // switch
489     return stres;
490   }// toString()
491 
492   /**
493    * Updates the content of the gaz list with the given parameter.
494    * Depends on the mode of the gaz list. In the case of
495    {@link #LIST_MODE} the new content is parsed and loaded as single
496    * nodes through the {@link java.util.List} interface. In the case of
497    {@link #STRING_MODE} the new content is stored as a String and is
498    * not parsed.
499    
500    @param newContent the new content of the gazetteer list
501    */
502   public void updateContent(String newContent) {
503     switch(mode) {
504       case STRING_MODE: {
505         content = newContent;
506         break;
507       }
508       case LIST_MODE: {
509         BufferedReader listReader;
510         listReader = new BufferedReader(new StringReader(newContent));
511         String line;
512         List<GazetteerNode> tempEntries = new ArrayList<GazetteerNode>();
513         try {
514           while(null != (line = listReader.readLine())) {
515             tempEntries.add(new GazetteerNode(line, separator));
516           // while
517           listReader.close();
518         catch(IOException x) {
519           /** should never be thrown */
520           throw new gate.util.LuckyException("IOException :" + x.getMessage());
521         }
522 
523         isModified = !tempEntries.equals(entries);
524         clear();
525         entries = tempEntries;
526         break;
527       // LIST_MODE
528       default{
529         throw new gate.util.GateRuntimeException("Invalid Mode =" + mode
530                 "\nValid modes are:\nLIST_MODE = " + LIST_MODE
531                 "\nSTRING_MODE = " + STRING_MODE);
532       }// default
533     // switch mode
534   // updateContent(String)
535 
536 // Class GazetteerList