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    public 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    }