001 /* 002 * Copyright 2005,2009 Ivan SZKIBA 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.ini4j.spi; 017 018 public class EscapeTool 019 { 020 private static final String ESCAPE_LETTERS = "\\tnfbr"; 021 private static final String ESCAPEABLE_CHARS = "\\\t\n\f\b\r"; 022 private static final char ESCAPE_CHAR = '\\'; 023 static final char[] HEX = "0123456789abcdef".toCharArray(); 024 private static final EscapeTool INSTANCE = ServiceFinder.findService(EscapeTool.class); 025 private static final char ASCII_MIN = 0x20; 026 private static final char ASCII_MAX = 0x7e; 027 static final int HEX_DIGIT_MASK = 0x0f; 028 static final int HEX_DIGIT_3_OFFSET = 4; 029 static final int HEX_DIGIT_2_OFFSET = 8; 030 static final int HEX_DIGIT_1_OFFSET = 12; 031 static final int HEX_RADIX = 16; 032 private static final int UNICODE_HEX_DIGITS = 4; 033 static final char DOUBLE_QUOTE = '"'; 034 035 public static EscapeTool getInstance() 036 { 037 return INSTANCE; 038 } 039 040 public String escape(String line) 041 { 042 int len = line.length(); 043 StringBuilder buffer = new StringBuilder(len * 2); 044 045 for (int i = 0; i < len; i++) 046 { 047 char c = line.charAt(i); 048 int idx = ESCAPEABLE_CHARS.indexOf(c); 049 050 if (idx >= 0) 051 { 052 buffer.append(ESCAPE_CHAR); 053 buffer.append(ESCAPE_LETTERS.charAt(idx)); 054 } 055 else 056 { 057 if ((c < ASCII_MIN) || (c > ASCII_MAX)) 058 { 059 escapeBinary(buffer, c); 060 } 061 else 062 { 063 buffer.append(c); 064 } 065 } 066 } 067 068 return buffer.toString(); 069 } 070 071 public String quote(String value) 072 { 073 String ret = value; 074 075 if ((value != null) && (value.length() != 0)) 076 { 077 StringBuilder buff = new StringBuilder(); 078 079 buff.append(DOUBLE_QUOTE); 080 for (int i = 0; i < value.length(); i++) 081 { 082 char c = value.charAt(i); 083 084 if ((c == ESCAPE_CHAR) || (c == DOUBLE_QUOTE)) 085 { 086 buff.append(ESCAPE_CHAR); 087 } 088 089 buff.append(c); 090 } 091 092 buff.append(DOUBLE_QUOTE); 093 ret = buff.toString(); 094 } 095 096 return ret; 097 } 098 099 public String unescape(String line) 100 { 101 int n = line.length(); 102 StringBuilder buffer = new StringBuilder(n); 103 int i = 0; 104 105 while (i < n) 106 { 107 char c = line.charAt(i++); 108 109 if (c == ESCAPE_CHAR) 110 { 111 c = line.charAt(i++); 112 int next = unescapeBinary(buffer, c, line, i); 113 114 if (next == i) 115 { 116 int idx = ESCAPE_LETTERS.indexOf(c); 117 118 if (idx >= 0) 119 { 120 c = ESCAPEABLE_CHARS.charAt(idx); 121 } 122 123 buffer.append(c); 124 } 125 else 126 { 127 i = next; 128 } 129 } 130 else 131 { 132 buffer.append(c); 133 } 134 } 135 136 return buffer.toString(); 137 } 138 139 public String unquote(String value) 140 { 141 StringBuilder buff = new StringBuilder(); 142 boolean escape = false; 143 144 for (int i = 1; i < (value.length() - 1); i++) 145 { 146 char c = value.charAt(i); 147 148 if (c == ESCAPE_CHAR) 149 { 150 if (!escape) 151 { 152 escape = true; 153 154 continue; 155 } 156 157 escape = false; 158 } 159 160 buff.append(c); 161 } 162 163 return buff.toString(); 164 } 165 166 void escapeBinary(StringBuilder buff, char c) 167 { 168 buff.append("\\u"); 169 buff.append(HEX[(c >>> HEX_DIGIT_1_OFFSET) & HEX_DIGIT_MASK]); 170 buff.append(HEX[(c >>> HEX_DIGIT_2_OFFSET) & HEX_DIGIT_MASK]); 171 buff.append(HEX[(c >>> HEX_DIGIT_3_OFFSET) & HEX_DIGIT_MASK]); 172 buff.append(HEX[c & HEX_DIGIT_MASK]); 173 } 174 175 int unescapeBinary(StringBuilder buff, char escapeType, String line, int index) 176 { 177 int ret = index; 178 179 if (escapeType == 'u') 180 { 181 try 182 { 183 buff.append((char) Integer.parseInt(line.substring(index, index + UNICODE_HEX_DIGITS), HEX_RADIX)); 184 ret = index + UNICODE_HEX_DIGITS; 185 } 186 catch (Exception x) 187 { 188 throw new IllegalArgumentException("Malformed \\uxxxx encoding.", x); 189 } 190 } 191 192 return ret; 193 } 194 }