/* * Vitry, copyright (C) Hans Hoglund 2011 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * See COPYING.txt for details. */ package vitry.runtime.util; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Locale; import vitry.runtime.error.ParseError; public class Strings { private Strings() { } public static String join(Object[] elements) { return join (elements, "", ", ", ""); } public static String join(Iterable<?> elements) { return join (elements, "", ", ", ""); } public static String join(Object[] elements, String start, String delim, String end) { return join(java.util.Arrays.asList(elements), start, delim, end); } public static String join(Iterable<?> elements, String start, String delim, String end) { StringBuilder strb = new StringBuilder(); boolean first = true; strb.append(start); for (Object e : elements) { if (first) first = false; else strb.append(delim); strb.append(e); } strb.append(end); return strb.toString(); } public static String limit(String s, int maxLength) { if (s.length() > maxLength) return s.substring(0, maxLength) + "..."; else return s; } public static String unescape(String str) { if (str == null) { return null; } StringWriter writer = null; try { writer = new StringWriter(str.length()); unescape(writer, str); } catch (IOException ioe) { // this should never ever happen while writing to a StringWriter assert false; } return writer.toString(); } public static void unescape(Writer out, String str) throws IOException { if (out == null) { throw new IllegalArgumentException("The Writer must not be null"); } if (str == null) { return; } int sz = str.length(); StringBuilder unicode = new StringBuilder(4); boolean hadSlash = false; boolean inUnicode = false; for (int i = 0; i < sz; i++) { char ch = str.charAt(i); if (inUnicode) { // if in unicode, then we're reading unicode // values in somehow unicode.append(ch); if (unicode.length() == 4) { // unicode now contains the four hex digits // which represents our unicode character try { int value = Integer.parseInt(unicode.toString(), 16); out.write((char) value); unicode.setLength(0); inUnicode = false; hadSlash = false; } catch (NumberFormatException nfe) { throw new ParseError("Unable to parse unicode value: " + unicode); } } continue; } if (hadSlash) { // handle an escaped value hadSlash = false; switch (ch) { case '\\': out.write('\\'); break; case '\'': out.write('\''); break; case '\"': out.write('"'); break; case 'r': out.write('\r'); break; case 'f': out.write('\f'); break; case 't': out.write('\t'); break; case 'n': out.write('\n'); break; case 'b': out.write('\b'); break; case 'u': { // uh-oh, we're in unicode country.... inUnicode = true; break; } default: out.write(ch); break; } continue; } else if (ch == '\\') { hadSlash = true; continue; } out.write(ch); } if (hadSlash) { // then we're in the weird case of a \ at the end of the // string, let's output it anyway. out.write('\\'); } } public static String escape(String str) { return escapeJavaStyleString(str, false, false); } public static void escape(Writer out, String str) throws IOException { escapeJavaStyleString(out, str, false, false); } /** * <p>Worker method for the {@link #escapeJavaScript(String)} method.</p> * * @param str String to escape values in, may be null * @param escapeSingleQuotes escapes single quotes if <code>true</code> * @param escapeForwardSlash TODO * @return the escaped string */ private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes, boolean escapeForwardSlash) { if (str == null) { return null; } try { StringWriter writer = new StringWriter(str.length() * 2); escapeJavaStyleString(writer, str, escapeSingleQuotes, escapeForwardSlash); return writer.toString(); } catch (IOException ioe) { // this should never ever happen while writing to a StringWriter return Utils.assertFalse(); } } /** * <p>Worker method for the {@link #escapeJavaScript(String)} method.</p> * * @param out write to receieve the escaped string * @param str String to escape values in, may be null * @param escapeSingleQuote escapes single quotes if <code>true</code> * @param escapeForwardSlash TODO * @throws IOException if an IOException occurs */ private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote, boolean escapeForwardSlash) throws IOException { if (out == null) { throw new IllegalArgumentException("The Writer must not be null"); } if (str == null) { return; } int sz; sz = str.length(); for (int i = 0; i < sz; i++) { char ch = str.charAt(i); // handle unicode if (ch > 0xfff) { out.write("\\u" + hex(ch)); } else if (ch > 0xff) { out.write("\\u0" + hex(ch)); } else if (ch > 0x7f) { out.write("\\u00" + hex(ch)); } else if (ch < 32) { switch (ch) { case '\b' : out.write('\\'); out.write('b'); break; case '\n' : out.write('\\'); out.write('n'); break; case '\t' : out.write('\\'); out.write('t'); break; case '\f' : out.write('\\'); out.write('f'); break; case '\r' : out.write('\\'); out.write('r'); break; default : if (ch > 0xf) { out.write("\\u00" + hex(ch)); } else { out.write("\\u000" + hex(ch)); } break; } } else { switch (ch) { case '\'' : if (escapeSingleQuote) { out.write('\\'); } out.write('\''); break; case '"' : out.write('\\'); out.write('"'); break; case '\\' : out.write('\\'); out.write('\\'); break; case '/' : if (escapeForwardSlash) { out.write('\\'); } out.write('/'); break; default : out.write(ch); break; } } } } private static String hex(char ch) { return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH); } }