001/*
002Copyright (c) 2011+, HL7, Inc
003All rights reserved.
004
005Redistribution and use in source and binary forms, with or without modification, 
006are permitted provided that the following conditions are met:
007
008 * Redistributions of source code must retain the above copyright notice, this 
009   list of conditions and the following disclaimer.
010 * Redistributions in binary form must reproduce the above copyright notice, 
011   this list of conditions and the following disclaimer in the documentation 
012   and/or other materials provided with the distribution.
013 * Neither the name of HL7 nor the names of its contributors may be used to 
014   endorse or promote products derived from this software without specific 
015   prior written permission.
016
017THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
018ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
019WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
020IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
021INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
022NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
023PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
024WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
025ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
026POSSIBILITY OF SUCH DAMAGE.
027
028 */
029package org.hl7.fhir.utilities;
030
031import org.apache.commons.io.FileUtils;
032import org.hl7.fhir.exceptions.FHIRException;
033
034import javax.sound.sampled.AudioFormat;
035import javax.sound.sampled.AudioSystem;
036import javax.sound.sampled.SourceDataLine;
037import java.io.*;
038import java.math.BigDecimal;
039import java.net.URLEncoder;
040import java.nio.channels.FileChannel;
041import java.util.ArrayList;
042import java.util.List;
043import java.util.UUID;
044
045
046public class Utilities {
047
048//       private static final String TOKEN_REGEX = "^a-z[A-Za-z0-9]*$";
049
050
051  private static final String OID_REGEX = "[0-2](\\.(0|[1-9]([0-9])*))*";
052  // http://stackoverflow.com/questions/3780406/how-to-play-a-sound-alert-in-a-java-application
053  public static float SAMPLE_RATE = 8000f;
054  
055  public static String URLEncode(String string) {
056    try {
057      return URLEncoder.encode(string, "UTF-8");
058    } catch (UnsupportedEncodingException e) {
059      throw new Error(e.getMessage());
060    }
061  }
062  
063  public static String appendForwardSlash(String definitions) {
064            return definitions.endsWith("/") ? definitions : definitions+"/";
065          }
066    
067  public static String appendPeriod(String s) {
068    if (Utilities.noString(s))
069      return s;
070    s = s.trim();
071    if (s.endsWith(".") || s.endsWith("?"))
072      return s;
073    return s+".";
074  }
075    
076  public static String appendSlash(String definitions) {
077            return definitions.endsWith(File.separator) ? definitions : definitions+File.separator;
078          }
079    
080  public static String asCSV(List<String> strings) {
081    StringBuilder s = new StringBuilder();
082    boolean first = true;
083    for (String n : strings) {
084      if (!first)
085        s.append(",");
086      s.append(n);
087      first = false;
088    }
089    return s.toString();
090  }
091    
092  public static String asHtmlBr(String prefix, List<String> strings) {
093    StringBuilder s = new StringBuilder();
094    boolean first = true;
095    for (String n : strings) {
096      if (!first)
097        s.append("<br/>");
098      s.append(prefix);
099      s.append(n);
100      first = false;
101    }
102    return s.toString();
103  }
104        
105        public static String camelCase(String value) {
106          return new Inflector().camelCase(value.trim().replace(" ", "_"), false);
107        }
108
109        public static String capitalize(String s)
110        {
111                if( s == null ) return null;
112                if( s.length() == 0 ) return s;
113                if( s.length() == 1 ) return s.toUpperCase();
114
115                return s.substring(0, 1).toUpperCase() + s.substring(1);
116        }
117        
118  public static String changeFileExt(String name, String ext) {
119    if (name.lastIndexOf('.') > -1)
120      return name.substring(0, name.lastIndexOf('.')) + ext;
121    else
122      return name+ext;
123  }
124          
125  public static int charCount(String s, char c) {
126          int res = 0;
127          for (char ch : s.toCharArray())
128                if (ch == c)
129                  res++;
130          return res;
131  }
132        
133  public static boolean charInRange(char ch, char a, char z) {
134    return ch >= a && ch <= z;
135  }
136
137  public static boolean charInSet(char value, char... array) {
138    for (int i : array)
139      if (value == i)
140          return true;
141    return false;
142  }
143
144  public static boolean checkFile(String purpose, String dir, String file, List<String> errors)
145        throws IOException
146  {
147    if (!new CSFile(dir+file).exists()) {
148      errors.add("Unable to find "+purpose+" file "+file+" in "+dir);
149      return false;
150    } else {
151      return true;
152    }
153  }
154
155  public static boolean checkFolder(String dir, List<String> errors)
156        throws IOException
157  {
158          if (!new CSFile(dir).exists()) {
159      errors.add("Unable to find directory "+dir);
160      return false;
161    } else {
162      return true;
163    }
164  }
165
166  public static String cleanupTextString( String contents )
167  {
168          if( contents == null || contents.trim().equals("") )
169                  return null;
170          else
171                  return contents.trim();
172  }
173
174  public static void clearDirectory(String folder) throws IOException {
175    File dir = new File(folder);
176    if (dir.exists())
177      FileUtils.cleanDirectory(dir);
178//        String[] files = new CSFile(folder).list();
179//        if (files != null) {
180//                for (String f : files) {
181//                        File fh = new CSFile(folder+File.separatorChar+f);
182//                        if (fh.isDirectory())
183//                                clearDirectory(fh.getAbsolutePath());
184//                        fh.delete();
185//                }
186//        }
187  }
188
189  public static boolean compareIgnoreWhitespace(File f1, File f2) throws IOException {
190    InputStream in1 = null;
191    InputStream in2 = null;
192    try {
193      in1 = new BufferedInputStream(new FileInputStream(f1));
194      in2 = new BufferedInputStream(new FileInputStream(f2));
195
196      int expectedByte = in1.read();
197      while (expectedByte != -1) {
198        boolean w1 = isWhitespace(expectedByte);
199        if (w1)
200          while (isWhitespace(expectedByte))
201            expectedByte = in1.read();
202        int foundByte = in2.read();
203        if (w1) {
204          if (!isWhitespace(foundByte))
205            return false;
206          while (isWhitespace(foundByte))
207            foundByte = in2.read();
208        }
209        if (expectedByte != foundByte)
210          return false;
211        expectedByte = in1.read();
212      }
213      if (in2.read() != -1) {
214        return false;
215      }
216      return true;
217    } finally {
218      if (in1 != null) {
219        try {
220          in1.close();
221        } catch (IOException e) {}
222      }
223      if (in2 != null) {
224        try {
225          in2.close();
226        } catch (IOException e) {}
227      }
228    }
229  }
230
231  public static boolean compareIgnoreWhitespace(String fn1, String fn2) throws IOException {
232    return compareIgnoreWhitespace(new File(fn1), new File(fn2));
233  }
234
235  public static void copyDirectory(String sourceFolder, String destFolder, FileNotifier notifier) throws IOException, FHIRException  {
236    CSFile src = new CSFile(sourceFolder);
237    if (!src.exists())
238      throw new FHIRException("Folder " +sourceFolder+" not found");
239    createDirectory(destFolder);
240
241   String[] files = src.list();
242   for (String f : files) {
243     if (new CSFile(sourceFolder+File.separator+f).isDirectory()) {
244       if (!f.startsWith(".")) // ignore .svn...
245         copyDirectory(sourceFolder+File.separator+f, destFolder+File.separator+f, notifier);
246     } else {
247       if (notifier != null)
248         notifier.copyFile(sourceFolder+File.separator+f, destFolder+File.separator+f);
249       copyFile(new CSFile(sourceFolder+File.separator+f), new CSFile(destFolder+File.separator+f));
250     }
251   }
252  }
253  
254  public static void copyFile(String source, String dest) throws IOException {
255    copyFile(new File(source), new File(dest));
256  }
257
258        public static void copyFile(File sourceFile, File destFile) throws IOException {
259                if(!destFile.exists()) {
260                        if (!new CSFile(destFile.getParent()).exists()) {
261                                createDirectory(destFile.getParent());
262                        }
263                        destFile.createNewFile();
264                }
265
266                FileChannel source = null;
267                FileChannel destination = null;
268
269                try {
270                        source = new FileInputStream(sourceFile).getChannel();
271                        destination = new FileOutputStream(destFile).getChannel();
272                        destination.transferFrom(source, 0, source.size());
273                }
274                finally {
275                        if(source != null) {
276                                source.close();
277                        }
278                        if(destination != null) {
279                                destination.close();
280                        }
281                }
282        }
283
284  public static void copyFileToDirectory(File source, File destDir) throws IOException {
285        copyFile(source, new File(path(destDir.getAbsolutePath(), source.getName())));
286  }
287
288  public static int countFilesInDirectory(String dirName) {
289    File dir = new File(dirName);
290         if (dir.exists() == false) {
291      return 0;
292         }
293    int i = 0;
294    for (File f : dir.listFiles())
295      if (!f.isDirectory())
296        i++;
297    return i;
298  }
299
300  public static void createDirectory(String path) throws IOException{
301    new CSFile(path).mkdirs();
302  }
303
304  public static File createTempFile(String prefix, String suffix) throws IOException {
305 // this allows use to eaily identify all our dtemp files and delete them, since delete on Exit doesn't really work.
306    File file = File.createTempFile("ohfu-"+prefix, suffix);
307    file.deleteOnExit();
308    return file;
309  }
310
311  public static void deleteAllFiles(String folder, String type) {
312    File src = new File(folder);
313    String[] files = src.list();
314    for (String f : files) {
315      if (new File(folder+File.separator+f).isDirectory()) {
316        deleteAllFiles(folder+File.separator+f, type);
317      } else if (f.endsWith(type)) {
318        new File(folder+File.separator+f).delete();
319      }
320    }
321
322  }
323
324  public static void deleteTempFiles() throws IOException {
325    File file = createTempFile("test", "test");
326    String folder = getDirectoryForFile(file.getAbsolutePath());
327    String[] list = new File(folder).list(new FilenameFilter() {
328      public boolean accept(File dir, String name) {
329        return name.startsWith("ohfu-");
330      }
331    });
332    if (list != null) {
333      for (String n : list) {
334        new File(path(folder, n)).delete();
335      }
336    }
337  }
338
339  public static String encodeUri(String v) {
340    return v.replace(" ", "%20").replace("?", "%3F").replace("=", "%3D");
341  }
342
343  public static boolean equals(String one, String two) {
344    if (one == null && two == null)
345      return true;
346    if (one == null || two == null)
347      return false;
348    return one.equals(two);
349  }
350
351  public static boolean equivalent(String l, String r) {
352    if (Utilities.noString(l) && Utilities.noString(r))
353      return true;
354    if (Utilities.noString(l) || Utilities.noString(r))
355      return false;
356    return l.toLowerCase().equals(r.toLowerCase());
357  }
358
359  public static boolean equivalentNumber(String l, String r) {
360    if (Utilities.noString(l) && Utilities.noString(r))
361      return true;
362    if (Utilities.noString(l) || Utilities.noString(r))
363      return false;
364    l = l.toLowerCase().trim();
365    r = r.toLowerCase().trim(); // not that this should make any difference
366    return l.startsWith(r) || r.startsWith(l);
367  }
368
369  public static String escapeJava(String doco) {
370    if (doco == null)
371      return "";
372
373    StringBuilder b = new StringBuilder();
374    for (char c : doco.toCharArray()) {
375      if (c == '\r')
376        b.append("\\r");
377      else if (c == '\n')
378        b.append("\\n");
379      else if (c == '"')
380        b.append("\\\"");
381      else if (c == '\\')
382        b.append("\\\\");
383      else
384        b.append(c);
385    }
386    return b.toString();
387  }
388
389
390
391//  public static void checkCase(String filename) {
392//    File f = new CSFile(filename);
393//    if (!f.getName().equals(filename))
394//      throw new FHIRException("Filename  ")
395//    
396//  }
397
398  public static String escapeJson(String value) {
399    if (value == null)
400      return "";
401
402    StringBuilder b = new StringBuilder();
403    for (char c : value.toCharArray()) {
404      if (c == '\r')
405        b.append("\\r");
406      else if (c == '\n')
407        b.append("\\n");
408      else if (c == '"')
409        b.append("\\\"");
410      else if (c == '\'')
411        b.append("\\'");
412      else if (c == '\\')
413        b.append("\\\\");
414      else
415        b.append(c);
416    }
417    return b.toString();
418  }
419
420        public static String escapeXml(String doco) {
421                if (doco == null)
422                        return "";
423
424                StringBuilder b = new StringBuilder();
425                for (char c : doco.toCharArray()) {
426                  if (c == '<')
427                          b.append("&lt;");
428                  else if (c == '>')
429                          b.append("&gt;");
430                  else if (c == '&')
431                          b.append("&amp;");
432      else if (c == '"')
433        b.append("&quot;");
434                  else
435                          b.append(c);
436                }
437                return b.toString();
438        }
439
440  public static boolean existsInList(String value, String... array) {
441    if (value == null)
442      return false;
443    for (String s : array)
444      if (value.equals(s))
445          return true;
446    return false;
447  }
448
449  public static boolean existsInList(int value, int... array) {
450    for (int i : array)
451      if (value == i)
452          return true;
453    return false;
454  }
455
456  public static boolean existsInListNC(String value, String... array) {
457    for (String s : array)
458      if (value.equalsIgnoreCase(s))
459          return true;
460    return false;
461  }
462
463  public static String fileTitle(String file) {
464    if (file == null)
465      return null;
466    String s = new File(file).getName();
467    return s.indexOf(".") == -1? s : s.substring(0, s.indexOf("."));
468  }
469
470  public static String getDirectoryForFile(String filepath) {
471    File f = new File(filepath);
472    return f.getParent();
473  }
474
475  public static String getFileExtension(String fn) {
476    return fn.contains(".") ? fn.substring(fn.lastIndexOf(".")+1) : "";
477  }
478
479  public static String getFileNameForName(String name) {
480    return name.toLowerCase();
481  }
482
483  public static String humanize(String code) {
484    StringBuilder b = new StringBuilder();
485    boolean lastBreak = true;
486    for (char c : code.toCharArray()) {
487      if (Character.isLetter(c)) {
488        if (lastBreak)
489          b.append(Character.toUpperCase(c));
490        else {
491          if (Character.isUpperCase(c))
492            b.append(" ");
493          b.append(c);
494        }
495        lastBreak = false;
496      } else {
497        b.append(" ");
498        lastBreak = true;
499      }
500    }
501    if (b.length() == 0)
502      return code;
503    else
504      return b.toString();
505  }
506
507  public static boolean isAbsoluteFileName(String source) {
508    if (isWindows())
509      return (source.length() > 2 && source.charAt(1) == ':') || source.startsWith("\\\\");
510    else
511      return source.startsWith("//");
512  }
513
514  public static boolean isAbsoluteUrl(String ref) {
515    return ref != null && (ref.startsWith("http:") || ref.startsWith("https:") || ref.startsWith("urn:uuid:") || ref.startsWith("urn:oid:")) ;
516  }
517
518  private static boolean isAlphabetic(char c) {
519    return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
520  }
521  
522        public static boolean isAsciiChar(char ch) {
523                return ch >= ' ' && ch <= '~';
524  }
525
526    public static boolean isDecimal(String string) {
527      if (Utilities.noString(string))
528        return false;
529      try {
530        BigDecimal bd = new BigDecimal(string);
531        return bd != null;
532      } catch (Exception e) {
533        return false;
534      }
535    }
536
537  private static boolean isDigit(char c) {
538    return (c >= '0') && (c <= '9');
539  }
540
541    public static boolean isFloat(String string) {
542      if (Utilities.noString(string))
543        return false;
544      try {
545        float r = Float.parseFloat(string);
546        return r != r + 1; // just to suppress the hint
547      } catch (Exception e) {
548        return false;
549      }
550    }
551
552    public static boolean isHex(String string) {
553      try {
554        int i = Integer.parseInt(string, 16);
555        return i != i+1;
556      } catch (Exception e) {
557        return false;
558      }
559    }
560
561    public static boolean isInteger(String string) {
562      try {
563        int i = Integer.parseInt(string);
564        return i != i+1;
565      } catch (Exception e) {
566        return false;
567      }
568    }
569
570  public static boolean isOid(String cc) {
571    return cc.matches(OID_REGEX) && cc.lastIndexOf('.') >= 5;
572  }
573
574  public static boolean isPlural(String word) {
575    word = word.toLowerCase();
576    if ("restricts".equals(word) || "contains".equals(word) || "data".equals(word) || "specimen".equals(word) || "replaces".equals(word) || "addresses".equals(word)
577        || "supplementalData".equals(word) || "instantiates".equals(word))
578      return false;
579    Inflector inf = new Inflector();
580    return !inf.singularize(word).equals(word);
581  }
582
583  public static boolean isToken(String tail) {
584    if (tail == null || tail.length() == 0)
585      return false;
586    boolean result = isAlphabetic(tail.charAt(0));
587    for (int i = 1; i < tail.length(); i++) {
588        result = result && (isAlphabetic(tail.charAt(i)) || isDigit(tail.charAt(i)) || (tail.charAt(i) == '_')  || (tail.charAt(i) == '[') || (tail.charAt(i) == ']'));
589    }
590    return result;
591  }
592
593  public static boolean isURL(String s) {
594    boolean ok = s.matches("^http(s{0,1})://[a-zA-Z0-9_/\\-\\.]+\\.([A-Za-z/]{2,5})[a-zA-Z0-9_/\\&\\?\\=\\-\\.\\~\\%]*");
595    return ok;
596 }
597
598        public static boolean isWhitespace(String s) {
599          boolean ok = true;
600          for (int i = 0; i < s.length(); i++)
601                ok = ok && Character.isWhitespace(s.charAt(i));
602          return ok;
603
604  }
605
606  private static boolean isWhitespace(int b) {
607    return b == 9 || b == 10 || b == 13 || b == 32;
608  }
609
610  private static boolean isWindows() {
611    return System.getProperty("os.name").startsWith("Windows");
612  }
613
614  public static String makeId(String name) {
615    StringBuilder b = new StringBuilder();
616    for (char ch : name.toCharArray()) {
617      if (ch >= 'a' && ch <= 'z')
618        b.append(ch);
619      else if (ch >= 'A' && ch <= 'Z')
620        b.append(ch);
621      else if (ch >= '0' && ch <= '9')
622        b.append(ch);
623      else if (ch == '-' || ch == '.')
624        b.append(ch);
625    }
626    return b.toString();
627  }
628
629  public static String makeUuidUrn() {
630    return "urn:uuid:"+UUID.randomUUID().toString().toLowerCase();
631  }
632
633  public static String nmtokenize(String cs) {
634    if (cs == null)
635      return "";
636    StringBuilder s = new StringBuilder();
637    for (int i = 0; i < cs.length(); i++) {
638      char c = cs.charAt(i);
639      if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_')
640        s.append(c);
641      else if (c != ' ')
642        s.append("."+Integer.toString(c));
643    }
644    return s.toString();
645  }
646
647  public static boolean noString(String v) {
648    return v == null || v.equals("");
649  }
650
651  public static String normaliseEolns(String value) {
652    return value.replace("\r\n", "\r").replace("\n", "\r").replace("\r", "\r\n");
653  }
654
655        public static String normalize(String s) {
656                if (noString(s))
657                        return null;
658          StringBuilder b = new StringBuilder();
659          boolean isWhitespace = false;
660          for (int i = 0; i < s.length(); i++) {
661                char c = s.charAt(i);
662                if (!Character.isWhitespace(c)) {
663                        b.append(Character.toLowerCase(c));
664                        isWhitespace = false;
665                } else if (!isWhitespace) {
666                        b.append(' ');
667                        isWhitespace = true;
668                }
669          }
670          return b.toString().trim();
671  }
672
673  public static String normalizeSameCase(String s) {
674    if (noString(s))
675      return null;
676    StringBuilder b = new StringBuilder();
677    boolean isWhitespace = false;
678    for (int i = 0; i < s.length(); i++) {
679      char c = s.charAt(i);
680      if (!Character.isWhitespace(c)) {
681        b.append(c);
682        isWhitespace = false;
683      } else if (!isWhitespace) {
684        b.append(' ');
685        isWhitespace = true;
686      }
687    }
688    return b.toString().trim();
689  }
690
691  public static String oidRoot(String id) {
692    if (id == null || !id.contains("."))
693      return id;
694    return id.substring(0, id.indexOf("."));
695  }
696  
697  public static String oidTail(String id) {
698    if (id == null || !id.contains("."))
699      return id;
700    return id.substring(id.lastIndexOf(".")+1);
701  }
702
703  public static String padLeft(String src, char c, int len) {
704    StringBuilder s = new StringBuilder();
705    for (int i = 0; i < len - src.length(); i++)
706      s.append(c);
707    s.append(src);
708    return s.toString();
709  }
710
711  public static String padRight(String src, char c, int len) {
712    StringBuilder s = new StringBuilder();
713    s.append(src);
714    for (int i = 0; i < len - src.length(); i++)
715      s.append(c);
716    return s.toString();
717  }
718
719  public static String path(String... args) throws IOException {
720    StringBuilder s = new StringBuilder();
721    boolean d = false;
722    boolean first = true;
723    for(String arg: args) {
724      if (first && arg == null)
725        continue;
726      first = false;
727      if (!d)
728        d = !noString(arg);
729      else if (!s.toString().endsWith(File.separator))
730        s.append(File.separator);
731      String a = arg;
732      a = a.replace("\\", File.separator);
733      a = a.replace("/", File.separator);
734      if (s.length() > 0 && a.startsWith(File.separator))
735        a = a.substring(File.separator.length());
736
737      while (a.startsWith(".."+File.separator)) {
738        String p = s.toString().substring(0, s.length()-1);
739        if (!p.contains(File.separator)) {
740          s = new StringBuilder();
741        } else {
742          s = new StringBuilder(p.substring(0,  p.lastIndexOf(File.separator))+File.separator);
743        }
744        a = a.substring(3);
745      }
746      if ("..".equals(a)) {
747        int i = s.substring(0, s.length()-1).lastIndexOf(File.separator);
748        s = new StringBuilder(s.substring(0, i+1));
749      } else
750        s.append(a);
751    }
752    return s.toString();
753  }
754
755  public static String pathURL(String... args) {
756    StringBuilder s = new StringBuilder();
757    boolean d = false;
758    for(String arg: args) {
759      if (!d)
760        d = !noString(arg);
761      else if (!s.toString().endsWith("/"))
762        s.append("/");
763      s.append(arg);
764    }
765    return s.toString();
766  }
767
768  public static String pluralize(String word, int count) {
769    if (count == 1)
770      return word;
771    Inflector inf = new Inflector();
772    return inf.pluralize(word);
773  }
774  
775  /**
776     * Returns the plural form of the word in the string.
777     *
778     * Examples:
779     *
780     * <pre>
781     *   inflector.pluralize(&quot;post&quot;)               #=&gt; &quot;posts&quot;
782     *   inflector.pluralize(&quot;octopus&quot;)            #=&gt; &quot;octopi&quot;
783     *   inflector.pluralize(&quot;sheep&quot;)              #=&gt; &quot;sheep&quot;
784     *   inflector.pluralize(&quot;words&quot;)              #=&gt; &quot;words&quot;
785     *   inflector.pluralize(&quot;the blue mailman&quot;)   #=&gt; &quot;the blue mailmen&quot;
786     *   inflector.pluralize(&quot;CamelOctopus&quot;)       #=&gt; &quot;CamelOctopi&quot;
787     * </pre>
788     *
789     *
790     *
791     * Note that if the {@link Object#toString()} is called on the supplied object, so this method works for non-strings, too.
792     *
793     *
794     * @param word the word that is to be pluralized.
795     * @return the pluralized form of the word, or the word itself if it could not be pluralized
796     * @see #singularize(Object)
797     */
798  public static String pluralizeMe( String word ) {
799    Inflector inf = new Inflector();
800    return inf.pluralize(word);
801  }
802
803  public static String removePeriod(String s) {
804    if (Utilities.noString(s))
805      return s;
806    if (s.endsWith("."))
807      return s.substring(0, s.length()-1);
808    return s;
809  }
810
811  public static String[] splitByCamelCase(String name) {
812    List<String> parts = new ArrayList<String>();
813    StringBuilder b = new StringBuilder();
814    for (int i = 0; i < name.length(); i++) {
815      if (i > 0 && Character.isUpperCase(name.charAt(i))) {
816        parts.add(b.toString());
817        b = new StringBuilder();
818      }
819      b.append(Character.toLowerCase(name.charAt(i)));
820    }
821    parts.add(b.toString());
822    return parts.toArray(new String[] {} );
823  }
824
825  public static String splitLineForLength(String line, int prefixLength, int indent, int allowedLength) {
826    List<String> list = new ArrayList<String>();
827    while (prefixLength + line.length() > allowedLength) {
828      int i = allowedLength - (list.size() == 0 ? prefixLength : indent);
829      while (i > 0 && line.charAt(i) != ' ')
830        i--;
831      if (i == 0)
832        break;
833      list.add(line.substring(0, i));
834      line = line.substring(i+1);
835    }
836    list.add(line);
837    StringBuilder b = new StringBuilder();
838    boolean first = true;
839    for (String s : list) {
840      if (first)
841        first = false;
842      else
843        b.append("\r\n"+padLeft("", ' ', indent));
844      b.append(s);
845    }
846    return b.toString();
847  }
848
849        public static String stripBOM(String string) {
850          return string.replace("\uFEFF", "");
851  }
852
853  public static String systemEol()
854  {
855          return System.getProperty("line.separator");
856  }
857
858        public static String titleize(String s) {
859          StringBuilder b = new StringBuilder();
860          boolean up = true;
861          for (char c : s.toCharArray()) {
862            if (up)
863              b.append(Character.toUpperCase(c));
864            else
865              b.append(c);
866            up = c == ' ';
867          }
868          return b.toString();
869        }
870
871  public static void tone(int hz, int msecs) {
872      tone(hz, msecs, 1.0);
873   }
874
875  public static void tone(int hz, int msecs, double vol) {
876    try {
877      byte[] buf = new byte[1];
878      AudioFormat af =
879          new AudioFormat(
880              SAMPLE_RATE, // sampleRate
881              8,           // sampleSizeInBits
882              1,           // channels
883              true,        // signed
884              false);      // bigEndian
885      SourceDataLine sdl;
886      sdl = AudioSystem.getSourceDataLine(af);
887      sdl.open(af);
888      sdl.start();
889      for (int i=0; i < msecs*8; i++) {
890        double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI;
891        buf[0] = (byte)(Math.sin(angle) * 127.0 * vol);
892        sdl.write(buf,0,1);
893      }
894      sdl.drain();
895      sdl.stop();
896      sdl.close();
897    } catch (Exception e) {
898    }
899  }
900
901  public static String unCamelCase(String name) {
902    StringBuilder b = new StringBuilder();
903    boolean first = true;
904    for (char c : name.toCharArray()) {
905      if (Character.isUpperCase(c)) {
906        if (!first)
907          b.append(" ");
908        b.append(Character.toLowerCase(c));
909      } else
910        b.append(c);
911      first = false;
912    }
913    return b.toString();
914  }
915
916  public static String uncapitalize(String s) {
917    if( s == null ) return null;
918    if( s.length() == 0 ) return s;
919    if( s.length() == 1 ) return s.toLowerCase();
920
921    return s.substring(0, 1).toLowerCase() + s.substring(1);
922  }
923
924  public static String unescapeXml(String xml) throws FHIRException  {
925    if (xml == null)
926      return null;
927
928    StringBuilder b = new StringBuilder();
929    int i = 0;
930    while (i < xml.length()) {
931      if (xml.charAt(i) == '&') {
932        StringBuilder e = new StringBuilder();
933        i++;
934        while (xml.charAt(i) != ';') {
935          e.append(xml.charAt(i));
936          i++;
937        }
938        if (e.toString().equals("lt"))
939          b.append("<");
940        else if (e.toString().equals("gt"))
941          b.append(">");
942        else if (e.toString().equals("amp"))
943          b.append("&");
944        else if (e.toString().equals("quot"))
945          b.append("\"");
946        else if (e.toString().equals("mu"))
947          b.append((char)956);
948        else
949          throw new FHIRException("unknown XML entity \""+e.toString()+"\"");
950      }  else
951        b.append(xml.charAt(i));
952      i++;
953    }
954    return b.toString();
955  }
956
957
958
959
960}