2 * Copyright (C) 2001, 2002 The Mir-coders group
4 * This file is part of Mir.
6 * Mir is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Mir is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Mir; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * In addition, as a special exception, The Mir-coders gives permission to link
21 * the code of this program with any library licensed under the Apache Software License,
22 * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23 * (or with modified versions of the above that use the same license as the above),
24 * and distribute linked combinations including the two. You must obey the
25 * GNU General Public License in all respects for all of the code used other than
26 * the above mentioned libraries. If you modify this file, you may extend this
27 * exception to your version of the file, but you are not obligated to do so.
28 * If you do not wish to do so, delete this exception statement from your version.
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.io.LineNumberReader;
35 import java.io.OutputStream;
36 import java.io.OutputStreamWriter;
37 import java.io.PrintWriter;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
42 import java.util.Vector;
45 import multex.Failure;
47 public class PropertiesManipulator {
51 public PropertiesManipulator() {
52 entries = new Vector();
53 values = new HashMap();
56 public void addEmptyLine() {
57 entries.add(new EmptyLine());
60 public void addComment(String aComment) {
61 entries.add(new Comment(aComment));
64 public void addEntry(String aKey, String aValue) {
65 entries.add(new Entry(aKey, aValue));
66 values.put(aKey, aValue);
69 public Iterator getEntries() {
70 return entries.iterator();
73 public String get(String aKey) {
74 return (String) values.get(aKey);
77 public boolean containsKey(String aKey) {
78 return values.containsKey(aKey);
81 public static class Comment {
82 private String comment;
84 public Comment(String aComment) {
88 public String getComment() {
93 public static class EmptyLine {
98 public static class Entry {
100 private String value;
102 public Entry(String aKey, String aValue) {
107 public String getKey() {
111 public String getValue() {
116 private final static String PLAIN= "[^\\\\]*";
117 private final static String ESCAPE= "\\\\[ tn]";
118 private final static String UNICODE= "\\\\u[a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]";
121 private static String decode(String aValue) {
123 SimpleParser parser = new SimpleParser(aValue);
124 StringBuffer result = new StringBuffer();
126 while (!parser.isAtEnd()) {
127 result.append(parser.parse(PLAIN));
129 if (!parser.isAtEnd()) {
130 if (parser.parses(UNICODE)) {
131 String unicode = parser.parse(UNICODE);
133 result.append((char) Integer.parseInt(unicode.substring(2,6), 16));
135 else if (parser.parses(ESCAPE)) {
136 String escape = parser.parse(ESCAPE);
137 result.append(escape.substring(1));
140 throw new PropertiesManipulatorExc("Invalid escape code: " + parser.remainingData());
144 return result.toString();
146 catch (Throwable t) {
147 throw new PropertiesManipulatorFailure(t);
151 private static String encode(String aValue, boolean aUseUnicodeEscapes) {
153 StringBuffer result = new StringBuffer();
154 boolean leadingspace=true;
156 for (int i = 0; i<aValue.length(); i++) {
157 char c = aValue.charAt(i);
159 if (aUseUnicodeEscapes && (c<0x20 || c>0x7e)) {
160 String code=Integer.toHexString(c);
161 result.append("\\u");
162 for (int j=0; j<4-code.length(); j++)
168 result.append("\\\\");
172 result.append("\\n");
176 result.append("\\r");
180 result.append("\\t");
182 else if (c==' ' && leadingspace) {
183 result.append("\\ ");
189 leadingspace = leadingspace && c ==' ';
192 return result.toString();
194 catch (Throwable t) {
195 throw new PropertiesManipulatorFailure(t);
200 private final static String SPACE = "[\t\n\r ]*";
201 private final static String KEY = "(([\\\\].)|([^\\\\=: \t\n\r]))*";
202 private final static String SEPARATOR = "[\t\n\r ]*[:=]?[\t\n\r ]*";
203 private final static String VALUE = "(([\\\\].)|([^\\\\]))*";
205 public static PropertiesManipulator readProperties(InputStream anInputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
206 return readProperties(anInputStream, "ISO-8859-1");
209 public static PropertiesManipulator readProperties(InputStream anInputStream, String anEncoding) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
211 PropertiesManipulator result = new PropertiesManipulator();
212 LineNumberReader reader = new LineNumberReader(new InputStreamReader(anInputStream, anEncoding));
214 String line = reader.readLine();
216 while (line != null) {
217 String trimmedLine = line.trim();
219 if (trimmedLine.length() == 0) {
220 result.addEmptyLine();
222 else if (trimmedLine.startsWith("!") || trimmedLine.startsWith("#")) {
223 result.addComment(line);
226 SimpleParser parser = new SimpleParser(line);
228 String key = parser.parse(KEY);
229 parser.skip(SEPARATOR);
230 String value = parser.parse(VALUE);
231 while (parser.remainingData().length()>0) {
232 if (!parser.remainingData().equals("\\"))
233 throw new PropertiesManipulatorExc("internal error: remainingData = " + parser.remainingData());
235 line = reader.readLine();
237 throw new PropertiesManipulatorExc("Unexpected end of file");
239 parser = new SimpleParser(line);
241 value = value + parser.parse(VALUE);
244 result.addEntry(decode(key), decode(value));
246 line = reader.readLine();
253 catch (PropertiesManipulatorExc t) {
256 catch (Throwable t) {
257 throw new PropertiesManipulatorFailure(t);
261 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
262 writeProperties(aProperties, anOutputStream, "ISO-8859-1", true);
265 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream, String anEncoding, boolean aUseUnicodeEscapes) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
267 PrintWriter p = new PrintWriter(new OutputStreamWriter(anOutputStream, anEncoding));
270 Iterator i = aProperties.getEntries();
272 while (i.hasNext()) {
273 Object entry = i.next();
275 if (entry instanceof EmptyLine) {
278 else if (entry instanceof Comment) {
279 p.println(((Comment) entry).getComment());
281 else if (entry instanceof Entry) {
282 String key = encode( ( (Entry) entry).getKey(), aUseUnicodeEscapes);
284 if ( ( (Entry) entry).getValue() != null)
285 value = encode( ( (Entry) entry).getValue(), aUseUnicodeEscapes);
287 String line = key + " = " + value;
292 else throw new PropertiesManipulatorExc("Unknown entry class: " +entry.getClass().getName());
299 catch (Throwable t) {
300 throw new PropertiesManipulatorFailure(t);
304 public static class PropertiesManipulatorFailure extends Failure {
305 public PropertiesManipulatorFailure(Throwable aThrowable) {
306 super(aThrowable.getMessage(), aThrowable);
309 public PropertiesManipulatorFailure(String aMessage, Throwable aThrowable) {
310 super(aMessage, aThrowable);
314 public static class PropertiesManipulatorExc extends Exc {
315 public PropertiesManipulatorExc(String aMessage) {