001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    
022    import java.io.IOException;
023    
024    import java.util.HashMap;
025    import java.util.Map;
026    import java.util.Set;
027    import java.util.TreeSet;
028    
029    /**
030     * <p>
031     * This is a rewrite of java.util.Properties that is not synchronized and
032     * natively supports non-ASCII encodings. It can also be configured to be
033     * "safe", allowing the values to have new line characters. When stored to a
034     * given BufferedWriter, "safe" properties will replace all new line characters
035     * with a _SAFE_NEWLINE_CHARACTER_.
036     * </p>
037     *
038     * <p>
039     * In its current form, this is not intended to replace java.util.Properties for
040     * reading properties flat files. This class is not thread-safe.
041     * </p>
042     *
043     * @author Alexander Chow
044     */
045    public class UnicodeProperties extends HashMap<String, String> {
046    
047            public UnicodeProperties() {
048                    super();
049            }
050    
051            public UnicodeProperties(boolean safe) {
052                    super();
053    
054                    _safe = safe;
055            }
056    
057            public void fastLoad(String props) {
058                    if (Validator.isNull(props)) {
059                            return;
060                    }
061    
062                    int x = props.indexOf(CharPool.NEW_LINE);
063                    int y = 0;
064    
065                    while (x != -1) {
066                            put(props.substring(y, x));
067    
068                            y = x;
069    
070                            x = props.indexOf(CharPool.NEW_LINE, y + 1);
071                    }
072    
073                    put(props.substring(y));
074            }
075    
076            public String getProperty(String key) {
077                    return get(key);
078            }
079    
080            public String getProperty(String key, String defaultValue) {
081                    String value = getProperty(key);
082    
083                    if (value == null) {
084                            return defaultValue;
085                    }
086                    else {
087                            return value;
088                    }
089            }
090    
091            public boolean isSafe() {
092                    return _safe;
093            }
094    
095            public void load(String props) throws IOException {
096                    if (Validator.isNull(props)) {
097                            return;
098                    }
099    
100                    UnsyncBufferedReader unsyncBufferedReader = null;
101    
102                    try {
103                            unsyncBufferedReader = new UnsyncBufferedReader(
104                                    new UnsyncStringReader(props));
105    
106                            String line = unsyncBufferedReader.readLine();
107    
108                            while (line != null) {
109                                    put(line);
110                                    line = unsyncBufferedReader.readLine();
111                            }
112                    }
113                    finally {
114                            if (unsyncBufferedReader != null) {
115                                    try {
116                                            unsyncBufferedReader.close();
117                                    }
118                                    catch (Exception e) {
119                                    }
120                            }
121                    }
122            }
123    
124            public void put(String line) {
125                    line = line.trim();
126    
127                    if (!_isComment(line)) {
128                            int pos = line.indexOf(CharPool.EQUAL);
129    
130                            if (pos != -1) {
131                                    String key = line.substring(0, pos).trim();
132                                    String value = line.substring(pos + 1).trim();
133    
134                                    if (_safe) {
135                                            value = _decode(value);
136                                    }
137    
138                                    setProperty(key, value);
139                            }
140                            else {
141                                    _log.error("Invalid property on line " + line);
142                            }
143                    }
144            }
145    
146            @Override
147            public String put(String key, String value) {
148                    if (key == null) {
149                            return null;
150                    }
151                    else {
152                            if (value == null) {
153                                    return remove(key);
154                            }
155                            else {
156                                    _length += key.length() + value.length() + 2;
157    
158                                    return super.put(key, value);
159                            }
160                    }
161            }
162    
163            @Override
164            public String remove(Object key) {
165                    if ((key == null) || !containsKey(key)) {
166                            return null;
167                    }
168                    else {
169                            String keyString = (String)key;
170    
171                            String value = super.remove(key);
172    
173                            _length -= keyString.length() + value.length() + 2;
174    
175                            return value;
176                    }
177            }
178    
179            public String setProperty(String key, String value) {
180                    return put(key, value);
181            }
182    
183            public String toSortedString() {
184                    StringBuilder sb = new StringBuilder(_length);
185    
186                    Set<String> keys = new TreeSet<String>(keySet());
187    
188                    for (String key : keys) {
189                            String value = get(key);
190    
191                            if (Validator.isNull(value)) {
192                                    continue;
193                            }
194    
195                            if (_safe) {
196                                    value = _encode(value);
197                            }
198    
199                            sb.append(key);
200                            sb.append(StringPool.EQUAL);
201                            sb.append(value);
202                            sb.append(StringPool.NEW_LINE);
203                    }
204    
205                    return sb.toString();
206            }
207    
208            @Override
209            public String toString() {
210                    StringBuilder sb = new StringBuilder(_length);
211    
212                    for (Map.Entry<String, String> entry : entrySet()) {
213                            String value = entry.getValue();
214    
215                            if (Validator.isNull(value)) {
216                                    continue;
217                            }
218    
219                            if (_safe) {
220                                    value = _encode(value);
221                            }
222    
223                            sb.append(entry.getKey());
224                            sb.append(StringPool.EQUAL);
225                            sb.append(value);
226                            sb.append(StringPool.NEW_LINE);
227                    }
228    
229                    return sb.toString();
230            }
231    
232            protected int getToStringLength() {
233                    return _length;
234            }
235    
236            private static String _decode(String value) {
237                    return StringUtil.replace(
238                            value, _SAFE_NEWLINE_CHARACTER, StringPool.NEW_LINE);
239            }
240    
241            private static String _encode(String value) {
242                    return StringUtil.replace(
243                            value,
244                            new String[] {
245                                    StringPool.RETURN_NEW_LINE, StringPool.NEW_LINE,
246                                    StringPool.RETURN
247                            },
248                            new String[] {
249                                    _SAFE_NEWLINE_CHARACTER, _SAFE_NEWLINE_CHARACTER,
250                                    _SAFE_NEWLINE_CHARACTER
251                            });
252            }
253    
254            private boolean _isComment(String line) {
255                    return (line.length() == 0) || line.startsWith(StringPool.POUND);
256            }
257    
258            private static final String _SAFE_NEWLINE_CHARACTER =
259                    "_SAFE_NEWLINE_CHARACTER_";
260    
261            private static Log _log = LogFactoryUtil.getLog(UnicodeProperties.class);
262    
263            private int _length;
264            private boolean _safe = false;
265    
266    }