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 the com.oreilly.servlet library, any library
22 * licensed under the Apache Software License, The Sun (tm) Java Advanced
23 * Imaging library (JAI), The Sun JIMI library (or with modified versions of
24 * the above that use the same license as the above), and distribute linked
25 * combinations including the two. You must obey the GNU General Public
26 * License in all respects for all of the code used other than the above
27 * mentioned libraries. If you modify this file, you may extend this exception
28 * to your version of the file, but you are not obligated to do so. If you do
29 * not wish to do so, delete this exception statement from your version.
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.io.LineNumberReader;
37 import java.io.OutputStream;
38 import java.io.OutputStreamWriter;
39 import java.io.PrintWriter;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.List;
44 import java.util.Vector;
47 import multex.Failure;
49 public class PropertiesManipulator {
53 public PropertiesManipulator() {
54 entries = new Vector();
55 values = new HashMap();
58 public void addEmptyLine() {
59 entries.add(new EmptyLine());
62 public void addComment(String aComment) {
63 entries.add(new Comment(aComment));
66 public void addEntry(String aKey, String aValue) {
67 entries.add(new Entry(aKey, aValue));
68 values.put(aKey, aValue);
71 public Iterator getEntries() {
72 return entries.iterator();
75 public String get(String aKey) {
76 return (String) values.get(aKey);
79 public boolean containsKey(String aKey) {
80 return values.containsKey(aKey);
83 public static class Comment {
84 private String comment;
86 public Comment(String aComment) {
90 public String getComment() {
95 public static class EmptyLine {
100 public static class Entry {
102 private String value;
104 public Entry(String aKey, String aValue) {
109 public String getKey() {
113 public String getValue() {
118 private final static String PLAIN= "[^\\\\]*";
119 private final static String ESCAPE= "\\\\[ tn]";
120 private final static String UNICODE= "\\\\u[a-fA-F0-9][a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]";
123 private static String decode(String aValue) {
125 SimpleParser parser = new SimpleParser(aValue);
126 StringBuffer result = new StringBuffer();
128 while (!parser.isAtEnd()) {
129 result.append(parser.parse(PLAIN));
131 if (!parser.isAtEnd()) {
132 if (parser.parses(UNICODE)) {
133 String unicode = parser.parse(UNICODE);
135 result.append((char) Integer.parseInt(unicode.substring(2,6), 16));
137 else if (parser.parses(ESCAPE)) {
138 String escape = parser.parse(ESCAPE);
139 result.append(escape.substring(1));
142 throw new PropertiesManipulatorExc("Invalid escape code: " + parser.remainingData());
146 return result.toString();
148 catch (Throwable t) {
149 throw new PropertiesManipulatorFailure(t);
153 private static String encode(String aValue, boolean aUseUnicodeEscapes) {
155 StringBuffer result = new StringBuffer();
156 boolean leadingspace=true;
158 for (int i = 0; i<aValue.length(); i++) {
159 char c = aValue.charAt(i);
161 if (aUseUnicodeEscapes && (c<0x20 || c>0x7e)) {
162 String code=Integer.toHexString(c);
163 result.append("\\u");
164 for (int j=0; j<4-code.length(); j++)
170 result.append("\\\\");
174 result.append("\\n");
178 result.append("\\r");
182 result.append("\\t");
184 else if (c==' ' && leadingspace) {
185 result.append("\\ ");
191 leadingspace = leadingspace && c ==' ';
194 return result.toString();
196 catch (Throwable t) {
197 throw new PropertiesManipulatorFailure(t);
202 private final static String SPACE = "[\t\n\r ]*";
203 private final static String KEY = "(([\\\\].)|([^\\\\=: \t\n\r]))*";
204 private final static String SEPARATOR = "[\t\n\r ]*[:=]?[\t\n\r ]*";
205 private final static String VALUE = "(([\\\\].)|([^\\\\]))*";
208 public static PropertiesManipulator readProperties(InputStream anInputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
209 return readProperties(anInputStream, "ISO-8859-1");
212 public static PropertiesManipulator readProperties(InputStream anInputStream, String anEncoding) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
214 PropertiesManipulator result = new PropertiesManipulator();
215 LineNumberReader reader = new LineNumberReader(new InputStreamReader(anInputStream, anEncoding));
217 String line = reader.readLine();
219 while (line != null) {
220 String trimmedLine = line.trim();
222 if (trimmedLine.length() == 0) {
223 result.addEmptyLine();
225 else if (trimmedLine.startsWith("!") || trimmedLine.startsWith("#")) {
226 result.addComment(line);
229 SimpleParser parser = new SimpleParser(line);
231 String key = parser.parse(KEY);
232 parser.skip(SEPARATOR);
233 String value = parser.parse(VALUE);
234 while (parser.remainingData().length()>0) {
235 if (!parser.remainingData().equals("\\"))
236 throw new PropertiesManipulatorExc("internal error: remainingData = " + parser.remainingData());
238 line = reader.readLine();
240 throw new PropertiesManipulatorExc("Unexpected end of file");
242 parser = new SimpleParser(line);
244 value = value + parser.parse(VALUE);
247 result.addEntry(decode(key), decode(value));
249 line = reader.readLine();
256 catch (PropertiesManipulatorExc t) {
259 catch (Throwable t) {
260 throw new PropertiesManipulatorFailure(t);
264 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
265 writeProperties(aProperties, anOutputStream, "ISO-8859-1", true);
268 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream, String anEncoding, boolean aUseUnicodeEscapes) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
270 PrintWriter p = new PrintWriter(new OutputStreamWriter(anOutputStream, anEncoding));
273 Iterator i = aProperties.getEntries();
275 while (i.hasNext()) {
276 Object entry = i.next();
278 if (entry instanceof EmptyLine) {
281 else if (entry instanceof Comment) {
282 p.println(((Comment) entry).getComment());
284 else if (entry instanceof Entry) {
285 String key = encode( ( (Entry) entry).getKey(), aUseUnicodeEscapes);
287 if ( ( (Entry) entry).getValue() != null)
288 value = encode( ( (Entry) entry).getValue(), aUseUnicodeEscapes);
290 String line = key + " = " + value;
295 else throw new PropertiesManipulatorExc("Unknown entry class: " +entry.getClass().getName());
302 catch (Throwable t) {
303 throw new PropertiesManipulatorFailure(t);
307 public static class PropertiesManipulatorFailure extends Failure {
308 public PropertiesManipulatorFailure(Throwable aThrowable) {
309 super(aThrowable.getMessage(), aThrowable);
312 public PropertiesManipulatorFailure(String aMessage, Throwable aThrowable) {
313 super(aMessage, aThrowable);
317 public static class PropertiesManipulatorExc extends Exc {
318 public PropertiesManipulatorExc(String aMessage) {