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 import org.ini4j.Registry;
019
020 import org.ini4j.Registry.Type;
021
022 import java.io.UnsupportedEncodingException;
023
024 import java.nio.charset.Charset;
025
026 import java.util.Arrays;
027
028 public class RegEscapeTool extends EscapeTool
029 {
030 private static final RegEscapeTool INSTANCE = ServiceFinder.findService(RegEscapeTool.class);
031 private static final Charset HEX_CHARSET = Charset.forName("UTF-16LE");
032 private static final int LOWER_DIGIT = 0x0f;
033 private static final int UPPER_DIGIT = 0xf0;
034 private static final int DIGIT_SIZE = 4;
035
036 public static final RegEscapeTool getInstance()
037 {
038 return INSTANCE;
039 }
040
041 public TypeValuesPair decode(String raw)
042 {
043 Type type = type(raw);
044 String value = (type == Type.REG_SZ) ? unquote(raw) : raw.substring(type.toString().length() + 1);
045 String[] values;
046
047 switch (type)
048 {
049
050 case REG_EXPAND_SZ:
051 case REG_MULTI_SZ:
052 value = bytes2string(binary(value));
053 break;
054
055 case REG_DWORD:
056 value = String.valueOf(Long.parseLong(value, HEX_RADIX));
057 break;
058
059 case REG_SZ:
060 break;
061
062 default:
063 break;
064 }
065
066 if (type == Type.REG_MULTI_SZ)
067 {
068 values = splitMulti(value);
069 }
070 else
071 {
072 values = new String[] { value };
073 }
074
075 return new TypeValuesPair(type, values);
076 }
077
078 public String encode(TypeValuesPair data)
079 {
080 String ret = null;
081
082 if (data.getType() == Type.REG_SZ)
083 {
084 ret = quote(data.getValues()[0]);
085 }
086 else if (data.getValues()[0] != null)
087 {
088 ret = encode(data.getType(), data.getValues());
089 }
090
091 return ret;
092 }
093
094 byte[] binary(String value)
095 {
096 byte[] bytes = new byte[value.length()];
097 int idx = 0;
098 int shift = DIGIT_SIZE;
099
100 for (int i = 0; i < value.length(); i++)
101 {
102 char c = value.charAt(i);
103
104 if (Character.isWhitespace(c))
105 {
106 continue;
107 }
108
109 if (c == ',')
110 {
111 idx++;
112 shift = DIGIT_SIZE;
113 }
114 else
115 {
116 int digit = Character.digit(c, HEX_RADIX);
117
118 if (digit >= 0)
119 {
120 bytes[idx] |= digit << shift;
121 shift = 0;
122 }
123 }
124 }
125
126 return Arrays.copyOfRange(bytes, 0, idx + 1);
127 }
128
129 String encode(Type type, String[] values)
130 {
131 StringBuilder buff = new StringBuilder();
132
133 buff.append(type.toString());
134 buff.append(Type.SEPARATOR_CHAR);
135 switch (type)
136 {
137
138 case REG_EXPAND_SZ:
139 buff.append(hexadecimal(values[0]));
140 break;
141
142 case REG_DWORD:
143 buff.append(String.format("%08x", Long.parseLong(values[0])));
144 break;
145
146 case REG_MULTI_SZ:
147 int n = values.length;
148
149 for (int i = 0; i < n; i++)
150 {
151 buff.append(hexadecimal(values[i]));
152 buff.append(',');
153 }
154
155 buff.append("00,00");
156 break;
157
158 default:
159 buff.append(values[0]);
160 break;
161 }
162
163 return buff.toString();
164 }
165
166 String hexadecimal(String value)
167 {
168 StringBuilder buff = new StringBuilder();
169
170 if ((value != null) && (value.length() != 0))
171 {
172 byte[] bytes = string2bytes(value);
173
174 for (int i = 0; i < bytes.length; i++)
175 {
176 buff.append(Character.forDigit((bytes[i] & UPPER_DIGIT) >> DIGIT_SIZE, HEX_RADIX));
177 buff.append(Character.forDigit(bytes[i] & LOWER_DIGIT, HEX_RADIX));
178 buff.append(',');
179 }
180
181 buff.append("00,00");
182 }
183
184 return buff.toString();
185 }
186
187 Registry.Type type(String raw)
188 {
189 Registry.Type type;
190
191 if (raw.charAt(0) == DOUBLE_QUOTE)
192 {
193 type = Registry.Type.REG_SZ;
194 }
195 else
196 {
197 int idx = raw.indexOf(Registry.TYPE_SEPARATOR);
198
199 type = (idx < 0) ? Registry.Type.REG_SZ : Registry.Type.fromString(raw.substring(0, idx));
200 }
201
202 return type;
203 }
204
205 // XXX Java 1.4 compatibility hack
206 private String bytes2string(byte[] bytes)
207 {
208 String str;
209
210 try
211 {
212 str = new String(bytes, 0, bytes.length - 2, HEX_CHARSET);
213 }
214 catch (NoSuchMethodError x)
215 {
216 try
217 {
218 str = new String(bytes, 0, bytes.length, HEX_CHARSET.name());
219 }
220 catch (UnsupportedEncodingException ex)
221 {
222 throw new IllegalStateException(ex);
223 }
224 }
225
226 return str;
227 }
228
229 private String[] splitMulti(String value)
230 {
231 int len = value.length();
232 int start;
233 int end;
234 int n = 0;
235
236 start = 0;
237 for (end = value.indexOf(0, start); end >= 0; end = value.indexOf(0, start))
238 {
239 n++;
240 start = end + 1;
241 if (start >= len)
242 {
243 break;
244 }
245 }
246
247 String[] values = new String[n];
248
249 start = 0;
250 for (int i = 0; i < n; i++)
251 {
252 end = value.indexOf(0, start);
253 values[i] = value.substring(start, end);
254 start = end + 1;
255 }
256
257 return values;
258 }
259
260 // XXX Java 1.4 compatibility hack
261 private byte[] string2bytes(String value)
262 {
263 byte[] bytes;
264
265 try
266 {
267 bytes = value.getBytes(HEX_CHARSET);
268 }
269 catch (NoSuchMethodError x)
270 {
271 try
272 {
273 bytes = value.getBytes(HEX_CHARSET.name());
274 }
275 catch (UnsupportedEncodingException ex)
276 {
277 throw new IllegalStateException(ex);
278 }
279 }
280
281 return bytes;
282 }
283 }