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 }