View Javadoc

1   /*
2    * Copyright 2005,2009 Ivan SZKIBA
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.ini4j.spi;
17  
18  import org.ini4j.Registry;
19  
20  import org.ini4j.Registry.Type;
21  
22  import java.io.UnsupportedEncodingException;
23  
24  import java.nio.charset.Charset;
25  
26  import java.util.Arrays;
27  
28  public class RegEscapeTool extends EscapeTool
29  {
30      private static final RegEscapeTool INSTANCE = ServiceFinder.findService(RegEscapeTool.class);
31      private static final Charset HEX_CHARSET = Charset.forName("UTF-16LE");
32      private static final int LOWER_DIGIT = 0x0f;
33      private static final int UPPER_DIGIT = 0xf0;
34      private static final int DIGIT_SIZE = 4;
35  
36      public static final RegEscapeTool getInstance()
37      {
38          return INSTANCE;
39      }
40  
41      public TypeValuesPair decode(String raw)
42      {
43          Type type = type(raw);
44          String value = (type == Type.REG_SZ) ? unquote(raw) : raw.substring(type.toString().length() + 1);
45          String[] values;
46  
47          switch (type)
48          {
49  
50              case REG_EXPAND_SZ:
51              case REG_MULTI_SZ:
52                  value = bytes2string(binary(value));
53                  break;
54  
55              case REG_DWORD:
56                  value = String.valueOf(Long.parseLong(value, HEX_RADIX));
57                  break;
58  
59              case REG_SZ:
60                  break;
61  
62              default:
63                  break;
64          }
65  
66          if (type == Type.REG_MULTI_SZ)
67          {
68              values = splitMulti(value);
69          }
70          else
71          {
72              values = new String[] { value };
73          }
74  
75          return new TypeValuesPair(type, values);
76      }
77  
78      public String encode(TypeValuesPair data)
79      {
80          String ret = null;
81  
82          if (data.getType() == Type.REG_SZ)
83          {
84              ret = quote(data.getValues()[0]);
85          }
86          else if (data.getValues()[0] != null)
87          {
88              ret = encode(data.getType(), data.getValues());
89          }
90  
91          return ret;
92      }
93  
94      byte[] binary(String value)
95      {
96          byte[] bytes = new byte[value.length()];
97          int idx = 0;
98          int shift = DIGIT_SIZE;
99  
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 }