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
026/**
027 * Utility class for common application string functions.
028 *
029 * @author Philipp C. Heckel (philipp.heckel@gmail.com)
030 */
031public class StringUtil {
032        /**
033         * Transforms a string to a camel case representation, including the
034         * first character.
035         *
036         * <p>Examples:
037         * <ul>
038         *  <li><code>toCamelCase("hello world") -&gt; "HelloWorld"</code></li>
039         *  <li><code>toCamelCase("hello_world") -&gt; "HelloWorld"</code></li>
040         *  <li><code>toCamelCase("hello_World") -&gt; "HelloWorld"</code></li>
041         *  <li><code>toCamelCase("helloWorld") -&gt; "HelloWorld"</code></li>
042         *  <li><code>toCamelCase("HelloWorld") -&gt; "HelloWorld"</code></li>
043         * </ul>
044         */
045        public static String toCamelCase(String str) {
046                StringBuilder sb = new StringBuilder();
047
048                for (String s : str.split("[-_ ]")) {
049                        if (s.length() > 0) {
050                                sb.append(Character.toUpperCase(s.charAt(0)));
051
052                                if (s.length() > 1) {
053                                        sb.append(s.substring(1, s.length()));
054                                }
055                        }
056                }
057
058                return sb.toString();
059        }
060
061        /**
062         * Transforms a string to underscore-delimited representation.
063         *
064         * <p>Examples:
065         * <ul>
066         *  <li><code>toUnderScoreDelimited("HelloWorld") -&gt; "hello_world"</code></li>
067         *  <li><code>toUnderScoreDelimited("helloWorld") -&gt; "hello_world"</code></li>
068         * </ul>
069         */
070        public static String toSnakeCase(String str) {
071                StringBuilder sb = new StringBuilder();
072
073                for (char c : str.toCharArray()) {
074                        if (Character.isLetter(c) || Character.isDigit(c)) {
075                                if (Character.isUpperCase(c)) {
076                                        if (sb.length() > 0) {
077                                                sb.append("_");
078                                        }
079
080                                        sb.append(Character.toLowerCase(c));
081                                } else {
082                                        sb.append(c);
083                                }
084                        } else {
085                                sb.append("_");
086                        }
087                }
088
089                return sb.toString();
090        }
091
092        /**
093         * Converts a byte array to a lower case hex representation.
094         * If the given byte array is <code>null</code>, an empty string is returned.
095         */
096        public static String toHex(byte[] bytes) {
097                if (bytes == null) {
098                        return "";
099                } else {
100                        final char[] hexArray = "0123456789ABCDEF".toCharArray();
101                        char[] hexChars = new char[bytes.length * 2];
102                        for (int j = 0; j < bytes.length; j++) {
103                                int v = bytes[j] & 0xFF;
104                                hexChars[j * 2] = hexArray[v >>> 4];
105                                hexChars[j * 2 + 1] = hexArray[v & 0x0F];
106                        }
107                        return String.valueOf(hexChars).toLowerCase();
108                }
109        }
110
111        /**
112         * Creates byte array from a hex represented string.
113         */
114        public static byte[] fromHex(String hexString) {
115                if (hexString.length() % 2 == 1) {
116                        throw new IllegalArgumentException("hex string must be of even length");
117                }
118
119                char[] hex = hexString.toCharArray();
120                byte[] raw = new byte[hex.length / 2];
121                for (int src = 0, dst = 0; dst < raw.length; ++dst) {
122                        int hi = Character.digit(hex[src++], 16);
123                        int lo = Character.digit(hex[src++], 16);
124                        if ((hi < 0) || (lo < 0))
125                                throw new IllegalArgumentException();
126                        raw[dst] = (byte) (hi << 4 | lo);
127                }
128                return raw;
129        }
130
131        /**
132         * Creates a byte array from a given string, using the UTF-8
133         * encoding. This calls {@link String#getBytes(java.nio.charset.Charset)}
134         * internally with "UTF-8" as charset.
135         */
136        public static byte[] toBytesUTF8(String s) {
137                try {
138                        return s.getBytes("UTF-8");
139                } catch (UnsupportedEncodingException e) {
140                        throw new RuntimeException("JVM does not support UTF-8 encoding.", e);
141                }
142        }
143
144        /**
145         * Returns the count of the substring
146         */
147        public static int substrCount(String haystack, String needle) {
148                int lastIndex = 0;
149                int count = 0;
150
151                if (needle != null && haystack != null) {
152                        while (lastIndex != -1) {
153                                lastIndex = haystack.indexOf(needle, lastIndex);
154
155                                if (lastIndex != -1) {
156                                        count++;
157                                        lastIndex += needle.length();
158                                }
159                        }
160                }
161
162                return count;
163        }
164
165        public static String getStackTrace(Exception exception) {
166                StringWriter stackTraceStringWriter = new StringWriter();
167                exception.printStackTrace(new PrintWriter(stackTraceStringWriter));
168
169                return stackTraceStringWriter.toString();
170        }
171
172        public static <T> String join(List<T> objects, String delimiter, StringJoinListener<T> listener) {
173                StringBuilder objectsStr = new StringBuilder();
174
175                for (int i = 0; i < objects.size(); i++) {
176                        if (listener != null) {
177                                objectsStr.append(listener.getString(objects.get(i)));
178                        } else {
179                                objectsStr.append(objects.get(i).toString());
180                        }
181
182                        if (i < objects.size() - 1) {
183                                objectsStr.append(delimiter);
184                        }
185                }
186
187                return objectsStr.toString();
188        }
189
190        public static <T> String join(List<T> objects, String delimiter) {
191                return join(objects, delimiter, null);
192        }
193
194        public static <T> String join(T[] objects, String delimiter, StringJoinListener<T> listener) {
195                return join(Arrays.asList(objects), delimiter, listener);
196        }
197
198        public static <T> String join(T[] objects, String delimiter) {
199                return join(Arrays.asList(objects), delimiter, null);
200        }
201
202        public static interface StringJoinListener<T> {
203                public String getString(T object);
204        }
205}