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
018public class EscapeTool
019{
020    private static final String ESCAPE_LETTERS = "\\tnfbr";
021    private static final String ESCAPEABLE_CHARS = "\\\t\n\f\b\r";
022    private static final char ESCAPE_CHAR = '\\';
023    static final char[] HEX = "0123456789abcdef".toCharArray();
024    private static final EscapeTool INSTANCE = ServiceFinder.findService(EscapeTool.class);
025    private static final char ASCII_MIN = 0x20;
026    private static final char ASCII_MAX = 0x7e;
027    static final int HEX_DIGIT_MASK = 0x0f;
028    static final int HEX_DIGIT_3_OFFSET = 4;
029    static final int HEX_DIGIT_2_OFFSET = 8;
030    static final int HEX_DIGIT_1_OFFSET = 12;
031    static final int HEX_RADIX = 16;
032    private static final int UNICODE_HEX_DIGITS = 4;
033    static final char DOUBLE_QUOTE = '"';
034
035    public static EscapeTool getInstance()
036    {
037        return INSTANCE;
038    }
039
040    public String escape(String line)
041    {
042        int len = line.length();
043        StringBuilder buffer = new StringBuilder(len * 2);
044
045        for (int i = 0; i < len; i++)
046        {
047            char c = line.charAt(i);
048            int idx = ESCAPEABLE_CHARS.indexOf(c);
049
050            if (idx >= 0)
051            {
052                buffer.append(ESCAPE_CHAR);
053                buffer.append(ESCAPE_LETTERS.charAt(idx));
054            }
055            else
056            {
057                if ((c < ASCII_MIN) || (c > ASCII_MAX))
058                {
059                    escapeBinary(buffer, c);
060                }
061                else
062                {
063                    buffer.append(c);
064                }
065            }
066        }
067
068        return buffer.toString();
069    }
070
071    public String quote(String value)
072    {
073        String ret = value;
074
075        if ((value != null) && (value.length() != 0))
076        {
077            StringBuilder buff = new StringBuilder();
078
079            buff.append(DOUBLE_QUOTE);
080            for (int i = 0; i < value.length(); i++)
081            {
082                char c = value.charAt(i);
083
084                if ((c == ESCAPE_CHAR) || (c == DOUBLE_QUOTE))
085                {
086                    buff.append(ESCAPE_CHAR);
087                }
088
089                buff.append(c);
090            }
091
092            buff.append(DOUBLE_QUOTE);
093            ret = buff.toString();
094        }
095
096        return ret;
097    }
098
099    public String unescape(String line)
100    {
101        int n = line.length();
102        StringBuilder buffer = new StringBuilder(n);
103        int i = 0;
104
105        while (i < n)
106        {
107            char c = line.charAt(i++);
108
109            if (c == ESCAPE_CHAR)
110            {
111                c = line.charAt(i++);
112                int next = unescapeBinary(buffer, c, line, i);
113
114                if (next == i)
115                {
116                    int idx = ESCAPE_LETTERS.indexOf(c);
117
118                    if (idx >= 0)
119                    {
120                        c = ESCAPEABLE_CHARS.charAt(idx);
121                    }
122
123                    buffer.append(c);
124                }
125                else
126                {
127                    i = next;
128                }
129            }
130            else
131            {
132                buffer.append(c);
133            }
134        }
135
136        return buffer.toString();
137    }
138
139    public String unquote(String value)
140    {
141        StringBuilder buff = new StringBuilder();
142        boolean escape = false;
143
144        for (int i = 1; i < (value.length() - 1); i++)
145        {
146            char c = value.charAt(i);
147
148            if (c == ESCAPE_CHAR)
149            {
150                if (!escape)
151                {
152                    escape = true;
153
154                    continue;
155                }
156
157                escape = false;
158            }
159
160            buff.append(c);
161        }
162
163        return buff.toString();
164    }
165
166    void escapeBinary(StringBuilder buff, char c)
167    {
168        buff.append("\\u");
169        buff.append(HEX[(c >>> HEX_DIGIT_1_OFFSET) & HEX_DIGIT_MASK]);
170        buff.append(HEX[(c >>> HEX_DIGIT_2_OFFSET) & HEX_DIGIT_MASK]);
171        buff.append(HEX[(c >>> HEX_DIGIT_3_OFFSET) & HEX_DIGIT_MASK]);
172        buff.append(HEX[c & HEX_DIGIT_MASK]);
173    }
174
175    int unescapeBinary(StringBuilder buff, char escapeType, String line, int index)
176    {
177        int ret = index;
178
179        if (escapeType == 'u')
180        {
181            try
182            {
183                buff.append((char) Integer.parseInt(line.substring(index, index + UNICODE_HEX_DIGITS), HEX_RADIX));
184                ret = index + UNICODE_HEX_DIGITS;
185            }
186            catch (Exception x)
187            {
188                throw new IllegalArgumentException("Malformed \\uxxxx encoding.", x);
189            }
190        }
191
192        return ret;
193    }
194}