Java tutorial
// Copyright (c) 2015 ServiceStack LLC. All rights reserved. // License: https://servicestack.net/bsd-license.txt package net.servicestack.client; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import net.servicestack.func.Function; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.HttpURLConnection; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import static net.servicestack.func.Func.last; // Generic Utils public class Utils { public static Integer tryParseInt(String str) { try { return Integer.parseInt(str); } catch (Exception ex) { return null; } } public static Double tryParseDouble(String str) { try { return Double.parseDouble(str); } catch (Exception ex) { return null; } } static final String KotlinAnnotationClass = "kotlin.jvm.internal.KotlinClass"; public static boolean isKotlinClass(Class type) { for (Annotation attr : type.getAnnotations()) { if (KotlinAnnotationClass.equals(attr.annotationType().getName())) return true; } return false; } public static Field[] getSerializableFields(Class type) { List<Field> fields = new ArrayList<Field>(); boolean isKotlin = isKotlinClass(type); for (Class<?> c = type; c != null; c = c.getSuperclass()) { if (c == Object.class) break; for (Field f : c.getDeclaredFields()) { if (Modifier.isStatic(f.getModifiers())) continue; if (!Modifier.isPublic(f.getModifiers())) { if (isKotlin) { //Kotlin class convention has private backing fields f.setAccessible(true); //Needed to access private fields } else { continue; } } fields.add(f); } } return fields.toArray(new Field[fields.size()]); } public static String stripQuotes(String str) { String result = str; if (str.indexOf("\"") == 0 && str.lastIndexOf("\"") == str.length() - 1) { result = str.substring(1, str.length() - 1); } return result; } public static void reverse(byte[] bytes) { if (bytes == null) return; int i = 0; int j = bytes.length - 1; byte hold; while (j > i) { hold = bytes[j]; bytes[j] = bytes[i]; bytes[i] = hold; j--; i++; } } final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); public static String toHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } public static byte[] fromHex(String hex) { int len = hex.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); } return data; } public static String toGuidString(UUID uuid) { byte[] guidBytes = toGuidBytes(uuid); String to = toHex(guidBytes); return to; } public static UUID fromGuidString(String guid) { guid = guid.replaceAll("-", ""); byte[] guidBytes = fromHex(guid); UUID to = fromGuidBytes(guidBytes); return to; } public static UUID fromGuidBytes(byte[] guidBytes) { ByteBuffer buf = ByteBuffer.wrap(guidBytes); byte[] first4 = new byte[4]; buf.get(first4); reverse(first4); byte[] second2 = new byte[2]; buf.get(second2); reverse(second2); byte[] third2 = new byte[2]; buf.get(third2); reverse(third2); long lsb = buf.getLong(); buf = ByteBuffer.wrap(new byte[8]).put(first4).put(second2).put(third2); buf.rewind(); long msb = buf.getLong(); return new UUID(msb, lsb); } public static byte[] toGuidBytes(UUID theUuid) { ByteBuffer first8 = ByteBuffer.allocate(8); first8.putLong(theUuid.getMostSignificantBits()); first8.rewind(); byte[] first4 = new byte[4]; first8.get(first4); reverse(first4); byte[] second2 = new byte[2]; first8.get(second2); reverse(second2); byte[] third2 = new byte[2]; first8.get(third2); reverse(third2); ByteBuffer converted16 = ByteBuffer.allocate(16).put(first4).put(second2).put(third2); ByteBuffer last8 = ByteBuffer.allocate(8); last8.putLong(theUuid.getLeastSignificantBits()); last8.rewind(); converted16.put(last8); return converted16.array(); } static final String wcfJsonPrefix = "/Date("; private static final ThreadLocal<SimpleDateFormat> iso8601Formatter = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); } }; private static final ThreadLocal<SimpleDateFormat> iso8601FormatterWithMs = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); } }; public static String toJsonDate(Date date) { return "/Date(" + date.getTime() + "-0000)/"; } public static Date parseDate(String string) { String str = string.startsWith("\\") ? string.substring(1) : string; if (str.startsWith(wcfJsonPrefix)) { String body = splitOnLast(splitOnFirst(str, '(')[1], ')')[0]; String unixTimeStr = splitOnFirst(body, '-', 1)[0]; unixTimeStr = splitOnFirst(unixTimeStr, '+', 1)[0]; long unixTime = Long.parseLong(unixTimeStr); return new Date(unixTime); } return fromIsoDateString(string); } public final static int isoDateLength = "YYYY-MM-DDT00:00:00+00:00".length(); public final static int isoDateWithMsLength = "YYYY-MM-DDT00:00:00.000+00:00".length(); public final static int isoDateWithSubMsMin = "YYYY-MM-DDT00:00:00.0000+00:00".length(); public final static int isoDateWithSubMsMax = "YYYY-MM-DDT00:00:00.0000000+00:00".length(); public static String stripSubMillis(String iso8601string) { if (iso8601string.length() < isoDateWithSubMsMin || iso8601string.length() > isoDateWithSubMsMax) return iso8601string; String[] parts = splitOnFirst(iso8601string, '.'); String suffix = parts[1].substring(parts[1].length() - 6); //+00:00 String ms = parts[1].substring(0, 3); return parts[0] + "." + ms + suffix; } public static Date fromIsoDateString(String iso8601string) { if (iso8601string == null) return null; String s = iso8601string.replace("Z", "+00:00"); try { s = s.substring(0, 22) + s.substring(23); // to get rid of the ":" s = stripSubMillis(s); if (s.length() == isoDateWithMsLength) return iso8601FormatterWithMs.get().parse(s); return iso8601Formatter.get().parse(s); } catch (Exception e) { return ParseManual(iso8601string); } } public static Date ParseManual(String dateTimeStr) { if (dateTimeStr == null || dateTimeStr.length() < "yyyy-MM-dd".length()) return null; if (dateTimeStr.endsWith("Z")) dateTimeStr = dateTimeStr.substring(0, dateTimeStr.length() - 1); String[] parts = dateTimeStr.split("T"); if (parts.length == 1) parts = Utils.splitOnFirst(dateTimeStr, ' '); String[] dateParts = parts[0].split("-"); int hh = 0, min = 0, ss = 0, ms = 0; double subMs = 0; int offsetMultiplier = 0; if (parts.length == 1) { return new Date(Utils.tryParseInt(dateParts[0]) - 1900, Utils.tryParseInt(dateParts[1]) - 1, Utils.tryParseInt(dateParts[2]), 0, 0, 0); } else if (parts.length == 2) { String[] timeStringParts = parts[1].split("\\+"); if (timeStringParts.length == 2) { offsetMultiplier = -1; } else { timeStringParts = parts[1].split("-"); if (timeStringParts.length == 2) { offsetMultiplier = 1; } } String timeOffset = timeStringParts.length == 2 ? timeStringParts[1] : null; String[] timeParts = timeStringParts[0].split(":"); if (timeParts.length == 3) { Integer val = null; if ((val = Utils.tryParseInt(timeParts[0])) != null) hh = val; if ((val = Utils.tryParseInt(timeParts[1])) != null) min = val; String[] secParts = timeParts[2].split("\\."); if ((val = Utils.tryParseInt(secParts[0])) != null) ss = val; if (secParts.length == 2) { String msStr = String.format("%03d", Utils.tryParseInt(secParts[1])); ms = Utils.tryParseInt(msStr.substring(0, 3)); if (msStr.length() > 3) { String subMsStr = msStr.substring(3); // subMs = Utils.tryParseDouble(subMsStr) / Math.pow(10, subMsStr.length()); } } } Date dateTime = new Date(Utils.tryParseInt(dateParts[0]) - 1900, Utils.tryParseInt(dateParts[1]) - 1, Utils.tryParseInt(dateParts[2]), hh, min, ss); Calendar cal = Calendar.getInstance(); cal.setTime(dateTime); if (ms > 0) { cal.add(Calendar.MILLISECOND, ms); } // if (subMs != 0) // dateTime=dateTime.AddMilliseconds(subMs); //Doesn't support sub millis if (offsetMultiplier != 0 && timeOffset != null) { timeParts = timeOffset.split(":"); if (timeParts.length == 2) { hh = Utils.tryParseInt(timeParts[0]); min = Utils.tryParseInt(timeParts[1]); } else { hh = Utils.tryParseInt(timeOffset.substring(0, 2)); min = Utils.tryParseInt(timeOffset.substring(2)); } cal.add(Calendar.HOUR, offsetMultiplier * hh); cal.add(Calendar.MINUTE, offsetMultiplier * min); } dateTime = cal.getTime(); return dateTime; } return null; } /*String Utils*/ public static String[] splitOnFirst(String strVal, char needle) { return splitOnFirst(strVal, needle, 0); } public static String[] splitOnFirst(String strVal, char needle, int start) { if (strVal == null) return new String[0]; int pos = strVal.indexOf(needle, start); return pos == -1 ? new String[] { strVal } : new String[] { strVal.substring(0, pos), strVal.substring(pos + 1) }; } public static String[] splitOnFirst(String strVal, String needle) { return splitOnFirst(strVal, needle, 0); } public static String[] splitOnFirst(String strVal, String needle, int start) { if (strVal == null) return new String[0]; int pos = strVal.indexOf(needle, start); return pos == -1 ? new String[] { strVal } : new String[] { strVal.substring(0, pos), strVal.substring(pos + needle.length()) }; } public static String[] splitOnLast(String strVal, char needle) { return splitOnLast(strVal, needle, strVal.length()); } public static String[] splitOnLast(String strVal, char needle, int start) { if (strVal == null) return new String[0]; int pos = strVal.lastIndexOf(needle, start); return pos == -1 ? new String[] { strVal } : new String[] { strVal.substring(0, pos), strVal.substring(pos + 1) }; } public static String[] splitOnLast(String strVal, String needle) { return splitOnLast(strVal, needle, strVal.length()); } public static String[] splitOnLast(String strVal, String needle, int start) { if (strVal == null) return new String[0]; int pos = strVal.lastIndexOf(needle, start); return pos == -1 ? new String[] { strVal } : new String[] { strVal.substring(0, pos), strVal.substring(pos + needle.length()) }; } public static String combinePath(String basePath, String withPath) { if (basePath == null) basePath = ""; if (withPath == null) withPath = ""; String prefix = basePath.endsWith("/") ? basePath : basePath + "/"; String suffix = withPath.startsWith("/") ? withPath.substring(1) : withPath; return prefix + suffix; } public static String fromUtf8Bytes(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public static byte[] toUtf8Bytes(String string) { try { return string.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public static String readToEnd(HttpURLConnection response) { try { return readToEnd(response.getInputStream(), "UTF-8"); } catch (IOException e) { throw new RuntimeException(e); } } public static String readToEnd(InputStream stream, final String charsetName) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charsetName)); String line; StringBuilder sb = new StringBuilder(); while ((line = reader.readLine()) != null) { sb.append(line); } String text = sb.toString(); reader.close(); return text; } public static byte[] readBytesToEnd(HttpURLConnection response) { try { return readBytesToEnd(response.getInputStream()); } catch (IOException e) { throw new RuntimeException(e); } } public static byte[] readBytesToEnd(InputStream stream) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024); final BufferedInputStream bufferedStream = new BufferedInputStream(stream, 8192); try { final byte[] buffer = new byte[1024]; int bytesRead = 0; while ((bytesRead = bufferedStream.read(buffer)) > 0) { bytes.write(buffer, 0, bytesRead); } return bytes.toByteArray(); } finally { bufferedStream.close(); } } public static String getUnderlyingContentType(String contentType) { return splitOnFirst(contentType, ';')[0].trim().toLowerCase(); } public static boolean matchesContentType(String contentType, String matchesContentType) { return getUnderlyingContentType(contentType).equals(getUnderlyingContentType(matchesContentType)); } public static String sanitizeVarName(String name) { return name.replaceAll("_", "").toLowerCase(); } public static ResponseStatus createResponseStatus(Object obj) { if (obj instanceof JsonObject) { return createResponseStatus((JsonObject) obj); } return null; } public static ResponseStatus createResponseStatus(JsonObject obj) { ResponseStatus status = new ResponseStatus(); for (Map.Entry<String, JsonElement> jsonElementEntry : obj.entrySet()) { String key = jsonElementEntry.getKey(); String varName = Utils.sanitizeVarName(key); try { Object field = obj.get(key); if (varName.toLowerCase().equals("errorcode")) { status.setErrorCode(getAsStringOrNull(jsonElementEntry.getValue())); } else if (varName.toLowerCase().equals("message")) { status.setMessage(getAsStringOrNull(jsonElementEntry.getValue())); } else if (varName.toLowerCase().equals("stacktrace")) { status.setStackTrace(getAsStringOrNull(jsonElementEntry.getValue())); } else if (varName.toLowerCase().equals("errors")) { if (field instanceof JsonArray) { JsonArray jFields = (JsonArray) field; ArrayList<ResponseError> errors = new ArrayList<>(); for (JsonElement fieldElement : jFields) { JsonObject fieldObj = fieldElement.getAsJsonObject(); ResponseError fieldError = new ResponseError(); for (Map.Entry<String, JsonElement> entry : fieldObj.entrySet()) { String fieldKey = entry.getKey(); String fieldName = Utils.sanitizeVarName(fieldKey); if (fieldName.toLowerCase().equals("errorcode")) { fieldError.setErrorCode(getAsStringOrNull(entry.getValue())); } else if (fieldName.toLowerCase().equals("message")) { fieldError.setMessage(getAsStringOrNull(entry.getValue())); } else if (fieldName.toLowerCase().equals("fieldname")) { fieldError.setFieldName(getAsStringOrNull(entry.getValue())); } } errors.add(fieldError); } status.setErrors(errors); } status.setStackTrace(obj.get(key).toString()); } } catch (JsonParseException e) { Log.e(e); } } return status; } public static <T> ArrayList<T> createList(T... params) { ArrayList<T> to = new ArrayList<>(); to.addAll(Arrays.asList(params)); return to; } public static <K, V> HashMap<K, V> createMap(K k1, V v1) { HashMap<K, V> to = new HashMap<>(); to.put(k1, v1); return to; } public static <K, V> HashMap<K, V> createMap(K k1, V v1, K k2, V v2) { HashMap<K, V> to = new HashMap<>(); to.put(k1, v1); to.put(k2, v2); return to; } public static <K, V> HashMap<K, V> createMap(K k1, V v1, K k2, V v2, K k3, V v3) { HashMap<K, V> to = new HashMap<>(); to.put(k1, v1); to.put(k2, v2); to.put(k3, v3); return to; } public static boolean equals(String s1, String s2) { if (s1 == null) return s2 == null; if (s2 == null) return s1 == null; return s1.equals(s2); } public static String trimStart(String text, char character) { if (text == null || text.length() == 0) return ""; int i = 0; while (text.charAt(i) == character) { i++; } return text.substring(i).trim(); } public static String trimEnd(String text, char character) { if (text == null || text.length() == 0) return ""; int i = text.length() - 1; while (text.charAt(i) == character) { if (--i < 0) { return ""; } } return text.substring(0, i + 1).trim(); } public static String toHumanFriendlyUrl(String url) { if (url == null) return null; url = trimEnd(last(splitOnFirst(url, "://")), '/'); return url; } public static <K, V> HashMap<K, ArrayList<V>> createMap(ArrayList<V> xs, Function<V, K> f) { HashMap<K, ArrayList<V>> to = new HashMap<>(); if (xs == null) return to; for (V val : xs) { K key = f.apply(val); ArrayList<V> list = to.get(key); if (list == null) to.put(key, list = new ArrayList<V>()); list.add(val); } return to; } //From: http://iharder.sourceforge.net/current/java/base64/ (Public Domain) public static String toBase64String(String source) { return toBase64String(toUtf8Bytes(source)); } public static String toBase64String(byte[] source) { byte[] encoded = toBase64Bytes(source); try { return new String(encoded, "US-ASCII"); } catch (UnsupportedEncodingException e) { return new String(encoded); } } public static byte[] toBase64Bytes(byte[] source) { return toBase64Bytes(source, 0, source.length); } public static byte[] toBase64Bytes(byte[] source, int off, int len) { if (source == null) throw new NullPointerException("Cannot serialize a null array."); if (off < 0) throw new IllegalArgumentException("Cannot have negative offset: " + off); if (len < 0) throw new IllegalArgumentException("Cannot have length offset: " + len); if (off + len > source.length) throw new IllegalArgumentException(String.format( "Cannot have offset of %d and length of %d with array of length %d", off, len, source.length)); int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual encoding byte[] outBuff = new byte[encLen]; int d = 0; int e = 0; int len2 = len - 2; for (; d < len2; d += 3, e += 4) { encode3to4(source, d + off, 3, outBuff, e); } if (d < len) { encode3to4(source, d + off, len - d, outBuff, e); e += 4; } // Only resize array if we didn't guess it right. if (e <= outBuff.length - 1) { byte[] finalOut = new byte[e]; System.arraycopy(outBuff, 0, finalOut, 0, e); return finalOut; } else { return outBuff; } } private static String getAsStringOrNull(JsonElement jsonElement) { return jsonElement.isJsonNull() ? null : jsonElement.getAsString(); } private final static byte EQUALS_SIGN = (byte) '='; private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' }; private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) { byte[] ALPHABET = _STANDARD_ALPHABET; int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0); switch (numSigBytes) { case 3: destination[destOffset] = ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; return destination; case 2: destination[destOffset] = ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; destination[destOffset + 3] = EQUALS_SIGN; return destination; case 1: destination[destOffset] = ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2] = EQUALS_SIGN; destination[destOffset + 3] = EQUALS_SIGN; return destination; default: return destination; } } }