Strings.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  *  Hamish Cunningham, 22/02/2000
011  *
012  *  $Id: Strings.java 17619 2014-03-11 09:41:14Z markagreenwood $
013  */
014 
015 package gate.util;
016 
017 import java.io.*;
018 import java.util.Arrays;
019 import java.util.Collection;
020 import java.util.HashMap;
021 import java.util.Iterator;
022 import java.util.LinkedHashSet;
023 import java.util.Map;
024 import java.util.Set;
025 
026 /** Some utilities for use with Strings. */
027 public class Strings {
028 
029   /** What character to pad with. */
030   private static char padChar = ' ';
031 
032   /** Local fashion for newlines this year. */
033   private static String newline = System.getProperty("line.separator");
034 
035   /** Get local fashion for newlines. */
036   public static String getNl() { return newline; }
037 
038   /** Local fashion for path separators. */
039   private static String pathSep = System.getProperty("path.separator");
040 
041   /** Get local fashion for path separators (e.g. ":"). */
042   public static String getPathSep() { return pathSep; }
043 
044   /** Local fashion for file separators. */
045   private static String fileSep = System.getProperty("file.separator");
046 
047   /** Get local fashion for file separators (e.g. "/"). */
048   public static String getFileSep() { return fileSep; }
049 
050   /** Add n pad characters to pad. */
051   public static String addPadding(String pad, int n) {
052     StringBuffer s = new StringBuffer(pad);
053     for(int i = 0; i < n; i++)
054       s.append(padChar);
055 
056     return s.toString();
057   // padding
058 
059   /** Helper method to add line numbers to a string */
060   public static String addLineNumbers(String text) {
061     return addLineNumbers(text, 1);
062   }
063   
064   public static String addLineNumbers(String text, int startLine) {
065     // construct a line reader for the text
066     BufferedReader reader = new BufferedReader(new StringReader(text));
067     String line = null;
068     StringBuffer result = new StringBuffer();
069 
070     try {
071       for(int lineNum = startLine; line = reader.readLine() ) != null; lineNum++) {
072         String pad;
073         if(lineNum < 10pad = " ";
074         else pad = "";
075         result.append(pad + lineNum + "  " + line + Strings.getNl());
076       }
077     catch(IOException ie) { }
078 
079     return result.toString();
080   // addLineNumbers
081 
082   /**
083    * A method to unescape Java strings, returning a string containing escape
084    * sequences into the respective character. i.e. "\" followed by "t" is turned
085    * into the tab character.
086    *
087    @param str the string to unescape
088    @return a new unescaped string of the one passed in
089    */
090   public static String unescape(String str) {
091     if (str == nullreturn str;
092 
093     StringBuilder sb = new StringBuilder()// string to build
094 
095     StringBuilder unicodeStr = new StringBuilder(4)// store unicode sequences
096 
097     boolean inUnicode = false;
098     boolean hadSlash = false;
099 
100     for (char ch: str.toCharArray()) {
101       if (inUnicode) {
102         unicodeStr.append(ch);
103         if (unicodeStr.length() == 4) {
104           try {
105             int unicodeValue = Integer.parseInt(unicodeStr.toString()16);
106             sb.append((charunicodeValue);
107             unicodeStr.setLength(0);
108             inUnicode = false;
109             hadSlash = false;
110           catch (NumberFormatException e) {
111             throw new RuntimeException("Couldn't parse unicode value: " + unicodeStr, e);
112           }
113         }
114         continue;
115       }
116       if (hadSlash) {
117         hadSlash = false;
118         switch (ch) {
119           case '\\':
120             sb.append('\\');
121             break;
122           case '\'':
123             sb.append('\'');
124             break;
125           case '\"':
126             sb.append('"');
127             break;
128           case 'r':
129             sb.append('\r');
130             break;
131           case 'f':
132             sb.append('\f');
133               break;
134           case 't':
135             sb.append('\t');
136             break;
137           case 'n':
138             sb.append('\n');
139             break;
140           case 'b':
141             sb.append('\b');
142             break;
143           case 'u':
144             inUnicode = true;
145             break;
146           default :
147             sb.append(ch);
148             break;
149         }
150         continue;
151       else if (ch == '\\') {
152         hadSlash = true;
153         continue;
154       }
155       sb.append(ch);
156     }
157     if (hadSlash) {
158       sb.append('\\');
159     }
160     return sb.toString();
161   }
162 
163   /**
164    * Converts a size given as a number of bytes (e.g. a file size) to a 
165    * human-readable string.
166    @param bytes the size to be converted.
167    @param base10 if <code>true</code> then the multiplier used is 10^3 (i.e. 
168    * 1000 bytes are reported as 1kB, etc.); otherwise 1024 is used as a 
169    * multiplier (1024 bytes is 1KiB, etc.).
170    @return a human readable version of a file size
171    */
172   public static String humanReadableByteCount(long bytes, boolean base10) {
173     int unit = base10 ? 1000 1024;
174     if (bytes < unitreturn bytes + " B";
175     int exp = (int) (Math.log(bytes/ Math.log(unit));
176     String pre = (base10 ? "kMGTPE" "KMGTPE").charAt(exp-1(base10 ? "" "i");
177     return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
178   }
179   
180   /**
181    * Convert about any object to a human readable string.<br>
182    * Use {@link Arrays#deepToString(Object[])} to convert an array or
183    * a collection.
184    @param object object to be converted to a string
185    @return a string representation of the object, the empty string if null.
186    */
187   public static String toString(Object object) {
188     if (object == null) {
189       return "";
190     else if (object instanceof Object[]) {
191       return Arrays.deepToString((Object[])object);
192     else if (object instanceof Collection) {
193       return Arrays.deepToString(((Collection<?>)object).toArray());
194     else {
195       return object.toString();
196     }
197   }
198 
199   /**
200    * Create a String representation of a Set of String with the format
201    * [value, value].
202    * Escape with a backslash the separator character.
203    @param set set to convert to one String
204    @return a String that represent the set
205    @see #toSet(String, String)
206    */
207   public static String toString(LinkedHashSet<String> set) {
208     StringBuilder builder = new StringBuilder();
209     builder.append('[');
210     for (String element : set) {
211       // escape element separator
212       String escapedElement = element.replaceAll(",""\\\\,");
213       builder.append(escapedElement).append(", ");
214     }
215     if (builder.length() 1) { // remove last element separator
216       builder.delete(builder.length()-2, builder.length());
217     }
218     builder.append("]");
219     return builder.toString();
220   }
221 
222   /**
223    * Create a String representation of a Map of String*String with the format
224    * {key=value, key=value}.
225    * Escape with a backslash the separator characters.
226    @param map map to convert to one String
227    @return a String that represent the map
228    @see #toMap(String)
229    */
230   public static String toString(Map<String, String> map) {
231     StringBuilder builder = new StringBuilder();
232     builder.append('{');
233     for (Map.Entry<String, String> entry : map.entrySet()) {
234       // escape element separator
235       String escapedKey = entry.getKey().replaceAll("=""\\\\=")
236         .replaceAll(",""\\\\,");
237       String escapedValue = entry.getValue().replaceAll("=""\\\\=")
238         .replaceAll(",""\\\\,");
239       builder.append(escapedKey).append("=").append(escapedValue).append(", ");
240     }
241     if (builder.length() 1) { // remove last element separator
242       builder.delete(builder.length()-2, builder.length());
243     }
244     builder.append("}");
245     return builder.toString();
246   }
247 
248   /**
249    * Get back a Set of String from its String representation.
250    * Unescape backslashed separator characters.
251    @param string String to convert to a List
252    @param separator String that delimits the element of the set
253    @return a linked hashset
254    @see #toString(java.util.LinkedHashSet)
255    */
256   public static LinkedHashSet<String> toSet(String string, String separator) {
257     LinkedHashSet<String> set = new LinkedHashSet<String>();
258     if (string == null
259      || string.length() 3) {
260       return set;
261     }
262     // remove last character
263     String value = string.substring(0, string.length()-1);
264     int index = 1;
265     int startIndex = 1;
266     int separatorIndex;
267     // split on element separator
268     while ((separatorIndex = value.indexOf(separator, index)) != -1) {
269       // check that the separator is not an escaped character
270       if (value.charAt(separatorIndex-1!= '\\') {
271         set.add(value.substring(startIndex, separatorIndex)
272           // unescape separator
273           .replaceAll("\\\\"+separator.charAt(0)""+separator.charAt(0)));
274         startIndex = separatorIndex + separator.length();
275       }
276       index = separatorIndex + separator.length();
277     }
278     // last element of the set
279     set.add(value.substring(startIndex));
280     return set;
281   }
282 
283   /**
284    * Get back a Map of String*String from its String representation.
285    * Unescape backslashed separator characters.
286    @param string String to convert to a Map
287    @return a Map
288    @see #toString(java.util.Map)
289    */
290   public static Map<String, String> toMap(String string) {
291     Map<String, String> map = new HashMap<String, String>();
292     if (string == null
293      || string.length() 3) {
294       return map;
295     }
296     Set<String> firstList = toSet(string, ", ");
297     for (String element : firstList) {
298       LinkedHashSet<String> secondList = toSet("[" + element + "]""=");
299       if (secondList.size() == 2) {
300         Iterator<String> iterator = secondList.iterator();
301         map.put(iterator.next(), iterator.next());
302       else {
303         Err.prln("Ignoring element: [" + element + "]");
304         Err.prln("Expecting: [key=value]");
305       }
306     }
307     return map;
308   }
309 
310   /**
311    * Crop the text in the middle if too long.
312    @param text text to crop
313    @param maxLength maximum length of the text
314    @return cropped text if needed otherwise the same text
315    */
316   public static String crop(String text, int maxLength) {
317     if (text.length() > maxLength) {
318       text = text.substring(0(maxLength/2)-2"..."
319         + text.substring(text.length() (maxLength/2));
320     }
321     return text;
322   }
323 
324 // class Strings