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 }