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]";
120 private static String decode(String aValue) {
122 SimpleParser parser = new SimpleParser(aValue);
123 StringBuffer result = new StringBuffer();
125 while (!parser.isAtEnd()) {
126 result.append(parser.parse(PLAIN));
128 if (!parser.isAtEnd()) {
129 if (parser.parses(UNICODE)) {
130 String unicode = parser.parse(UNICODE);
132 result.append((char) Integer.parseInt(unicode.substring(2,6), 16));
134 else if (parser.parses(ESCAPE)) {
135 String escape = parser.parse(ESCAPE);
136 result.append(escape.substring(1));
139 throw new PropertiesManipulatorExc("Invalid escape code: " + parser.remainingData());
143 return result.toString();
145 catch (Throwable t) {
146 throw new PropertiesManipulatorFailure(t);
150 private static String encode(String aValue, boolean aUseUnicodeEscapes) {
152 StringBuffer result = new StringBuffer();
153 boolean leadingspace=true;
155 for (int i = 0; i<aValue.length(); i++) {
156 char c = aValue.charAt(i);
158 if (aUseUnicodeEscapes && (c<0x20 || c>0x7e)) {
159 String code=Integer.toHexString(c);
160 result.append("\\u");
161 for (int j=0; j<4-code.length(); j++)
167 result.append("\\\\");
171 result.append("\\n");
175 result.append("\\r");
179 result.append("\\t");
181 else if (c==' ' && leadingspace) {
182 result.append("\\ ");
188 leadingspace = leadingspace && c ==' ';
191 return result.toString();
193 catch (Throwable t) {
194 throw new PropertiesManipulatorFailure(t);
199 private final static String SPACE = "[\t\n\r ]*";
200 private final static String KEY = "(([\\\\].)|([^\\\\=: \t\n\r]))*";
201 private final static String SEPARATOR = "[\t\n\r ]*[:=]?[\t\n\r ]*";
202 private final static String VALUE = "(([\\\\].)|([^\\\\]))*";
204 public static PropertiesManipulator readProperties(InputStream anInputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
205 return readProperties(anInputStream, "ISO-8859-1");
208 public static PropertiesManipulator readProperties(InputStream anInputStream, String anEncoding) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
210 PropertiesManipulator result = new PropertiesManipulator();
211 LineNumberReader reader = new LineNumberReader(new InputStreamReader(anInputStream, anEncoding));
213 String line = reader.readLine();
215 while (line != null) {
216 String trimmedLine = line.trim();
218 if (trimmedLine.length() == 0) {
219 result.addEmptyLine();
221 else if (trimmedLine.startsWith("!") || trimmedLine.startsWith("#")) {
222 result.addComment(line);
225 SimpleParser parser = new SimpleParser(line);
227 String key = parser.parse(KEY);
228 parser.skip(SEPARATOR);
229 String value = parser.parse(VALUE);
230 while (parser.remainingData().length()>0) {
231 if (!parser.remainingData().equals("\\"))
232 throw new PropertiesManipulatorExc("internal error: remainingData = " + parser.remainingData());
234 line = reader.readLine();
236 throw new PropertiesManipulatorExc("Unexpected end of file");
238 parser = new SimpleParser(line);
240 value = value + parser.parse(VALUE);
243 result.addEntry(decode(key), decode(value));
245 line = reader.readLine();
252 catch (PropertiesManipulatorExc t) {
255 catch (Throwable t) {
256 throw new PropertiesManipulatorFailure(t);
260 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
261 writeProperties(aProperties, anOutputStream, "ISO-8859-1", true);
264 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream, String anEncoding, boolean aUseUnicodeEscapes) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
266 PrintWriter p = new PrintWriter(new OutputStreamWriter(anOutputStream, anEncoding));
269 Iterator i = aProperties.getEntries();
271 while (i.hasNext()) {
272 Object entry = i.next();
274 if (entry instanceof EmptyLine) {
277 else if (entry instanceof Comment) {
278 p.println(((Comment) entry).getComment());
280 else if (entry instanceof Entry) {
281 String key = encode( ( (Entry) entry).getKey(), aUseUnicodeEscapes);
283 if ( ( (Entry) entry).getValue() != null)
284 value = encode( ( (Entry) entry).getValue(), aUseUnicodeEscapes);
286 String line = key + " = " + value;
291 else throw new PropertiesManipulatorExc("Unknown entry class: " +entry.getClass().getName());
298 catch (Throwable t) {
299 throw new PropertiesManipulatorFailure(t);
303 public static class PropertiesManipulatorFailure extends Failure {
304 public PropertiesManipulatorFailure(Throwable aThrowable) {
305 super(aThrowable.getMessage(), aThrowable);
308 public PropertiesManipulatorFailure(String aMessage, Throwable aThrowable) {
309 super(aMessage, aThrowable);
313 public static class PropertiesManipulatorExc extends Exc {
314 public PropertiesManipulatorExc(String aMessage) {