/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.util; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.math.BigInteger; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.regex.Pattern; import org.whole.lang.model.IEntity; /** * @author Riccardo Solmi, Enrico Persiani */ public class StringUtils { private static final int READER_CHUNK_SIZE = 2048; private static final int BUFFER_SIZE = 8 * READER_CHUNK_SIZE; public static final String CLASS_FILE_EXTENSION = "class"; public static final String JAVA_FILE_EXTENSION = "java"; public static final String EOL_CHARS = "\n\r\u2028\u2029\u0085"; public static final String EOL_REGEXP = "\\r\\n|[\\n\\r\\u2028\\u2029\\u0085]"; public static final Pattern EOL_PATTERN = Pattern.compile(EOL_REGEXP); public static boolean isNewLineChar(char c) { return EOL_CHARS.indexOf(c) != -1; } public static String readAsString(InputStream contentStream, String encoding) throws IOException { if (!contentStream.markSupported()) contentStream = new BufferedInputStream(contentStream); // strip BOM if present StringUtils.encodingFromBOM(contentStream, null); return readAsString(new InputStreamReader(contentStream, encoding)); } public static String readAsString(Reader contentReader) throws IOException { BufferedReader reader = new BufferedReader(contentReader, BUFFER_SIZE); StringBuilder buffer = new StringBuilder(BUFFER_SIZE); char[] readBuffer = new char[READER_CHUNK_SIZE]; int n = reader.read(readBuffer); while (n > 0) { buffer.append(readBuffer, 0, n); n = reader.read(readBuffer); } return buffer.toString(); } public static String[] readAsStringRows(InputStream contentStream, String encoding) throws IOException { if (!contentStream.markSupported()) contentStream = new BufferedInputStream(contentStream); // strip BOM if present StringUtils.encodingFromBOM(contentStream, null); return readAsString(contentStream, encoding).split(EOL_REGEXP, -1); } public static char[] peekChars(Reader reader, int length) throws IOException { if (!reader.markSupported()) return null; char[] chars = new char[length]; reader.mark(chars.length); int read = reader.read(chars); reader.reset(); if (read == -1 || read != length) return null; if (read != length) { char[] readChars = new char[read]; System.arraycopy(chars, 0, readChars, 0, read); return readChars; } else return chars; } public static byte[] peekBytes(InputStream is, int length, boolean exactLength) throws IOException { if (!is.markSupported()) return null; byte[] bytes = new byte[length]; is.mark(bytes.length); int read = is.read(bytes); is.reset(); if (read == -1 || (read != length && exactLength)) return null; if (read != length) { byte[] readBytes = new byte[read]; System.arraycopy(bytes, 0, readBytes, 0, read); return readBytes; } else return bytes; } public static String encodingFromBOM(InputStream is, String defaultEncoding) { String encoding = null; try { // the BOM algorithm needs the first four bytes byte[] bytes = StringUtils.peekBytes(is, 4, true); if (bytes != null) { int magic = 0; for (int i=0; i<bytes.length; i++) magic |= (bytes[i] & 0xFF) << (8*(3-i)); encoding = StringUtils.encodingFromBOM(magic); // skip bom length bytes is.read(bytes, 0, bomLength(magic)); } } catch (IOException e) { } return encoding != null ? encoding : defaultEncoding; } public static int bomLength(int magic) { // first check for a byte order mark 4 bytes long switch (magic) { case 0x0000FEFF: case 0xFFFE0000: case 0xDD736673: case 0x84319533: case 0x2B2F7638: case 0x2B2F7639: case 0x2B2F762B: case 0x2B2F762F: return 4; } // then check for a byte order mark 3 bytes long switch (magic & 0xFFFFFF00) { case 0xEFBBBF00: case 0xF7644C00: case 0x0EFEFF00: case 0xFBEE2800: return 3; } // then check for a byte order mark 2 bytes long switch (magic & 0xFFFF0000) { case 0xFEFF0000: case 0xFFFE0000: return 2; } return 0; } public static String encodingFromBOM(int magic) { // first check for a byte order mark 4 bytes long switch (magic) { case 0x0000FEFF: return "UTF-32BE"; case 0xFFFE0000: return "UTF-32LE"; case 0xDD736673: return "UTF-EBCDIC"; case 0x84319533: return "GB-18030"; case 0x2B2F7638: case 0x2B2F7639: case 0x2B2F762B: case 0x2B2F762F: return "UTF-7"; } // then check for a byte order mark 3 bytes long switch (magic & 0xFFFFFF00) { case 0xEFBBBF00: return "UTF-8"; case 0xF7644C00: return "UTF-1"; case 0x0EFEFF00: return "SCSU"; case 0xFBEE2800: return "BOCU-1"; } // then check for a byte order mark 2 bytes long switch (magic & 0xFFFF0000) { case 0xFEFF0000: return "UTF-16BE"; case 0xFFFE0000: return "UTF-16LE"; } return null; } public static boolean isWhitespace(CharSequence cs) { int count = 0, length = cs.length(); while (count < length && Character.isWhitespace(cs.charAt(count))) count++; return count == length; } public static boolean isUpperCap(String str) { return str.length() > 0 && Character.isUpperCase(str.charAt(0)); } public static String toUpperCap(String str) { if (str.length() > 1) return str.substring(0, 1).toUpperCase()+str.substring(1); else return str.toUpperCase(); } public static boolean isLowerCap(String str) { return str.length() > 0 && Character.isLowerCase(str.charAt(0)); } public static String toLowerCap(String str) { if (str.length() > 1) return str.substring(0, 1).toLowerCase()+str.substring(1); else return str.toLowerCase(); } public static StringBuilder toLowerCap(StringBuilder builder) { if (builder.length()>0) { char ch = builder.charAt(0); if (Character.isUpperCase(ch)) builder.setCharAt(0, Character.toLowerCase(ch)); } return builder; } public static String toLowerPrefix(String str) { return toLowerPrefix(new StringBuilder(str)).toString(); } public static StringBuilder toLowerPrefix(StringBuilder builder) { for (int i=0; i<builder.length(); i++) { char ch = builder.charAt(i); if (Character.isUpperCase(ch)) builder.setCharAt(i, Character.toLowerCase(ch)); else if (Character.isLetter(ch)) { if (i>1) builder.setCharAt(i-1, Character.toUpperCase(builder.charAt(i-1))); break; } } return builder; } public static String camelCaseToUpperCaseWithUnderscores(String ccString) { return camelCaseToSeparatedWords(ccString, "_").toUpperCase(); } public static String camelCaseToSpacedWords(String ccString) { return camelCaseToSeparatedWords(ccString, " "); } public static String camelCaseToSeparatedWords(String ccString, String separator) { StringBuilder buffer = new StringBuilder(ccString); for (int i=ccString.length()-1; i>0; i--) if (Character.isUpperCase(ccString.charAt(i)) && Character.isLowerCase(ccString.charAt(i-1))) buffer.insert(i, separator); return buffer.toString(); } public static String toXMLString(String javaString) { char[] charArray = javaString.toCharArray(); StringBuilder buffer = new StringBuilder(); for (int i=0; i<charArray.length; i++) { final char ch = charArray[i]; switch (ch) { case ' ': buffer.append(' '); break; case '<': buffer.append("<"); break; case '>': buffer.append(">"); break; case '\'': buffer.append("'"); break; case '"': buffer.append("""); break; case '&': buffer.append("&"); break; case '\b': buffer.append(""); break; case '\t': buffer.append(" "); break; case '\n': buffer.append(" "); break; case '\f': buffer.append(" "); break; case '\r': buffer.append(" "); break; default: if (Character.isISOControl(ch) || Character.isWhitespace(ch)) { buffer.append("&#x"); buffer.append(charToHex(ch)); buffer.append(';'); } else buffer.append(ch); break; } } return buffer.toString(); } public static String toXMLCharData(String javaString) { char[] charArray = javaString.toCharArray(); StringBuilder buffer = new StringBuilder(); for (int i=0; i<charArray.length; i++) { final char ch = charArray[i]; switch (ch) { case ' ': buffer.append(' '); break; case '<': buffer.append("<"); break; case '>': buffer.append(">"); break; case '\'': buffer.append("'"); break; case '"': buffer.append("""); break; case '&': buffer.append("&"); break; case '\b': buffer.append(""); break; case '\t': case '\n': buffer.append(ch); break; case '\f': buffer.append(" "); break; case '\r': buffer.append(" "); break; default: if (Character.isISOControl(ch) || Character.isWhitespace(ch)) { buffer.append("&#x"); buffer.append(charToHex(ch)); buffer.append(';'); } else buffer.append(ch); break; } } return buffer.toString(); } public static byte[] cdataFilter;//0 valid; 1 invalid; 2 cdata_end; 3 surrogate public static byte cdataFilter(char ch) { if (cdataFilter == null) { cdataFilter = new byte[65536]; for (int i=0; i<cdataFilter.length; i++) { if (i < 0x0020) switch (i) { case '\t': case '\n': case '\r': cdataFilter[i]=0; break; default: cdataFilter[i]=1; break; } else if (i <= 0xd7ff) { if (i == ']') cdataFilter[i]=2; else cdataFilter[i]=0; } else if (i < 0xe000) { cdataFilter[i]=3; } else if (i <= 0xfffd) cdataFilter[i]=0; else cdataFilter[i]=1; } } return cdataFilter[ch]; } public static String toXMLCData(String cdata) { char[] charArray = cdata.toCharArray(); StringBuilder buffer = new StringBuilder(); for (int i=0; i<charArray.length; i++) { final char ch = charArray[i]; switch (cdataFilter(ch)) { case 0: buffer.append(ch); break; case 1: break; case 2: if (charArray[i+1] == ']' && charArray[i+2] == '>') { buffer.append("]]]]><![CDATA[>"); i +=2; } else buffer.append(ch); break; case 3: if (Character.isSurrogatePair(ch, charArray[i+1]) && Character.isSupplementaryCodePoint(Character.toCodePoint(ch, charArray[i+1]))) { buffer.append(ch); buffer.append(charArray[++i]); } } } return buffer.toString(); } private static final Pattern DOUBLE_DASH = Pattern.compile("--"); public static String toXMLCommentText(String text) { if (text.length() == 0) return text; if (text.charAt(text.length()-1) == '-') text = text.substring(0, text.length()-1)+"-"; if (text.charAt(0) == '-') text = "-"+text.substring(1); return DOUBLE_DASH.matcher(text).replaceAll("--"); } public static String byteToHex(byte b) { // Returns hex String representation of byte b char hexDigit[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] }; return new String(array); } public static String charToHex(char c) { // Returns hex String representation of char c byte hi = (byte) (c >>> 8); byte lo = (byte) (c & 0xff); return byteToHex(hi) + byteToHex(lo); } public static boolean isQualified(String name) { return name.indexOf('.') >= 0; } public static boolean isQualified2(String name) { return name.indexOf('.', name.indexOf('.')) >= 0; } public static String toPackageName(String qname) { int s = qname.lastIndexOf('.'); return (s == -1) ? qname : qname.substring(0, s); } public static String toSimpleName(String qname) { int s = qname.lastIndexOf('.'); return (s == -1) ? qname : qname.substring(s+1); } public static String toPackageQualifiedName(String qname) { return toPackageName(toPackageName(qname)); } public static String toSimpleQualifiedName(String qname) { int s = qname.lastIndexOf('.'); if (s == -1) return qname; else { s = qname.lastIndexOf('.', s-1); return (s == -1) ? qname : qname.substring(s+1); } } public static String packagePath(String packageName) { return packageName.replace('.', File.separatorChar); } public static String packageURIPath(String packageName) { return packageName.replace('.', '/'); } public static String packageName(String packagePath) { return packagePath.replace(File.separatorChar, '.'); } public static String toResourceClasspath(String className) { return appendFileExtension(className.replace('.', '/'), CLASS_FILE_EXTENSION); } public static String toClassName(String resourceClasspath) { return StringUtils.stripFileExtension(resourceClasspath).replace('/', '.'); } public static boolean inList(String word, String wordList) { return inList(word, wordList, ','); } public static boolean inList(String word, String wordList, char separator) { int idx = wordList.indexOf(word); if (idx != -1) { int left = idx-1; int right = idx+word.length(); return (left == -1 || wordList.charAt(left)==separator) && (right == wordList.length() || wordList.charAt(right)==separator); } return false; } public static final String transientId(IEntity entity) { return entity.wGetEntityDescriptor() + "@" + System.identityHashCode(entity); } public static final String simpleId(IEntity entity) { return entity.wGetEntityDescriptor().getName() + "@" + System.identityHashCode(entity); } private static Map<String, String> wrapperClassNamesMap; private static Map<String, String> wrapperClassNamesMap() { if (wrapperClassNamesMap == null) { wrapperClassNamesMap = new HashMap<String, String>(); wrapperClassNamesMap.put("Boolean", "boolean"); wrapperClassNamesMap.put("Byte", "byte"); wrapperClassNamesMap.put("Character", "char"); wrapperClassNamesMap.put("Double", "double"); wrapperClassNamesMap.put("Float", "float"); wrapperClassNamesMap.put("Integer", "int"); wrapperClassNamesMap.put("Long", "long"); wrapperClassNamesMap.put("Short", "short"); wrapperClassNamesMap.put("java.lang.Boolean", "boolean"); wrapperClassNamesMap.put("java.lang.Byte", "byte"); wrapperClassNamesMap.put("java.lang.Character", "char"); wrapperClassNamesMap.put("java.lang.Double", "double"); wrapperClassNamesMap.put("java.lang.Float", "float"); wrapperClassNamesMap.put("java.lang.Integer", "int"); wrapperClassNamesMap.put("java.lang.Long", "long"); wrapperClassNamesMap.put("java.lang.Short", "short"); } return wrapperClassNamesMap; } public static boolean isWrapper(String dataTypeName) { return wrapperClassNamesMap().containsKey(dataTypeName); } public static String unboxFilter(String dataTypeName) { String result = wrapperClassNamesMap().get(dataTypeName); return result != null ? result : dataTypeName; } private static Set<String> javaLangTypes; private static Set<String> javaLangTypes() { if (javaLangTypes == null) { javaLangTypes = new HashSet<String>(); javaLangTypes.addAll(Arrays.<String>asList(new String[] { "Package","Class","Object","Runtime","Process","System","Thread","Exception","Error", "Boolean","Byte","Character","Double","Float","Integer","Long","Short","String", "StringBuffer","StringBuilder","Void","Number","Math","Enum"})); } return javaLangTypes; } public static boolean isAmbiguous(String simpleName) { return javaLangTypes().contains(simpleName); } public static boolean isString(String id) { return "String".equals(id) || "java.lang.String".equals(id); } private static Map<String, Class<?>> primitiveTypesMap; private static Map<String, Class<?>> primitiveTypesMap() { if (primitiveTypesMap == null) { primitiveTypesMap = new HashMap<String, Class<?>>(); primitiveTypesMap.put("boolean", boolean.class); primitiveTypesMap.put("byte", byte.class); primitiveTypesMap.put("char", char.class); primitiveTypesMap.put("double", double.class); primitiveTypesMap.put("float", float.class); primitiveTypesMap.put("int", int.class); primitiveTypesMap.put("long", long.class); primitiveTypesMap.put("short", short.class); primitiveTypesMap.put("void", void.class); } return primitiveTypesMap; } public static boolean isPrimitive(String id) { return primitiveTypesMap().containsKey(id); } public static Class<?> primitiveClass(String id) { return primitiveTypesMap().get(id); } private static Map<String, Class<?>> javaTypesMap; private static Map<String, Class<?>> javaTypesMap() { if (javaTypesMap == null) { javaTypesMap = new HashMap<String, Class<?>>(); javaTypesMap.putAll(primitiveTypesMap()); javaTypesMap.put("String", String.class); javaTypesMap.put("java.lang.String", String.class); } return javaTypesMap; } public static boolean isPrimitiveOrString(String id) { return javaTypesMap().containsKey(id); } public static Class<?> primitiveOrStringClass(String id) { return javaTypesMap().get(id); } private static Set<String> javaKeywords; private static Set<String> javaKeywords() { if (javaKeywords == null) { javaKeywords = new HashSet<String>(); javaKeywords.addAll(Arrays.<String>asList(new String[] { "default","final","finally","interface","class","package","private","protected","public", "static","transient","native","volatile","abstract","synchronized","this","super", "catch","try","throw","throws","if","else","switch","case","for","while","do","import", "true", "false", "null", "implements", "extends", "const", "goto", "enum" })); javaKeywords.addAll(primitiveTypesMap().keySet()); } return javaKeywords; } public static boolean isJavaKeyword(String id) { return javaKeywords().contains(id); } public static String javaKeywordFilter(String id) { return isJavaKeyword(id) ? "_"+id : id; } public static String fieldName(String accessorName) { if (accessorName.startsWith("is")) return toLowerCap(accessorName.substring(2)); if (accessorName.startsWith("get")) return toLowerCap(accessorName.substring(3)); if (accessorName.startsWith("set")) return toLowerCap(accessorName.substring(3)); return accessorName; } public static String getterName(String type, String field) { return (type.equals("boolean") ? "is" : "get")+toUpperCap(field); } public static String getterName(String field) { return "get"+toUpperCap(field); } public static String setterName(String field) { return "set"+toUpperCap(field); } public static int commonPrefix(String str1, String str2) { int index = 0; int minLength = Math.min(str1.length(), str2.length()); while (index < minLength && str1.charAt(index) == str2.charAt(index)) index++; return index; } public static String camelPrefix(String str, int prefixSize) { return prefixSize+1 >= str.length() ? str : Character.isUpperCase(str.charAt(prefixSize)) ? str.substring(0, prefixSize+2)+"\u2026" : str.substring(0, prefixSize+1)+"\u2026"; } public static String removePrefix(String str, String prefix) { return str.startsWith(prefix) ? str.substring(prefix.length()) : str; } public static String removeSuffix(String str, String suffix) { int index = str.lastIndexOf(suffix); return index > -1 ? str.substring(0, index) : str; } public static Pattern regexPattern; //chars: \|([{.^$?*+ public static String quoteRegex(String regex) { if (regexPattern == null) regexPattern = Pattern.compile("[\\\\|\\(\\)\\[\\{\\.\\^\\$\\?\\*\\+]"); if (regexPattern.matcher(regex).find()) return Pattern.quote(regex); else return regex; } public static String escapeString(String t, boolean escapeDoubleQuotes, boolean escapeSingleQuotes) { StringBuilder r = new StringBuilder(); for (int j=0; j<t.length(); ++j) { char c = t.charAt(j); if (c==' ') r.append(' '); else if (c=='\n') r.append("\\n"); else if (c=='\r') r.append("\\r"); else if (c=='"' && escapeDoubleQuotes) r.append("\\\""); else if (c=='\'' && escapeSingleQuotes) r.append("\\\'"); else if (c=='\t') r.append("\\t"); else if (c=='\b') r.append("\\b"); else if (c=='\f') r.append("\\f"); else if (c=='\\') r.append("\\\\"); else if (Character.isISOControl(c) || Character.isWhitespace(c)) { r.append("\\u"); r.append(charToHex(c)); } else r.append(c); } return r.toString(); } public static String unescapeString(String t) { StringBuilder r = new StringBuilder(); int length = t.length(); int j=0; while (j<length) { char c = t.charAt(j); if (c=='\\') { c = t.charAt(++j); switch (c) { case 'n': r.append('\n'); break; case 't': r.append('\t'); break; case 'b': r.append('\b'); break; case 'r': r.append('\r'); break; case 'f': r.append('\f'); break; case '\\': r.append('\\'); break; case '\'': r.append('\''); break; case '"': r.append('"'); break; case 'x': r.append((char) Integer.parseInt(t.substring(j+1, j+3), 16)); j += 2; break; case 'u': r.append((char) Integer.parseInt(t.substring(j+1, j+5), 16)); j += 4; break; default: if (Character.isDigit(c)) { r.append((char) Integer.parseInt(t.substring(j, j+3), 8)); j += 2; } else throw new IllegalArgumentException("Bad escaped string: "+t); } } else r.append(c); j++; } return r.toString(); } public static String escapeCharacter(char c) { return escapeString(Character.toString(c), false, true); } public static String quoteString(String t) { return "\""+escapeString(t, true, false)+"\""; } public static String INTEGER_PATTERN_STRING = "(([-+]?\\d+)|(0x[\\da-f]+))l?"; public static Pattern INTEGER_PATTERN = Pattern.compile(INTEGER_PATTERN_STRING, Pattern.CASE_INSENSITIVE); public static boolean isJavaIntegerLiteral(String literal) { return INTEGER_PATTERN.matcher(literal).matches(); } public static long parseJavaIntegerLiteralType(String literal) { // remove type suffix as needed int length = literal.length(); char lastCharacter = literal.charAt(length -1); if (lastCharacter == 'l' || lastCharacter == 'L') { literal = literal.substring(0, length-1); length --; } if (length > 1 && literal.charAt(0) == '0') { if (literal.charAt(1) == 'x') return new BigInteger(literal.substring(2), 16).longValue();//Long.parseLong(literal.substring(2), 16); else return new BigInteger(literal.substring(1), 8).longValue();//Long.parseLong(literal.substring(1), 8); } else return new BigInteger(literal).longValue();//Long.parseLong(literal); } public static String collapse(String t) { for (int i=0, length=t.length(); i<length; i++) if (!Character.isWhitespace(t.charAt(i))) for (int j=length-1; j>=i; j--) if (!Character.isWhitespace(t.charAt(j))) return (i>0 ? " " : "") + t.substring(i, j+1) + (j<length-1 ? " " : ""); return ""; } public static String strip(String t) { for (int i=0, length=t.length(); i<length; i++) if (!Character.isWhitespace(t.charAt(i))) for (int j=length-1; j>=i; j--) if (!Character.isWhitespace(t.charAt(j))) return t.substring(i, j+1); return ""; } public static String stripLeading(String t) { for (int i=0, length=t.length(); i<length; i++) if (!Character.isWhitespace(t.charAt(i))) return t.substring(i, length); return ""; } public static String stripTrailing(String t) { for (int i=t.length()-1; i>=0; i--) if (!Character.isWhitespace(t.charAt(i))) return t.substring(0, i+1); return ""; } public static String stripFileExtension(String fileName) { return toPackageName(fileName); } public static String appendFileExtension(String fileName, String fileExtension) { return fileName+'.'+fileExtension; } public static String getFileExtension(String qname) { int s = qname.lastIndexOf('.'); return (s == -1) ? "" : qname.substring(s+1); } public static String getFileName(String qname) { int s = qname.lastIndexOf(File.separatorChar); return (s == -1) ? qname : qname.substring(s+1); } public static String getFilePath(String qname) { int s = qname.lastIndexOf(File.separatorChar); return (s == -1) ? "" : qname.substring(0, s); } public static String stripUpperPrefix(String t) { for (int i=0, length=t.length(); i<length; i++) if (!Character.isUpperCase(t.charAt(i))) return i>0 ? t.substring(i-1, length) : t; return ""; } public static String replaceExtension(String name, String newExtension) { int index = name.lastIndexOf('.'); if (index >= 0) name = name.substring(0, index); return name + "." + newExtension; } public static DateFormat basicISO8601DateTimeFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'"); public static DateFormat extendedISO8601DateTimeFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); static { basicISO8601DateTimeFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); extendedISO8601DateTimeFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); } public static String toBasicISO8601DateTime(Date value) { synchronized (basicISO8601DateTimeFormatter) { return basicISO8601DateTimeFormatter.format(value); } } public static Date fromBasicISO8601DateTime(String value) { synchronized (basicISO8601DateTimeFormatter) { try { return basicISO8601DateTimeFormatter.parse(value); } catch (ParseException e) { throw new IllegalArgumentException("ParseException: "+e.getMessage()); } } } public static String toExtendedISO8601DateTime(Date value) { synchronized (extendedISO8601DateTimeFormatter) { return extendedISO8601DateTimeFormatter.format(value); } } public static Date fromExtendedISO8601DateTime(String value) { synchronized (extendedISO8601DateTimeFormatter) { try { return extendedISO8601DateTimeFormatter.parse(value); } catch (ParseException e) { throw new IllegalArgumentException("ParseException: "+e.getMessage()); } } } private static String[] ORDINAL_SUFFIX = {"st","nd","rd","th","th","th","th","th","th","th"}; public static String toOrdinal(int value) { if (value <= 0) throw new IllegalArgumentException(); else if (value == 11) return "11th"; else return value+ORDINAL_SUFFIX[(value-1) % 10]; } public static boolean isValidEntityName(String name) { if (!isValidJavaIdentifier(name)) return false; char first = name.charAt(0); return (Character.isUpperCase(first) || first == '_'); } public static boolean isValidFeatureName(String name) { if (!isValidJavaIdentifier(name)) return false; char first = name.charAt(0); return (Character.isLowerCase(first) || first == '_'); } public static boolean isValidEnumLiteralName(String baseName) { return isValidJavaIdentifier(baseName); } public static boolean isValidJavaIdentifier(String name) { if (name.length() == 0 || isJavaKeyword(name)) return false; char[] chars = name.toCharArray(); if (!Character.isJavaIdentifierStart(chars[0])) return false; for (int i=1; i<chars.length; i++) if (!Character.isJavaIdentifierPart(chars[i])) return false; return true; } public static String toEntityName(String baseName) { String entityName; if (isJavaKeyword(toLowerCap(baseName))) entityName = baseName; else entityName = toFeatureName(baseName); int length = entityName.length(); if (!isUpperCap(entityName)) { if (length > 0 && Character.isLetter(entityName.charAt(0))) entityName = toUpperCap(entityName); else if (length == 0 || entityName.charAt(0) != '_') entityName = "_"+entityName; } return entityName; } public static String toFeatureName(String baseName) { if (baseName.length() == 0) return "_"; if (isJavaKeyword(toLowerCap(baseName))) return "_"+toLowerCap(baseName); int index = 0; StringBuilder builder = new StringBuilder(); char current = baseName.charAt(index++); if (!Character.isJavaIdentifierStart(current)) { builder.append('_'); if (Character.isJavaIdentifierPart(current)) builder.append(current); } else builder.append(current); while (index < baseName.length()) { current = baseName.charAt(index++); if (Character.isJavaIdentifierPart(current) && current != '$') builder.append(current); else builder.append('_'); } return toLowerCap(builder.toString()); } public static String toEnumLiteralName(String baseName) { if (baseName.length() == 0) return "_"; if (isJavaKeyword(toLowerCap(baseName))) return "_"+baseName; if (baseName.equals("instance")) return "_instance"; int index = 0; StringBuilder builder = new StringBuilder(); char current = baseName.charAt(index++); if (!Character.isJavaIdentifierStart(current)) { builder.append('_'); if (Character.isJavaIdentifierPart(current)) builder.append(current); } else builder.append(current); while (index < baseName.length()) { current = baseName.charAt(index++); if (Character.isJavaIdentifierPart(current)) builder.append(current); else builder.append('_'); } return builder.toString(); } public static String errorMessage(Throwable t) { String msg = t.getLocalizedMessage(); while (msg == null && t.getCause() != null) { t = t.getCause(); msg = t.getLocalizedMessage(); } if (msg == null) msg = t.toString(); return msg; } }