001/*
002 * Syncany, www.syncany.org
003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package org.syncany.util;
019
020import java.io.PrintWriter;
021import java.io.StringWriter;
022import java.io.UnsupportedEncodingException;
023import java.util.Arrays;
024import java.util.List;
025
026import javax.xml.bind.DatatypeConverter;
027
028/**
029 * Utility class for common application string functions.
030 * 
031 * @author Philipp C. Heckel (philipp.heckel@gmail.com)
032 */
033public class StringUtil {   
034        /**
035         * Transforms a string to a camel case representation, including the
036         * first character.
037         * 
038         * <p>Examples:
039         * <ul>
040         *  <li><code>toCamelCase("hello world") -&gt; "HelloWorld"</code></li>
041         *  <li><code>toCamelCase("hello_world") -&gt; "HelloWorld"</code></li>
042         *  <li><code>toCamelCase("hello_World") -&gt; "HelloWorld"</code></li>
043         *  <li><code>toCamelCase("helloWorld") -&gt; "HelloWorld"</code></li>
044         *  <li><code>toCamelCase("HelloWorld") -&gt; "HelloWorld"</code></li>
045         * </ul>
046         */
047    public static String toCamelCase(String str) {
048        StringBuilder sb = new StringBuilder();
049
050        for (String s : str.split("[-_ ]")) {
051                if (s.length() > 0) {
052                    sb.append(Character.toUpperCase(s.charAt(0)));
053        
054                    if (s.length() > 1) {
055                        sb.append(s.substring(1, s.length()));
056                    }
057                }
058        }
059
060        return sb.toString();
061    }
062    
063    /**
064     * Transforms a string to underscore-delimited representation.
065     * 
066         * <p>Examples:
067         * <ul>
068         *  <li><code>toUnderScoreDelimited("HelloWorld") -&gt; "hello_world"</code></li>
069         *  <li><code>toUnderScoreDelimited("helloWorld") -&gt; "hello_world"</code></li>
070         * </ul>
071     */
072    public static String toSnakeCase(String str) {
073                StringBuilder sb = new StringBuilder();
074
075        for (char c : str.toCharArray()) {   
076                if (Character.isLetter(c) || Character.isDigit(c)) {
077                        if (Character.isUpperCase(c)) {
078                        if (sb.length() > 0) {
079                                sb.append("_");
080                        }
081                        
082                        sb.append(Character.toLowerCase(c));
083                }
084                else {
085                        sb.append(c);
086                }
087                }
088                else {
089                        sb.append("_");
090                }
091        }
092
093        return sb.toString();
094        }
095    
096    /**
097     * Converts a byte array to a lower case hex representation.
098     * If the given byte array is <code>null</code>, an empty string is returned.
099     */
100    public static String toHex(byte[] bytes) {
101        if (bytes == null) {
102                return "";
103        }
104        else {
105                return DatatypeConverter.printHexBinary(bytes).toLowerCase();
106        }
107    }
108    
109    /**
110     * Creates byte array from a hex represented string.
111     */
112    public static byte[] fromHex(String s) {
113        return DatatypeConverter.parseHexBinary(s); // fast!            
114    }
115    
116    /**
117     * Creates a byte array from a given string, using the UTF-8
118     * encoding. This calls {@link String#getBytes(java.nio.charset.Charset)} 
119     * internally with "UTF-8" as charset.
120     */
121    public static byte[] toBytesUTF8(String s) {
122        try {
123                        return s.getBytes("UTF-8");
124                } 
125        catch (UnsupportedEncodingException e) {
126                        throw new RuntimeException("JVM does not support UTF-8 encoding.", e);
127                }
128    }
129    
130    /**
131     * Returns the count of the substring 
132     */
133    public static int substrCount(String haystack, String needle) {
134        int lastIndex = 0;
135        int count = 0;
136
137        if (needle != null && haystack != null) {
138                        while (lastIndex != -1) {
139                                lastIndex = haystack.indexOf(needle, lastIndex);
140        
141                                if (lastIndex != -1) {
142                                        count++;
143                                        lastIndex += needle.length();
144                                }
145                }
146        }
147        
148                return count;
149    }
150    
151    public static String getStackTrace(Exception exception) {
152        StringWriter stackTraceStringWriter = new StringWriter();
153        exception.printStackTrace(new PrintWriter(stackTraceStringWriter));
154        
155        return stackTraceStringWriter.toString();
156    }
157        
158        public static <T> String join(List<T> objects, String delimiter, StringJoinListener<T> listener) {
159                StringBuilder objectsStr = new StringBuilder();
160                
161                for (int i=0; i<objects.size(); i++) {
162                        if (listener != null) {
163                                objectsStr.append(listener.getString(objects.get(i)));
164                        }
165                        else {
166                                objectsStr.append(objects.get(i).toString());
167                        }
168                        
169                        if (i < objects.size()-1) { 
170                                objectsStr.append(delimiter);
171                        }                       
172                }
173                
174                return objectsStr.toString();
175        }   
176        
177        public static <T> String join(List<T> objects, String delimiter) {
178                return join(objects, delimiter, null);
179        } 
180        
181        public static <T> String join(T[] objects, String delimiter, StringJoinListener<T> listener) {
182                return join(Arrays.asList(objects), delimiter, listener);
183        }   
184        
185        public static <T> String join(T[] objects, String delimiter) {
186                return join(Arrays.asList(objects), delimiter, null);
187        }   
188        
189        public static interface StringJoinListener<T> {
190                public String getString(T object);
191        }
192}