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