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