rebuilding head
[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   private static String decode(String aValue) {
121     try {
122       SimpleParser parser = new SimpleParser(aValue);
123       StringBuffer result = new StringBuffer();
124
125       while (!parser.isAtEnd()) {
126         result.append(parser.parse(PLAIN));
127
128         if (!parser.isAtEnd()) {
129           if (parser.parses(UNICODE)) {
130             String unicode = parser.parse(UNICODE);
131
132             result.append((char) Integer.parseInt(unicode.substring(2,6), 16));
133           }
134           else if (parser.parses(ESCAPE)) {
135             String escape = parser.parse(ESCAPE);
136             result.append(escape.substring(1));
137           }
138           else
139             throw new PropertiesManipulatorExc("Invalid escape code: " + parser.remainingData());
140         }
141       }
142
143       return result.toString();
144     }
145     catch (Throwable t) {
146       throw new PropertiesManipulatorFailure(t);
147     }
148   }
149
150   private static String encode(String aValue, boolean aUseUnicodeEscapes) {
151     try {
152       StringBuffer result = new StringBuffer();
153       boolean leadingspace=true;
154
155       for (int i = 0; i<aValue.length(); i++) {
156         char c = aValue.charAt(i);
157
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++)
162             result.append("0");
163           result.append(code);
164         }
165         else if (c=='\\')
166         {
167           result.append("\\\\");
168         }
169         else if (c=='\n')
170         {
171           result.append("\\n");
172         }
173         else if (c=='\r')
174         {
175           result.append("\\r");
176         }
177         else if (c=='\t')
178         {
179           result.append("\\t");
180         }
181         else if (c==' ' && leadingspace) {
182           result.append("\\ ");
183         }
184         else {
185           result.append(c);
186         }
187
188         leadingspace = leadingspace && c ==' ';
189       }
190
191       return result.toString();
192     }
193     catch (Throwable t) {
194       throw new PropertiesManipulatorFailure(t);
195     }
196   }
197
198   // ML: to be fixed
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 = "(([\\\\].)|([^\\\\]))*";
203
204   public static PropertiesManipulator readProperties(InputStream anInputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
205     return readProperties(anInputStream, "ISO-8859-1");
206   }
207
208   public static PropertiesManipulator readProperties(InputStream anInputStream, String anEncoding) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
209     try {
210       PropertiesManipulator result = new PropertiesManipulator();
211       LineNumberReader reader = new LineNumberReader(new InputStreamReader(anInputStream, anEncoding));
212
213       String line = reader.readLine();
214
215       while (line != null) {
216         String trimmedLine = line.trim();
217
218         if (trimmedLine.length() == 0) {
219           result.addEmptyLine();
220         }
221         else if (trimmedLine.startsWith("!") || trimmedLine.startsWith("#")) {
222           result.addComment(line);
223         }
224         else {
225           SimpleParser parser = new SimpleParser(line);
226           parser.skip(SPACE);
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());
233
234             line = reader.readLine();
235             if (line==null) {
236               throw new PropertiesManipulatorExc("Unexpected end of file");
237             }
238             parser = new SimpleParser(line);
239             parser.skip(SPACE);
240             value = value + parser.parse(VALUE);
241           }
242
243           result.addEntry(decode(key), decode(value));
244         }
245         line = reader.readLine();
246       }
247
248       reader.close();
249
250       return result;
251     }
252     catch (PropertiesManipulatorExc t) {
253       throw t;
254     }
255     catch (Throwable t) {
256       throw new PropertiesManipulatorFailure(t);
257     }
258   }
259
260   public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
261     writeProperties(aProperties, anOutputStream, "ISO-8859-1", true);
262   }
263
264   public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream, String anEncoding, boolean aUseUnicodeEscapes) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
265     try {
266       PrintWriter p = new PrintWriter(new OutputStreamWriter(anOutputStream, anEncoding));
267
268       try {
269         Iterator i = aProperties.getEntries();
270
271         while (i.hasNext()) {
272           Object entry = i.next();
273
274           if (entry instanceof EmptyLine) {
275             p.println();
276           }
277           else if (entry instanceof Comment) {
278             p.println(((Comment) entry).getComment());
279           }
280           else if (entry instanceof Entry) {
281             String key = encode( ( (Entry) entry).getKey(), aUseUnicodeEscapes);
282             String value = "";
283             if ( ( (Entry) entry).getValue() != null)
284               value = encode( ( (Entry) entry).getValue(), aUseUnicodeEscapes);
285
286             String line = key + " = " + value;
287
288             p.println(line);
289
290           }
291           else throw new PropertiesManipulatorExc("Unknown entry class: " +entry.getClass().getName());
292         }
293       }
294       finally {
295         p.close();
296       }
297     }
298     catch (Throwable t) {
299       throw new PropertiesManipulatorFailure(t);
300     }
301   }
302
303   public static class PropertiesManipulatorFailure extends Failure {
304     public PropertiesManipulatorFailure(Throwable aThrowable) {
305       super(aThrowable.getMessage(), aThrowable);
306     }
307
308     public PropertiesManipulatorFailure(String aMessage, Throwable aThrowable) {
309       super(aMessage, aThrowable);
310     }
311   }
312
313   public static class PropertiesManipulatorExc extends Exc {
314     public PropertiesManipulatorExc(String aMessage) {
315       super(aMessage);
316     }
317   }
318 }