ae88073ecc89f85fc14cb0e4c83e4c5b0c4806aa
[mir.git] / source / mir / util / PropertiesManipulator.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
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.
10  *
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.
15  *
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
19  *
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.
29  */
30 package mir.util;
31
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;
41 import java.util.Map;
42 import java.util.Vector;
43
44 import multex.Exc;
45 import multex.Failure;
46
47 public class PropertiesManipulator {
48   private List entries;
49   private Map values;
50
51   public PropertiesManipulator() {
52     entries = new Vector();
53     values = new HashMap();
54   }
55
56   public void addEmptyLine() {
57     entries.add(new EmptyLine());
58   }
59
60   public void addComment(String aComment) {
61     entries.add(new Comment(aComment));
62   }
63
64   public void addEntry(String aKey, String aValue) {
65     entries.add(new Entry(aKey, aValue));
66     values.put(aKey, aValue);
67   }
68
69   public Iterator getEntries() {
70     return entries.iterator();
71   }
72
73   public String get(String aKey) {
74     return (String) values.get(aKey);
75   }
76
77   public boolean containsKey(String aKey) {
78     return values.containsKey(aKey);
79   }
80
81   public static class Comment {
82     private String comment;
83
84     public Comment(String aComment) {
85       comment = aComment;
86     }
87
88     public String getComment() {
89       return comment;
90     }
91   }
92
93   public static class EmptyLine {
94     public EmptyLine() {
95     }
96   }
97
98   public static class Entry {
99     private String key;
100     private String value;
101
102     public Entry(String aKey, String aValue) {
103       key = aKey;
104       value = aValue;
105     }
106
107     public String getKey() {
108       return key;
109     }
110
111     public String getValue() {
112       return value;
113     }
114   }
115
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]";
119
120
121   private static String decode(String aValue) {
122     try {
123       SimpleParser parser = new SimpleParser(aValue);
124       StringBuffer result = new StringBuffer();
125
126       while (!parser.isAtEnd()) {
127         result.append(parser.parse(PLAIN));
128
129         if (!parser.isAtEnd()) {
130           if (parser.parses(UNICODE)) {
131             String unicode = parser.parse(UNICODE);
132
133             result.append((char) Integer.parseInt(unicode.substring(2,6), 16));
134           }
135           else if (parser.parses(ESCAPE)) {
136             String escape = parser.parse(ESCAPE);
137             result.append(escape.substring(1));
138           }
139           else
140             throw new PropertiesManipulatorExc("Invalid escape code: " + parser.remainingData());
141         }
142       }
143
144       return result.toString();
145     }
146     catch (Throwable t) {
147       throw new PropertiesManipulatorFailure(t);
148     }
149   }
150
151   private static String encode(String aValue, boolean aUseUnicodeEscapes) {
152     try {
153       StringBuffer result = new StringBuffer();
154       boolean leadingspace=true;
155
156       for (int i = 0; i<aValue.length(); i++) {
157         char c = aValue.charAt(i);
158
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++)
163             result.append("0");
164           result.append(code);
165         }
166         else if (c=='\\')
167         {
168           result.append("\\\\");
169         }
170         else if (c=='\n')
171         {
172           result.append("\\n");
173         }
174         else if (c=='\r')
175         {
176           result.append("\\r");
177         }
178         else if (c=='\t')
179         {
180           result.append("\\t");
181         }
182         else if (c==' ' && leadingspace) {
183           result.append("\\ ");
184         }
185         else {
186           result.append(c);
187         }
188
189         leadingspace = leadingspace && c ==' ';
190       }
191
192       return result.toString();
193     }
194     catch (Throwable t) {
195       throw new PropertiesManipulatorFailure(t);
196     }
197   }
198
199   // ML: to be fixed
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 = "(([\\\\].)|([^\\\\]))*";
204
205
206   public static PropertiesManipulator readProperties(InputStream anInputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
207     return readProperties(anInputStream, "ISO-8859-1");
208   }
209
210   public static PropertiesManipulator readProperties(InputStream anInputStream, String anEncoding) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
211     try {
212       PropertiesManipulator result = new PropertiesManipulator();
213       LineNumberReader reader = new LineNumberReader(new InputStreamReader(anInputStream, anEncoding));
214
215       String line = reader.readLine();
216
217       while (line != null) {
218         String trimmedLine = line.trim();
219
220         if (trimmedLine.length() == 0) {
221           result.addEmptyLine();
222         }
223         else if (trimmedLine.startsWith("!") || trimmedLine.startsWith("#")) {
224           result.addComment(line);
225         }
226         else {
227           SimpleParser parser = new SimpleParser(line);
228           parser.skip(SPACE);
229           String key = parser.parse(KEY);
230           parser.skip(SEPARATOR);
231           String value = parser.parse(VALUE);
232           while (parser.remainingData().length()>0) {
233             if (!parser.remainingData().equals("\\"))
234               throw new PropertiesManipulatorExc("internal error: remainingData = " + parser.remainingData());
235
236             line = reader.readLine();
237             if (line==null) {
238               throw new PropertiesManipulatorExc("Unexpected end of file");
239             }
240             parser = new SimpleParser(line);
241             parser.skip(SPACE);
242             value = value + parser.parse(VALUE);
243           }
244
245           result.addEntry(decode(key), decode(value));
246         }
247         line = reader.readLine();
248       }
249
250       reader.close();
251
252       return result;
253     }
254     catch (PropertiesManipulatorExc t) {
255       throw t;
256     }
257     catch (Throwable t) {
258       throw new PropertiesManipulatorFailure(t);
259     }
260   }
261
262   public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
263     writeProperties(aProperties, anOutputStream, "ISO-8859-1", true);
264   }
265
266   public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream, String anEncoding, boolean aUseUnicodeEscapes) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
267     try {
268       PrintWriter p = new PrintWriter(new OutputStreamWriter(anOutputStream, anEncoding));
269
270       try {
271         Iterator i = aProperties.getEntries();
272
273         while (i.hasNext()) {
274           Object entry = i.next();
275
276           if (entry instanceof EmptyLine) {
277             p.println();
278           }
279           else if (entry instanceof Comment) {
280             p.println(((Comment) entry).getComment());
281           }
282           else if (entry instanceof Entry) {
283             String key = encode( ( (Entry) entry).getKey(), aUseUnicodeEscapes);
284             String value = "";
285             if ( ( (Entry) entry).getValue() != null)
286               value = encode( ( (Entry) entry).getValue(), aUseUnicodeEscapes);
287
288             String line = key + " = " + value;
289
290             p.println(line);
291
292           }
293           else throw new PropertiesManipulatorExc("Unknown entry class: " +entry.getClass().getName());
294         }
295       }
296       finally {
297         p.close();
298       }
299     }
300     catch (Throwable t) {
301       throw new PropertiesManipulatorFailure(t);
302     }
303   }
304
305   public static class PropertiesManipulatorFailure extends Failure {
306     public PropertiesManipulatorFailure(Throwable aThrowable) {
307       super(aThrowable.getMessage(), aThrowable);
308     }
309
310     public PropertiesManipulatorFailure(String aMessage, Throwable aThrowable) {
311       super(aMessage, aThrowable);
312     }
313   }
314
315   public static class PropertiesManipulatorExc extends Exc {
316     public PropertiesManipulatorExc(String aMessage) {
317       super(aMessage);
318     }
319   }
320 }