GazetteerNode.java
001 /*
002  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *
005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
006  *  software, licenced under the GNU Library General Public License,
007  *  Version 2, June 1991 (in the distribution as file licence.html,
008  *  and also available at http://gate.ac.uk/gate/licence.html).
009  *
010  *  $Id: GazetteerNode.java 17593 2014-03-08 10:03:19Z markagreenwood $
011  */
012 
013 package gate.creole.gazetteer;
014 
015 import gate.util.GateRuntimeException;
016 
017 import java.util.*;
018 
019 /**
020  <p>
021  * A node in a gazetteer list allowing an arbitary amount of features
022  * to be added as metadata to an entry, e.g.:
023  </p>
024  <p>
025  * With the separator set to '\t', if a gazetteer entry looked like this:
026  </p>
027  <pre>Vodaphone&#09;type=mobile phone company</pre> 
028  <p>
029  * Then the GazetteerNode would consist of an entry "Vodaphone", with a featureMap
030  * containing the key "type", mapped to "mobile phone company".
031  *  </p>
032  @author JLy
033  *
034  */
035 public class GazetteerNode {
036   /** The gazetteer entry */
037   private String entry;
038   
039   /** The features associated to the entry. If there are no features for this entry, it is null */
040   private Map<String,Object> featureMap = null;
041   
042   /** The separator used in a GazetteerNode string */
043   private String separator;
044     
045   /**
046    * Constructor. Uses the default separator.
047    
048    @param entry the gazetteer entry
049    @param featureMap a map of name-value pairs
050    */
051   public GazetteerNode(String entry, Map<String,Object> featureMap) {
052     this.entry = entry;
053     this.featureMap = featureMap;
054   }
055 
056   /**
057    * Parses and create a gazetteer node from a string using no separator, i.e.
058    * the whole node is considered as the string to match, and there are no
059    * additional features.
060    
061    @param node the gazetteer node to be parsed
062    */
063   public GazetteerNode(String node) {
064     this(node, (Stringnull, false);
065   }
066   
067   /**
068    * Parses and create a gazetteer node from a string
069    
070    @param node the gazetteer node to be parsed
071    @param separator the separator used in the gazetteer node string to delimit 
072    * each name-value pair of features. If the separator is null, then the whole
073    * node will be used as the gazetteer entry
074    */
075   public GazetteerNode(String node, String separator) {
076     this(node, separator, false);
077   }
078   
079   /**
080    * Parses and create a gazetteer node from a string
081    *
082    @param node the gazetteer node to be parsed
083    @param separator the separator used in the gazetteer node string to delimit
084    * each name-value pair of features. If the separator is null, then the whole
085    * node will be used as the gazetteer entry
086    @param isOrdered true if the feature maps used should be ordered
087    */
088   public GazetteerNode(String node, String separator, boolean isOrdered) {
089     this.separator = (separator != null && separator.length() == 0)null : separator;
090     int index_sep;
091     if(this.separator == null || (index_sep = node.indexOf(this.separator)) == -) {
092       entry = node;
093       // leave featureMap null
094     else {
095       entry = node.substring(0, index_sep);
096       String features = node.substring(index_sep + 1);
097       featureMap = getFeatures(features, isOrdered);
098     }
099   }
100 
101   /**
102    * Given a string of name-value pairs in the format "name=value", separated
103    * by whatever this GazetteerNode's separator has been set to, convert it
104    * to the equivalent map.
105    
106    @param features a string in the format "name=value" separated by whatever
107    * the separator has been set to.
108    @param isOrdered true if the map returned should be ordered
109    @return a Map of the features
110    */
111   private Map<String,Object> getFeatures(String features, boolean isOrdered) {
112     
113     if (separator == null)
114       return null;
115     
116     // split the string into name-value pair strings
117     ArrayList<String> tempPairs = new ArrayList<String>();
118 
119     int substr_begin = 0;
120     int substr_end = features.indexOf(separator,substr_begin);
121     while (substr_end != -1) {   
122       tempPairs.add(features.substring(substr_begin,substr_end));
123       substr_begin = substr_end + 1;
124       substr_end = features.indexOf(separator,substr_begin)
125     }
126     
127     String lastPair = features.substring(substr_begin);
128 
129     if (lastPair.length() != 0) {
130       tempPairs.add(lastPair);
131     }
132     
133     String[] pairs = tempPairs.toArray(new String[tempPairs.size()])
134         
135     if (pairs.length == 0) {
136       return null;
137     }
138     
139     // extract the name and value from the pair strings and put in feature map
140     Map<String,Object> featureMap;
141     if (isOrdered) {
142       featureMap = new LinkedHashMap<String,Object>(pairs.length);
143     else {
144       featureMap = new HashMap<String,Object>(pairs.length);
145     }
146     for(int i = 0; i < pairs.length; i++) {
147       String pair = pairs[i];
148       int sep = pair.indexOf('=');
149       if(sep == -1) {
150         throw new GateRuntimeException("Correct format for gazetteer entry" +
151           " features is: [entry]([separator][featureName]=[featureValue])*");
152       else {
153         String name = pair.substring(0, sep).trim();
154         String value = pair.substring(sep + 1).trim();
155         if(name.length() && value.length() 0) {
156           featureMap.put(name, value);
157         }
158       }
159     }
160     
161     if (featureMap.size() == 0) {
162       return null;
163     }
164     return featureMap;
165   }
166 
167   /**
168    * Converts a featureMap to separated name value pairs. Note: the string will
169    * begin with the separator character.
170    
171    @param featureMap map to be converted
172    @return string of name/value pairs
173    */
174   public String featureMapToString(Map<String,Object> featureMap) {
175     String str = "";
176     if (featureMap instanceof LinkedHashMap) {
177       for (Object key : featureMap.keySet()) {
178         str += separator + key + "=" + featureMap.get(key);
179       }
180     else {
181       // sort into a predictable order
182       List<String> sortedKeys = new ArrayList<String>(featureMap.keySet());
183       Collections.sort(sortedKeys);
184       for(Iterator<String> it = sortedKeys.iterator(); it.hasNext();) {
185         String key = it.next();
186         str += separator + key + "=" + featureMap.get(key).toString();
187       }
188     }
189     return str;
190   }
191 
192   
193 
194   /**
195    * Gets the string representation of this node
196    
197    @return the string representation of this node
198    */
199   @Override
200   public String toString() {
201     if(featureMap == null || separator == null)
202       return entry;
203     else return entry + featureMapToString(featureMap);
204   }
205 
206   /**
207    * Checks this node vs another one for equality.
208    
209    @param o another node
210    @return true if the string representation of the entry and weighting match.
211    */
212   @Override
213   public boolean equals(Object o) {
214     boolean result = false;
215     if(instanceof GazetteerNode) {
216       result = this.toString().equals(o.toString());
217     }
218     return result;
219   }
220   
221   @Override
222   public int hashCode() {
223     return toString().hashCode();
224   }
225 
226   /**
227    @return the entry
228    */
229   public String getEntry() {
230     return entry;
231   }
232 
233   /**
234    @param entry
235    *          the entry to set
236    */
237   public void setEntry(String entry) {
238     this.entry = entry;
239   }
240 
241   /**
242    @return the featureMap
243    */
244   public Map<String,Object> getFeatureMap() {
245     return featureMap;
246   }
247   
248   /**
249    @param featureMap the featureMap to set
250    */
251   public void setFeatureMap(Map<String,Object> featureMap) {
252     this.featureMap = featureMap;
253   }
254 
255   /**
256    @return the separator
257    */
258   public String getSeparator() {
259     return separator;
260   }
261 
262   /**
263    @param separator the separator to set
264    */
265   public void setSeparator(String separator) {
266     this.separator = separator;
267   }
268 
269   
270 }