001    /**
002     * Copyright (c) 2000-present 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.Set;
026    import java.util.TreeSet;
027    
028    /**
029     * <p>
030     * This is a rewrite of java.util.Properties that is not synchronized and
031     * natively supports non-ASCII encodings. It can also be configured to be
032     * "safe", allowing the values to have new line characters. When stored to a
033     * given BufferedWriter, "safe" properties will replace all new line characters
034     * with a _SAFE_NEWLINE_CHARACTER_.
035     * </p>
036     *
037     * <p>
038     * In its current form, this is not intended to replace java.util.Properties for
039     * reading properties flat files. This class is not thread-safe.
040     * </p>
041     *
042     * @author Alexander Chow
043     */
044    public class UnicodeProperties extends HashMap<String, String> {
045    
046            public UnicodeProperties() {
047                    _safe = false;
048            }
049    
050            public UnicodeProperties(boolean safe) {
051                    _safe = safe;
052            }
053    
054            public void fastLoad(String props) {
055                    if (Validator.isNull(props)) {
056                            return;
057                    }
058    
059                    int x = props.indexOf(CharPool.NEW_LINE);
060                    int y = 0;
061    
062                    while (x != -1) {
063                            put(props.substring(y, x));
064    
065                            y = x;
066    
067                            x = props.indexOf(CharPool.NEW_LINE, y + 1);
068                    }
069    
070                    put(props.substring(y));
071            }
072    
073            public String getProperty(String key) {
074                    return get(key);
075            }
076    
077            public String getProperty(String key, String defaultValue) {
078                    String value = getProperty(key);
079    
080                    if (value == null) {
081                            return defaultValue;
082                    }
083                    else {
084                            return value;
085                    }
086            }
087    
088            public boolean isSafe() {
089                    return _safe;
090            }
091    
092            public void load(String props) throws IOException {
093                    if (Validator.isNull(props)) {
094                            return;
095                    }
096    
097                    UnsyncBufferedReader unsyncBufferedReader = null;
098    
099                    try {
100                            unsyncBufferedReader = new UnsyncBufferedReader(
101                                    new UnsyncStringReader(props));
102    
103                            String line = unsyncBufferedReader.readLine();
104    
105                            while (line != null) {
106                                    put(line);
107                                    line = unsyncBufferedReader.readLine();
108                            }
109                    }
110                    finally {
111                            if (unsyncBufferedReader != null) {
112                                    try {
113                                            unsyncBufferedReader.close();
114                                    }
115                                    catch (Exception e) {
116                                    }
117                            }
118                    }
119            }
120    
121            public void put(String line) {
122                    line = line.trim();
123    
124                    if (!_isComment(line)) {
125                            int pos = line.indexOf(CharPool.EQUAL);
126    
127                            if (pos != -1) {
128                                    String key = line.substring(0, pos).trim();
129                                    String value = line.substring(pos + 1).trim();
130    
131                                    if (_safe) {
132                                            value = _decode(value);
133                                    }
134    
135                                    setProperty(key, value);
136                            }
137                            else {
138                                    _log.error("Invalid property on line " + line);
139                            }
140                    }
141            }
142    
143            @Override
144            public String put(String key, String value) {
145                    if (key == null) {
146                            return null;
147                    }
148    
149                    if (value == null) {
150                            return remove(key);
151                    }
152    
153                    _length += key.length() + value.length() + 2;
154    
155                    return super.put(key, value);
156            }
157    
158            @Override
159            public String remove(Object key) {
160                    if ((key == null) || !containsKey(key)) {
161                            return null;
162                    }
163    
164                    String keyString = (String)key;
165    
166                    String value = super.remove(key);
167    
168                    _length -= keyString.length() + value.length() + 2;
169    
170                    return value;
171            }
172    
173            public String setProperty(String key, String value) {
174                    return put(key, value);
175            }
176    
177            /**
178             * @deprecated As of 7.0.0, replaced by {@link #toString}
179             */
180            @Deprecated
181            public String toSortedString() {
182                    return toString();
183            }
184    
185            @Override
186            public String toString() {
187                    StringBuilder sb = new StringBuilder(_length);
188    
189                    Set<String> keys = new TreeSet<>(keySet());
190    
191                    for (String key : keys) {
192                            String value = get(key);
193    
194                            if (Validator.isNull(value)) {
195                                    continue;
196                            }
197    
198                            if (_safe) {
199                                    value = _encode(value);
200                            }
201    
202                            sb.append(key);
203                            sb.append(StringPool.EQUAL);
204                            sb.append(value);
205                            sb.append(StringPool.NEW_LINE);
206                    }
207    
208                    return sb.toString();
209            }
210    
211            protected int getToStringLength() {
212                    return _length;
213            }
214    
215            private static String _decode(String value) {
216                    return StringUtil.replace(
217                            value, _SAFE_NEWLINE_CHARACTER, StringPool.NEW_LINE);
218            }
219    
220            private static String _encode(String value) {
221                    return StringUtil.replace(
222                            value,
223                            new String[] {
224                                    StringPool.RETURN_NEW_LINE, StringPool.NEW_LINE,
225                                    StringPool.RETURN
226                            },
227                            new String[] {
228                                    _SAFE_NEWLINE_CHARACTER, _SAFE_NEWLINE_CHARACTER,
229                                    _SAFE_NEWLINE_CHARACTER
230                            });
231            }
232    
233            private boolean _isComment(String line) {
234                    return (line.length() == 0) || line.startsWith(StringPool.POUND);
235            }
236    
237            private static final String _SAFE_NEWLINE_CHARACTER =
238                    "_SAFE_NEWLINE_CHARACTER_";
239    
240            private static final Log _log = LogFactoryUtil.getLog(
241                    UnicodeProperties.class);
242    
243            private int _length;
244            private final boolean _safe;
245    
246    }