/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.util; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; import java.util.Map; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNEncodingUtil { public static String uriEncode(String src) { StringBuffer sb = null; byte[] bytes; try { bytes = src.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { bytes = src.getBytes(); } for (int i = 0; i < bytes.length; i++) { int index = bytes[i] & 0xFF; if (uri_char_validity[index] > 0) { if (sb != null) { sb.append((char) bytes[i]); } continue; } if (sb == null) { sb = new StringBuffer(); try { sb.append(new String(bytes, 0, i, "UTF-8")); } catch (UnsupportedEncodingException e) { sb.append(new String(bytes, 0, i)); } } sb.append("%"); sb.append(Character.toUpperCase(Character.forDigit((index & 0xF0) >> 4, 16))); sb.append(Character.toUpperCase(Character.forDigit(index & 0x0F, 16))); } return sb == null ? src : sb.toString(); } public static String autoURIEncode(String src) { StringBuffer sb = null; byte[] bytes; try { bytes = src.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { bytes = src.getBytes(); } for (int i = 0; i < bytes.length; i++) { int index = bytes[i] & 0xFF; if (uri_char_validity[index] > 0) { if (sb != null) { sb.append((char) bytes[i]); } continue; } else if (index == '%' && i + 2 < bytes.length && isHexDigit((char) bytes[i + 1]) && isHexDigit((char) bytes[i + 2])) { if (sb != null) { sb.append((char) bytes[i]); } // digits will be processed fine. continue; } if (sb == null) { sb = new StringBuffer(); try { sb.append(new String(bytes, 0, i, "UTF-8")); } catch (UnsupportedEncodingException e) { sb.append(new String(bytes, 0, i)); } } sb.append("%"); sb.append(Character.toUpperCase(Character.forDigit((index & 0xF0) >> 4, 16))); sb.append(Character.toUpperCase(Character.forDigit(index & 0x0F, 16))); } return sb == null ? src : sb.toString(); } public static void assertURISafe(String path) throws SVNException { path = path == null ? "" : path; byte[] bytes; try { bytes = path.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_URL, "path ''{0}'' could not be encoded as UTF-8", path); SVNErrorManager.error(err, SVNLogType.DEFAULT); return; } if (bytes == null || bytes.length != path.length()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_URL, "path ''{0}'' doesn not look like URI-encoded path", path); SVNErrorManager.error(err, SVNLogType.DEFAULT); } for (int i = 0; i < bytes.length; i++) { if (uri_char_validity[bytes[i]] <= 0 && bytes[i] != '%') { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_URL, "path ''{0}'' doesn not look like URI-encoded path; character ''{1}'' is URI unsafe", new Object[] {path, ((char) bytes[i]) + ""}); SVNErrorManager.error(err, SVNLogType.DEFAULT); } } return; } public static String uriDecode(String src) { // this is string in ASCII-US encoding. boolean query = false; boolean decoded = false; int length = src.length(); ByteArrayOutputStream bos = new ByteArrayOutputStream(length); for(int i = 0; i < length; i++) { byte ch = (byte) src.charAt(i); if (ch == '?') { query = true; } else if (ch == '+' && query) { ch = ' '; } else if (ch == '%' && i + 2 < length && isHexDigit(src.charAt(i + 1)) && isHexDigit(src.charAt(i + 2))) { ch = (byte) (hexValue(src.charAt(i + 1))*0x10 + hexValue(src.charAt(i + 2))); decoded = true; i += 2; } else { // if character is not URI-safe try to encode it. } bos.write(ch); } if (!decoded) { return src; } try { return new String(bos.toByteArray(), "UTF-8"); } catch (UnsupportedEncodingException e) { } return src; } public static String xmlEncodeCDATA(String src) { return xmlEncodeCDATA(src, false); } public static String xmlEncodeCDATA(String src, boolean escapeQuotes) { StringBuffer sb = null; for(int i = 0; i < src.length(); i++) { char ch = src.charAt(i); switch (ch) { case '&': if (sb == null) { sb = createStringBuffer(src, i); } sb.append("&"); break; case '<': if (sb == null) { sb = createStringBuffer(src, i); } sb.append("<"); break; case '>': if (sb == null) { sb = createStringBuffer(src, i); } sb.append(">"); break; case '\r': if (sb == null) { sb = createStringBuffer(src, i); } sb.append(" "); break; case '\"': if (escapeQuotes) { if (sb == null) { sb = createStringBuffer(src, i); } sb.append("""); break; } default: if (sb != null) { sb.append(ch); } } } return sb != null ? sb.toString() : src; } public static String xmlEncodeAttr(String src) { StringBuffer sb = new StringBuffer(src.length()); for(int i = 0; i < src.length(); i++) { char ch = src.charAt(i); switch (ch) { case '&': if (sb == null) { sb = createStringBuffer(src, i); } sb.append("&"); break; case '<': if (sb == null) { sb = createStringBuffer(src, i); } sb.append("<"); break; case '>': if (sb == null) { sb = createStringBuffer(src, i); } sb.append(">"); break; case '\'': if (sb == null) { sb = createStringBuffer(src, i); } sb.append("'"); break; case '\"': if (sb == null) { sb = createStringBuffer(src, i); } sb.append("""); break; case '\r': if (sb == null) { sb = createStringBuffer(src, i); } sb.append(" "); break; case '\n': if (sb == null) { sb = createStringBuffer(src, i); } sb.append(" "); break; case '\t': if (sb == null) { sb = createStringBuffer(src, i); } sb.append(" "); break; default: if (sb != null) { sb.append(ch); } } } return sb != null ? sb.toString() : src; } public static boolean isXMLSafe(String value) { for (int i = 0; i < value.length(); i++) { char ch = value.charAt(i); if (ch < 0x20 && ch != 0x0A && ch != 0x0D && ch != 0x09 && ch != 0x08) { return false; } } return true; } private static final Map XML_UNESCAPE_MAP = new SVNHashMap(); static { XML_UNESCAPE_MAP.put("&", "&"); XML_UNESCAPE_MAP.put("<", "<"); XML_UNESCAPE_MAP.put(">", ">"); XML_UNESCAPE_MAP.put(""", "\""); XML_UNESCAPE_MAP.put("'", "'"); XML_UNESCAPE_MAP.put(" ", "\r"); XML_UNESCAPE_MAP.put(" ", "\n"); XML_UNESCAPE_MAP.put(" ", "\t"); } public static String xmlDecode(String value) { StringBuffer result = new StringBuffer(value.length()); int l = value.length(); for (int i = 0; i < l; i++) { char ch = value.charAt(i); if (ch == '&') { String replacement = null; for (int j = i + 1; j < i + 6 && j < l; j++) { if (value.charAt(j) == ';' && j - i > 1) { String escape = value.substring(i, j + 1); // full replacement = (String) XML_UNESCAPE_MAP.get(escape); if (replacement != null) { result.append(replacement); i = j; } break; } } if (replacement != null) { continue; } } result.append(ch); } return result.toString(); } public static String fuzzyEscape(String str) { char[] chars = str.toCharArray(); StringBuffer result = createStringBuffer(str, 0); for (int i = 0; i < chars.length; i++) { if (!isASCIIControlChar(chars[i]) || chars[i] == '\r' || chars[i] == '\n' || chars[i] == '\t') { result.append(chars[i]); } else { result.append("?\\"); int code = chars[i] & 0xFF; if (code < 100) { result.append('0'); } result.append(code); } } return result.toString(); } public static boolean isHexDigit(char ch) { return Character.isDigit(ch) || (Character.toUpperCase(ch) >= 'A' && Character.toUpperCase(ch) <= 'F'); } public static boolean isASCIIControlChar(char ch) { return (ch >= 0x00 && ch <= 0x1f) || ch == 0x7f; } private static int hexValue(char ch) { if (Character.isDigit(ch)) { return ch - '0'; } ch = Character.toUpperCase(ch); return (ch - 'A') + 0x0A; } private static StringBuffer createStringBuffer(String src, int length) { StringBuffer sb = new StringBuffer(src.length()); sb.append(src.toCharArray(), 0, length); return sb; } public static byte[] getBytes(final char[] data, String charset) { if (data == null) { return new byte[0]; } final CharBuffer cb = CharBuffer.wrap(data); Charset chrst; try { chrst = Charset.forName(charset); } catch (UnsupportedCharsetException e) { chrst = Charset.defaultCharset(); } try { ByteBuffer bb = chrst.newEncoder().encode(cb); final byte[] bytes = new byte[bb.limit()]; bb.get(bytes); if (bb.hasArray()) { clearArray(bb.array()); } return bytes; } catch (CharacterCodingException e) { } final byte[] bytes = new byte[data.length]; for (int i = 0; i < data.length; i++) { bytes[i] = (byte) (data[i] & 0xFF); } return bytes; } public static char[] copyOf(char[] source) { final char[] copy = source != null ? new char[source.length] : null; if (copy != null) { System.arraycopy(source, 0, copy, 0, source.length); } return copy; } public static char[] getChars(byte[] data, String charset) { return getChars(data, 0, data != null ? data.length : 0, charset); } public static char[] getChars(byte[] data, int offset, int length, String charset) { if (data == null) { return new char[0]; } Charset chrst; try { chrst = Charset.forName(charset); } catch (UnsupportedCharsetException e) { chrst = Charset.defaultCharset(); } try { CharBuffer cb = chrst.newDecoder().decode(ByteBuffer.wrap(data, offset, length)); final char[] chars = new char[cb.limit()]; cb.get(chars); return chars; } catch (CharacterCodingException e) { } final char[] chars = new char[data.length]; for (int i = 0; i < chars.length; i++) { chars[i] = (char) data[i]; } return chars; } public static void clearArray(byte[] array) { if (array == null) { return; } for (int i = 0; i < array.length; i++) { array[i] = 0; } Arrays.fill(array, (byte) 0xFF); } public static void clearArray(char[] array) { if (array == null) { return; } Arrays.fill(array, '\0'); } private static final byte[] uri_char_validity = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, /* 64 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, /* 128 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; }