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.nio.charset.Charset;
023
024 import java.util.Arrays;
025
026 public class RegEscapeTool extends EscapeTool
027 {
028 private static final RegEscapeTool INSTANCE = ServiceFinder.findService(RegEscapeTool.class);
029 private static final Charset HEX_CHARSET = Charset.forName("UTF-16LE");
030 private static final int LOWER_DIGIT = 0x0f;
031 private static final int UPPER_DIGIT = 0xf0;
032 private static final int DIGIT_SIZE = 4;
033
034 public static final RegEscapeTool getInstance()
035 {
036 return INSTANCE;
037 }
038
039 public TypeValuesPair decode(String raw)
040 {
041 Type type = type(raw);
042 String value = (type == Type.REG_SZ) ? unquote(raw) : raw.substring(type.toString().length() + 1);
043 String[] values;
044
045 switch (type)
046 {
047
048 case REG_EXPAND_SZ:
049 case REG_MULTI_SZ:
050 byte[] bytes = binary(value);
051
052 value = new String(bytes, 0, bytes.length - 2, HEX_CHARSET);
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 = value.getBytes(HEX_CHARSET);
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 private String[] splitMulti(String value)
206 {
207 int len = value.length();
208 int start;
209 int end;
210 int n = 0;
211
212 start = 0;
213 for (end = value.indexOf(0, start); end >= 0; end = value.indexOf(0, start))
214 {
215 n++;
216 start = end + 1;
217 if (start >= len)
218 {
219 break;
220 }
221 }
222
223 String[] values = new String[n];
224
225 start = 0;
226 for (int i = 0; i < n; i++)
227 {
228 end = value.indexOf(0, start);
229 values[i] = value.substring(start, end);
230 start = end + 1;
231 }
232
233 return values;
234 }
235 }