001    /**
002     *   GRANITE DATA SERVICES
003     *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004     *
005     *   This file is part of the Granite Data Services Platform.
006     *
007     *   Granite Data Services is free software; you can redistribute it and/or
008     *   modify it under the terms of the GNU Lesser General Public
009     *   License as published by the Free Software Foundation; either
010     *   version 2.1 of the License, or (at your option) any later version.
011     *
012     *   Granite Data Services is distributed in the hope that it will be useful,
013     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015     *   General Public License for more details.
016     *
017     *   You should have received a copy of the GNU Lesser General Public
018     *   License along with this library; if not, write to the Free Software
019     *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020     *   USA, or see <http://www.gnu.org/licenses/>.
021     */
022    package org.granite.util;
023    
024    import java.io.BufferedReader;
025    import java.io.IOException;
026    import java.io.StringReader;
027    import java.lang.reflect.Array;
028    import java.nio.ByteBuffer;
029    import java.nio.LongBuffer;
030    import java.util.ArrayList;
031    import java.util.Arrays;
032    import java.util.Collection;
033    import java.util.HashMap;
034    import java.util.List;
035    import java.util.Map;
036    
037    /**
038     * @author Franck WOLFF
039     */
040    public class StringUtil {
041    
042        private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
043    
044        public static char[] bytesToHexChars(byte[] bytes) {
045            return bytesToHexChars(bytes, new char[bytes.length * 2], 0);
046        }
047    
048        public static char[] bytesToHexChars(byte[] bytes, char[] chars, int off) {
049            if (chars.length - off < bytes.length * 2)
050                    throw new IllegalArgumentException("Unsufficient capacity in 'chars' parameter");
051            
052            for (int i = off, j = 0; i < bytes.length; ) {
053                    int b = bytes[i++] & 0xFF;
054                    chars[j++] = HEX_CHARS[b >> 4];
055                    chars[j++] = HEX_CHARS[b & 0x0F];
056            }
057            
058            return chars;
059        }
060    
061        public static byte[] hexCharsToBytes(char[] chars) {
062            return hexCharsToBytes(chars, 0);
063        }
064        
065        public static byte[] hexCharsToBytes(char[] chars, int off) {
066            final int len = chars.length;
067            if (((len - off) % 2) != 0)
068                    throw new IllegalArgumentException("(chars.length - off) must be even");
069            
070            byte[] bytes = new byte[(len - off) / 2];
071            for (int i = off, j = 0; i < len; ) {
072                    int b = 0;
073                    char c = chars[i++];
074                    b |= ((c - (c < 'A' ? '0' : ('A' - 10))) << 4);
075                    c = chars[i++];
076                    b |= (c - (c < 'A' ? '0' : ('A' - 10)));
077                    bytes[j++] = (byte)b;
078            }
079            return bytes;
080        }
081    
082        public static byte[] hexStringToBytes(String s) {
083            return hexStringToBytes(s, 0);
084        }
085        
086        public static byte[] hexStringToBytes(String s, int off) {
087            final int len = s.length();
088            if (((len - off) % 2) != 0)
089                    throw new IllegalArgumentException("(s.length() - off) must be even");
090            
091            byte[] bytes = new byte[(len - off) / 2];
092            for (int i = off, j = 0; i < len; ) {
093                    int b = 0;
094                    char c = s.charAt(i++);
095                    b |= ((c - (c < 'A' ? '0' : ('A' - 10))) << 4);
096                    c = s.charAt(i++);
097                    b |= (c - (c < 'A' ? '0' : ('A' - 10)));
098                    bytes[j++] = (byte)b;
099            }
100            return bytes;
101        }
102        
103        public static String toHexString(Number n) {
104            if (n == null)
105                return "null";
106    
107            byte[] bytes = new byte[8];
108            ByteBuffer bytesBuffer = ByteBuffer.wrap(bytes);
109            LongBuffer longBuffer = bytesBuffer.asLongBuffer();
110            longBuffer.put(0, n.longValue());
111    
112            StringBuilder sb = new StringBuilder(16);
113            for (int i = 0; i < bytes.length; i++) {
114                int b = bytes[i] & 0xFF;
115                if (b != 0 || sb.length() > 0 || i == (bytes.length - 1))
116                    sb.append(HEX_CHARS[b >> 4]).append(HEX_CHARS[b & 0x0F]);
117            }
118            return sb.toString();
119        }
120            
121            public static String removeSpaces(String s) {
122                    if (s == null)
123                            return null;
124            String[] tokens = s.split("\\s", -1);
125            if (tokens.length == 0)
126                    return "";
127            if (tokens.length == 1)
128                    return tokens[0];
129            StringBuilder sb = new StringBuilder();
130            for (String token : tokens)
131                    sb.append(token);
132            return sb.toString();
133        }
134            
135            public static String[] toStringArray(String s) {
136                    if (s == null)
137                            return new String[0];
138    
139                    List<String> lines = new ArrayList<String>();
140                    try {
141                            BufferedReader reader = new BufferedReader(new StringReader(s));
142                            String line = null;
143                            while ((line = reader.readLine()) != null)
144                                    lines.add(line);
145                    }
146                    catch (IOException e) {
147                            // can't happen...
148                    }
149                    return lines.toArray(new String[lines.size()]);
150            }
151    
152        public static String toString(Object o) {
153            return toString(o, -1);
154        }
155    
156        public static String toString(Object o, int maxItems) {
157            if (o == null)
158                return "null";
159    
160            if (o instanceof String)
161                return ("\"" + o + "\"");
162    
163            if (o instanceof Character || o.getClass() == Character.TYPE)
164                return ("'" + o + "'");
165    
166            if (o instanceof Number) {
167                if (o instanceof Byte || o instanceof Short || o instanceof Integer || o instanceof Long)
168                    return o + " <0x" + toHexString((Number)o) + ">";
169                return String.valueOf(o);
170            }
171    
172            // Enclose code in try catch block for uninitialized proxy exceptions (and the like).
173            try {
174                if (o.getClass().isArray()) {
175                    Class<?> type = o.getClass().getComponentType();
176    
177                    if (maxItems < 0) {
178                        if (type.isPrimitive()) {
179                            if (type == Byte.TYPE)
180                                return Arrays.toString((byte[])o);
181                            if (type == Character.TYPE)
182                                return Arrays.toString((char[])o);
183                            if (type == Integer.TYPE)
184                                 return Arrays.toString((int[])o);
185                            if (type == Double.TYPE)
186                                return Arrays.toString((double[])o);
187                            if (type == Long.TYPE)
188                                return Arrays.toString((long[])o);
189                            if (type == Float.TYPE)
190                                return Arrays.toString((float[])o);
191                            if (type == Short.TYPE)
192                                return Arrays.toString((short[])o);
193                            if (type == Boolean.TYPE)
194                                return Arrays.toString((boolean[])o);
195                            return "[Array of unknown primitive type: " + type + "]"; // Should never append...
196                        }
197                        return Arrays.toString((Object[])o);
198                    }
199    
200                    final int max = Math.min(maxItems, Array.getLength(o));
201                    List<Object> list = new ArrayList<Object>(max);
202    
203                    for (int i = 0; i < max; i++)
204                        list.add(Array.get(o, i));
205                    if (max < Array.getLength(o))
206                        list.add("(first " + max + '/' + Array.getLength(o) + " elements only...)");
207    
208                    o = list;
209                }
210                else if (o instanceof Collection<?> && maxItems >= 0) {
211    
212                    Collection<?> coll = (Collection<?>)o;
213                    final int max = Math.min(maxItems, coll.size());
214                    List<Object> list = new ArrayList<Object>(max);
215    
216                    int i = 0;
217                    for (Object item : coll) {
218                        if (i >= max) {
219                            list.add("(first " + max + '/' + coll.size() + " elements only...)");
220                            break;
221                        }
222                        list.add(item);
223                        i++;
224                    }
225    
226                    o = list;
227                }
228                else if (o instanceof Map<?, ?> && maxItems >= 0) {
229                    Map<?, ?> map = (Map<?, ?>)o;
230                    final int max = Math.min(maxItems, map.size());
231                    Map<Object, Object> copy = new HashMap<Object, Object>(max);
232    
233                    int i = 0;
234                    for (Map.Entry<?, ?> item : map.entrySet()) {
235                        if (i >= max) {
236                            copy.put("(first " + max + '/' + map.size() + " elements only...)", "...");
237                            break;
238                        }
239                        copy.put(item.getKey(), item.getValue());
240                        i++;
241                    }
242    
243                    o = copy;
244                }
245    
246                return String.valueOf(o);
247            } catch (Exception e) {
248                return o.getClass().getName() + " (exception: " + e.toString() + ")";
249            }
250        }
251    }