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 */
016package org.ini4j.spi;
017
018import org.ini4j.Registry;
019
020import org.ini4j.Registry.Type;
021
022import java.nio.charset.Charset;
023
024import java.util.Arrays;
025
026public 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}