LinearDefinition.java
001 /*
002  *  LinearDefinition.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: LinearDefinition.java 17594 2014-03-08 12:07:09Z markagreenwood $
015  */
016 package gate.creole.gazetteer;
017 
018 import gate.creole.ResourceInstantiationException;
019 import gate.util.BomStrippingInputStreamReader;
020 import gate.util.Files;
021 import gate.util.GateRuntimeException;
022 
023 import java.io.BufferedReader;
024 import java.io.BufferedWriter;
025 import java.io.File;
026 import java.io.FileWriter;
027 import java.io.IOException;
028 import java.net.MalformedURLException;
029 import java.net.URL;
030 import java.util.ArrayList;
031 import java.util.Collection;
032 import java.util.HashMap;
033 import java.util.HashSet;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.ListIterator;
037 import java.util.Map;
038 import java.util.Set;
039 
040 import org.apache.commons.io.IOUtils;
041 
042 /**
043  * Represents a Linear Definition [lists.def] file <br>
044  * The normal usage of the class will be * construct it * setURL * load
045  * * change * store
046  */
047 public class LinearDefinition extends gate.creole.AbstractLanguageResource
048                                                                           implements
049                                                                           List<LinearNode> {
050 
051   private static final long serialVersionUID = 4050479036709221175L;
052 
053   /** the default encoding of the definition */
054   private final static String ENCODING = "UTF-8";
055 
056   /** the list of nodes */
057   private List<LinearNode> nodes = new ArrayList<LinearNode>();
058 
059   /** the URL of the definition */
060   private URL url;
061 
062   /** set of lists as strings */
063   private List<String> lists = new ArrayList<String>();
064 
065   /** the encoding of the list */
066   private String encoding = "UTF-8";
067 
068   /** a mapping between a list and a node */
069   private Map<String, LinearNode> nodesByList =
070           new HashMap<String, LinearNode>();
071 
072   /** a map of gazetteer lists by nodes. this is loaded on loadLists */
073   private Map<LinearNode, GazetteerList> gazListsByNode =
074           new HashMap<LinearNode, GazetteerList>();
075 
076   /** flag whether the definition has been modified after loading */
077   private boolean isModified = false;
078 
079   /**
080    * the separator used to delimit feature name-value pairs in gazetteer
081    * lists
082    */
083   private String separator;
084 
085   public LinearDefinition() {
086   }
087 
088   /**
089    * Sets the encoding of the linear def
090    
091    @param encod the encoding to be set
092    */
093   public void setEncoding(String encod) {
094     encoding = encod;
095   }
096 
097   /**
098    * Gets the encoding of the linear def
099    
100    @return the encoding of the list
101    */
102   public String getEncoding() {
103     return encoding;
104   }
105 
106   /**
107    * Loads the gazetteer lists and maps them to the nodes
108    
109    @return a map of nodes vs GazetteerLists
110    @throws ResourceInstantiationException when the resource cannot be
111    *           created
112    */
113   public Map<LinearNode, GazetteerList> loadLists()
114           throws ResourceInstantiationException {
115     return loadLists(false);
116   }
117 
118   /**
119    * Loads the gazetteer lists and maps them to the nodes
120    
121    @return a map of nodes vs GazetteerLists
122    @param isOrdered true if the feature maps used should be ordered
123    @throws ResourceInstantiationException when the resource cannot be
124    *           created
125    */
126   public Map<LinearNode, GazetteerList> loadLists(boolean isOrdered)
127           throws ResourceInstantiationException {
128     try {
129       gazListsByNode = new HashMap<LinearNode, GazetteerList>();
130       Iterator<LinearNode> inodes = nodes.iterator();
131       while(inodes.hasNext()) {
132         LinearNode node = inodes.next();
133 
134         GazetteerList list = new GazetteerList();
135         list.setSeparator(separator);
136         URL lurl = new URL(url, node.getList());
137         list.setURL(lurl);
138         list.setEncoding(encoding);
139         list.load(isOrdered);
140 
141         gazListsByNode.put(node, list);
142       // while inodes
143     catch(Exception ex) {
144       throw new ResourceInstantiationException(ex);
145     }
146     return gazListsByNode;
147   // loadLists()
148 
149   /**
150    * Loads a single gazetteer list given a name
151    
152    @param listName the name of the list to be loaded
153    @return the loaded gazetteer list
154    @throws ResourceInstantiationException
155    */
156   public GazetteerList loadSingleList(String listName)
157           throws ResourceInstantiationException {
158     return loadSingleList(listName, false);
159   }
160 
161   /**
162    * Loads a single gazetteer list given a name
163    
164    @param listName the name of the list to be loaded
165    @param isOrdered true if the feature maps used should be ordered
166    @return the loaded gazetteer list
167    @throws ResourceInstantiationException
168    */
169   public GazetteerList loadSingleList(String listName, boolean isOrdered)
170           throws ResourceInstantiationException {
171     GazetteerList list = new GazetteerList();
172     list.setSeparator(separator);
173     try {
174 
175       try {
176         URL lurl = new URL(url, listName);
177         list.setURL(lurl);
178         list.load(isOrdered);
179       catch(Exception x) {
180         String path = url.getPath();
181         int slash = path.lastIndexOf("/");
182         if(-!= slash) {
183           path = path.substring(0, slash + 1);
184         }
185 
186         File f = new File(path + listName);
187 
188         if(!f.exists()) f.createNewFile();
189 
190         URL lurl = new URL(url, listName);
191         list.setURL(lurl);
192         list.load(isOrdered);
193 
194       }
195 
196     catch(MalformedURLException murle) {
197       throw new ResourceInstantiationException(murle);
198     catch(IOException ioex) {
199       throw new ResourceInstantiationException(ioex);
200     }
201     return list;
202   // loadSingleList
203 
204   /**
205    * Gets the lists by node map
206    
207    @return a map of nodes vs lists
208    */
209   public Map<LinearNode, GazetteerList> getListsByNode() {
210     return gazListsByNode;
211   }
212 
213   /**
214    * Gets a map of lists names vs nodes
215    
216    @return a map of lists names vs nodes
217    */
218   public Map<String, LinearNode> getNodesByListNames() {
219     return nodesByList;
220   }
221 
222   /**
223    * Gets the value of the isModified flag.
224    
225    @return true if the definition has been modified
226    */
227   @Override
228   public boolean isModified() {
229     return isModified;
230   }
231 
232   public void setModified(boolean modified) {
233     isModified = modified;
234   }
235 
236   /**
237    * Gets the url of this linear definition
238    
239    @return the url of this linear definition
240    */
241   public URL getURL() {
242     return url;
243   }
244 
245   /**
246    * Sets the url of this linear definition
247    
248    @param aUrl the url of this linear definition
249    */
250   public void setURL(URL aUrl) {
251     url = aUrl;
252   }
253 
254   /**
255    * Loads linear definition if url is set
256    */
257   public void load() throws ResourceInstantiationException {
258     if(null == url) {
259       throw new ResourceInstantiationException("URL not set (null).");
260     }
261     BufferedReader defReader = null;
262     try {
263       if("file".equals(url.getProtocol())) {
264         File definitionFile = Files.fileFromURL(url);
265         // create an new definition file only if not existing
266         definitionFile.createNewFile();
267       }
268       defReader =
269               new BomStrippingInputStreamReader((url).openStream(), ENCODING);
270 
271       String line;
272       LinearNode node;
273       while(null != (line = defReader.readLine())) {
274         node = new LinearNode(line);
275 
276         this.add(node);
277 
278       // while
279       isModified = false;
280     catch(Exception x) {
281       throw new ResourceInstantiationException(x);
282     finally {
283       IOUtils.closeQuietly(defReader);
284     }
285   // load();
286 
287   /**
288    * Stores this to a definition file.
289    */
290   public void store() throws ResourceInstantiationException {
291     if(null == url) {
292       throw new ResourceInstantiationException("URL not set.(null)");
293     }
294     try {
295 
296       File fileo = Files.fileFromURL(url);
297       fileo.delete();
298       BufferedWriter defWriter = new BufferedWriter(new FileWriter(fileo));
299       Iterator<LinearNode> inodes = nodes.iterator();
300       while(inodes.hasNext()) {
301         defWriter.write(inodes.next().toString());
302         defWriter.newLine();
303       }
304       defWriter.close();
305       isModified = false;
306     catch(Exception x) {
307       throw new ResourceInstantiationException(x);
308     }
309 
310   // store();
311 
312   /**
313    * Gets gazetteer lists of this definition. note that a new list is
314    * created so the adding and removing of lists will not affect the
315    * internal members. Also there is no setLists method since the
316    * leading member of the class is nodes, and lists cannot be added
317    * individually without being associated with a node.
318    
319    @return a list of the gazetteer lists names
320    */
321   public List<String> getLists() {
322     return new ArrayList<String>(lists);
323   }
324 
325   /**
326    * get the nodes of the definition as a list
327    
328    @return the list of nodes
329    */
330   public List<LinearNode> getNodes() {
331     return new ArrayList<LinearNode>(nodes);
332   }
333 
334   /**
335    * Gets the set of all major types in this definition
336    
337    @return the set of all major types present in this definition
338    */
339   public Set<String> getMajors() {
340     Set<String> result = new HashSet<String>();
341     for(int i = 0; i < nodes.size(); i++) {
342       String maj = nodes.get(i).getMajorType();
343       if(null != majresult.add(maj);
344     }
345     return result;
346   // getMajors
347 
348   /**
349    * Gets the set of all minor types in this definition
350    
351    @return the set of all minor types present in this definition
352    */
353   public Set<String> getMinors() {
354     Set<String> result = new HashSet<String>();
355     for(int i = 0; i < nodes.size(); i++) {
356       String min = nodes.get(i).getMinorType();
357       if(null != minresult.add(min);
358     }
359     result.add("");
360     return result;
361   // getMinors()
362 
363   /**
364    * Gets the set of all languages in this definition
365    
366    @return the set of all languages present in this definition
367    */
368   public Set<String> getLanguages() {
369     Set<String> result = new HashSet<String>();
370     for(int i = 0; i < nodes.size(); i++) {
371       String lang = nodes.get(i).getLanguage();
372       if(null != langresult.add(lang);
373     }
374     result.add("");
375     return result;
376   // getMinors()
377 
378   /*---implementation of interface java.util.List---*/
379   @Override
380   public boolean addAll(int index, Collection<? extends LinearNode> c) {
381     int size = nodes.size();
382     Iterator<? extends LinearNode> iter = c.iterator();
383     LinearNode o;
384     while(iter.hasNext()) {
385       o = iter.next();
386       add(index, o);
387       // instance of linearnode
388     // while
389 
390     boolean result = (size != nodes.size());
391     isModified |= result;
392     return result;
393   }
394 
395   @Override
396   public LinearNode get(int index) {
397     return nodes.get(index);
398   }
399 
400   @Override
401   public LinearNode set(int index, LinearNode element) {
402     throw new UnsupportedOperationException(
403             "this method has not been implemented");
404   }
405 
406   /**
407    * Add a node to this LinearDefinition.
408    <p>
409    * NOTE: this will throw a GateRuntimeException if anything goes wrong
410    * when reading the list.
411    
412    @param index
413    @param ln
414    */
415   @Override
416   public void add(int index, LinearNode ln) {
417     String list = ln.getList();
418     if(!nodesByList.containsKey(list)) {
419       try {
420         GazetteerList gl = loadSingleList(list);
421         gazListsByNode.put(ln, gl);
422         nodes.add(index, ln);
423         nodesByList.put(list, ln);
424         lists.add(list);
425         isModified = true;
426       catch(ResourceInstantiationException x) {
427         throw new GateRuntimeException("Error loading list: " + list + ": "
428                 + x.getMessage(), x);
429       }
430     // if unique
431   }
432 
433   @Override
434   public LinearNode remove(int index) {
435     LinearNode result = null;
436     int size = nodes.size();
437     result = nodes.remove(index);
438     if(null != result) {
439       String list = result.getList();
440       lists.remove(list);
441       nodesByList.remove(list);
442       gazListsByNode.remove(result);
443       isModified |= (size != nodes.size());
444     }
445     return result;
446   }
447 
448   @Override
449   public int indexOf(Object o) {
450     return nodes.indexOf(o);
451   }
452 
453   @Override
454   public int lastIndexOf(Object o) {
455     return nodes.lastIndexOf(o);
456   }
457 
458   @Override
459   public ListIterator<LinearNode> listIterator() {
460     throw new UnsupportedOperationException("this method is not implemented");
461   }
462 
463   @Override
464   public ListIterator<LinearNode> listIterator(int index) {
465     throw new UnsupportedOperationException("this method is not implemented");
466   }
467 
468   @Override
469   public List<LinearNode> subList(int fromIndex, int toIndex) {
470     return nodes.subList(fromIndex, toIndex);
471   // class SafeIterator
472 
473   @Override
474   public int size() {
475     return nodes.size();
476   }
477 
478   @Override
479   public boolean isEmpty() {
480     return == nodes.size();
481   }
482 
483   @Override
484   public boolean contains(Object o) {
485     return nodes.contains(o);
486   }
487 
488   @Override
489   public Iterator<LinearNode> iterator() {
490     return new SafeIterator();
491   }
492 
493   @Override
494   public Object[] toArray() {
495     return nodes.toArray();
496   }
497 
498   @Override
499   public <T> T[] toArray(T[] a) {
500     return nodes.toArray(a);
501   }
502 
503   /**
504    * Adds a new node, only if its list is new and uniquely mapped to
505    * this node.
506    <p>
507    * NOTE: this will throw a GateRuntimeException if anything goes wrong
508    * reading the list.
509    
510    @param o a node
511    @return true if the list of node is not already mapped with another
512    *         node.
513    */
514   @Override
515   public boolean add(LinearNode o) {
516     boolean result = false;
517     String list = o.getList();
518     if(!nodesByList.containsKey(list)) {
519       try {
520         GazetteerList gl = loadSingleList(list);
521         gazListsByNode.put(o, gl);
522         result = nodes.add(o);
523         nodesByList.put(list, o);
524         lists.add(list);
525         isModified = true;
526       catch(ResourceInstantiationException x) {
527         throw new GateRuntimeException("Error loading list: " + list + ": "
528                 + x.getMessage(), x);
529         // result = false;
530       }
531     // if unique
532     return result;
533   // add()
534 
535   @Override
536   public boolean remove(Object o) {
537     boolean result = false;
538     int size = nodes.size();
539     if(instanceof LinearNode) {
540       result = nodes.remove(o);
541       String list = ((LinearNode)o).getList();
542       lists.remove(list);
543       nodesByList.remove(list);
544       gazListsByNode.remove(o);
545       isModified |= (size != nodes.size());
546     // if linear node
547     return result;
548   }// remove
549 
550   @Override
551   public boolean containsAll(Collection<?> c) {
552     return nodes.containsAll(c);
553   }
554 
555   @Override
556   public boolean addAll(Collection<? extends LinearNode> c) {
557     boolean result = false;
558     Iterator<? extends LinearNode> iter = c.iterator();
559     LinearNode o;
560     while(iter.hasNext()) {
561       o = iter.next();
562       result |= add(o);
563     // while
564     return result;
565   // addAll()
566 
567   @Override
568   public boolean removeAll(Collection<?> c) {
569     boolean result = false;
570     Iterator<?> iter = c.iterator();
571     Object o;
572     while(iter.hasNext()) {
573       o = iter.next();
574       result |= remove(o);
575     }
576     return result;
577   }// removeAll()
578 
579   @Override
580   public boolean retainAll(Collection<?> c) {
581     int aprioriSize = nodes.size();
582     List<LinearNode> scrap = new ArrayList<LinearNode>();
583 
584     LinearNode node;
585     Iterator<LinearNode> inodes = nodes.iterator();
586     while(inodes.hasNext()) {
587       node = inodes.next();
588       if(c.contains(node)) {
589         scrap.add(node);
590       }
591     // for
592 
593     removeAll(scrap);
594     isModified |= (aprioriSize != nodes.size());
595     return (aprioriSize != nodes.size());
596   }
597 
598   @Override
599   public void clear() {
600     nodes.clear();
601     lists.clear();
602     nodesByList.clear();
603     gazListsByNode.clear();
604     isModified = true;
605   }
606 
607   @Override
608   public int hashCode() {
609     final int prime = 31;
610     int result = 1;
611     result = prime * result + ((lists == null: lists.hashCode());
612     result = prime * result + ((nodes == null: nodes.hashCode());
613     result =
614             prime * result
615                     ((nodesByList == null: nodesByList.hashCode());
616     return result;
617   }
618 
619   @Override
620   public boolean equals(Object obj) {
621     if(this == objreturn true;
622     if(obj == nullreturn false;
623     if(getClass() != obj.getClass()) return false;
624     LinearDefinition other = (LinearDefinition)obj;
625     if(lists == null) {
626       if(other.lists != nullreturn false;
627     else if(!lists.equals(other.lists)) return false;
628     if(nodes == null) {
629       if(other.nodes != nullreturn false;
630     else if(!nodes.equals(other.nodes)) return false;
631     if(nodesByList == null) {
632       if(other.nodesByList != nullreturn false;
633     else if(!nodesByList.equals(other.nodesByList)) return false;
634     return true;
635   }
636 
637   /*---end of implementation of interface java.util.List---*/
638 
639   /*-----------internal classes -------------*/
640 
641   /**
642    * SafeIterator class provides an iterator which is safe to be
643    * iterated and objects removed from it
644    */
645   private class SafeIterator implements Iterator<LinearNode> {
646     private Iterator<LinearNode> iter = LinearDefinition.this.nodes.iterator();
647 
648     private boolean removeCalled = false;
649 
650     private LinearNode last = null;
651 
652     @Override
653     public boolean hasNext() {
654       return iter.hasNext();
655     }
656 
657     @Override
658     public LinearNode next() {
659       removeCalled = false;
660       last = iter.next();
661       return last;
662     }
663 
664     @Override
665     public void remove() {
666       if(!removeCalled && null != last) {
667         LinearDefinition.this.remove(last);
668       }// if possible remove
669       removeCalled = true;
670     // remove
671 
672   // class SafeIterator
673 
674   /**
675    @return the separator
676    */
677   public String getSeparator() {
678     return separator;
679   }
680 
681   /**
682    @param separator the separator to set
683    */
684   public void setSeparator(String separator) {
685     this.separator = separator;
686   }
687 
688 // class LinearDefinition