2 * jSite-next - MapWriter.java -
3 * Copyright © 2008 David Roden
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 package net.pterodactylus.util.collection;
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.io.Writer;
26 import java.util.HashMap;
28 import java.util.Properties;
29 import java.util.Map.Entry;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
33 import net.pterodactylus.util.io.Closer;
34 import net.pterodactylus.util.logging.Logging;
35 import net.pterodactylus.util.number.Hex;
38 * Helper class that emulates the function of
39 * {@link Properties#store(java.io.OutputStream, String)} and
40 * {@link Properties#load(java.io.InputStream)} but does not suffer from the
41 * drawbacks of {@link Properties} (namely the fact that a
42 * <code>Properties</code> can not contain <code>null</code> values).
44 * @author David Roden <droden@gmail.com>
46 public class MapWriter {
49 private static final Logger logger = Logging.getLogger(MapWriter.class.getName());
52 * Writes the given map to the given writer.
55 * The writer to write the map’s content to
59 * if an I/O error occurs
61 public static void write(Writer writer, Map<String, String> map) throws IOException {
62 for (Entry<String, String> entry : map.entrySet()) {
63 if (entry.getValue() != null) {
64 writer.write(encode(entry.getKey()));
66 writer.write(encode(entry.getValue()));
73 * Reads a map from the given reader. Lines are read from the given reader
74 * until a line is encountered that does not contain a colon (“:”) or equals
78 * The reader to read from
79 * @return The map that was read
81 * if an I/O error occurs
83 public static Map<String, String> read(Reader reader) throws IOException {
84 logger.log(Level.FINE, "MapWriter.read(reader=" + reader + ")");
85 Map<String, String> map = new HashMap<String, String>();
86 BufferedReader bufferedReader = new BufferedReader(reader);
89 while ((line = bufferedReader.readLine()) != null) {
90 logger.log(Level.FINEST, "Read line: “" + line + "”");
91 if (line.startsWith("#") || (line.length() == 0)) {
94 if (line.indexOf('=') == -1) {
97 int split = line.indexOf('=');
98 String key = decode(line.substring(0, split));
99 String value = decode(line.substring(split + 1));
103 Closer.close(bufferedReader);
113 * Encodes the given String by replacing certain “unsafe” characters. CR
114 * (0x0d) is replaced by “\r”, LF (0x0a) is replaced by “\n”, the backslash
115 * (‘\’) will be replaced by “\\”, other characters that are either smaller
116 * than 0x20 or larger than 0x7f or that are ‘:’ or ‘=’ will be replaced by
117 * their unicode notation (“\u0000” for NUL, 0x00). All other values are
121 * The value to encode
122 * @return The encoded value
124 static String encode(String value) {
125 StringBuilder encodedString = new StringBuilder();
126 for (char character : value.toCharArray()) {
127 if (character == 0x0d) {
128 encodedString.append("\\r");
129 } else if (character == 0x0a) {
130 encodedString.append("\\n");
131 } else if (character == '\\') {
132 encodedString.append("\\\\");
133 } else if ((character < 0x20) || (character == '=') || (character > 0x7f)) {
134 encodedString.append("\\u").append(Hex.toHex(character, 4));
136 encodedString.append(character);
139 return encodedString.toString();
143 * Decodes the given value by reversing the changes made by
144 * {@link #encode(String)}.
147 * The value to decode
148 * @return The decoded value
150 static String decode(String value) {
151 StringBuilder decodedString = new StringBuilder();
152 boolean backslash = false;
154 char[] hexDigits = new char[4];
155 for (char character : value.toCharArray()) {
157 hexDigits[hexDigit - 1] = character;
160 decodedString.append((char) Integer.parseInt(new String(hexDigits), 16));
163 } else if (backslash) {
164 if (character == '\\') {
165 decodedString.append('\\');
166 } else if (character == 'r') {
167 decodedString.append('\r');
168 } else if (character == 'n') {
169 decodedString.append('\n');
170 } else if (character == 'u') {
174 } else if (character == '\\') {
178 decodedString.append(character);
181 return decodedString.toString();