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