3 import java.io.InputStream;
\r
4 import java.io.InputStreamReader;
\r
5 import java.io.LineNumberReader;
\r
6 import java.io.OutputStream;
\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
15 import multex.Failure;
\r
17 public class PropertiesManipulator {
\r
18 private List entries;
\r
21 public PropertiesManipulator() {
\r
22 entries = new Vector();
\r
23 values = new HashMap();
\r
26 public void addEmptyLine() {
\r
27 entries.add(new EmptyLine());
\r
30 public void addComment(String aComment) {
\r
31 entries.add(new Comment(aComment));
\r
34 public void addEntry(String aKey, String aValue) {
\r
35 entries.add(new Entry(aKey, aValue));
\r
36 values.put(aKey, aValue);
\r
39 public Iterator getEntries() {
\r
40 return entries.iterator();
\r
43 public String get(String aKey) {
\r
44 return (String) values.get(aKey);
\r
47 public boolean containsKey(String aKey) {
\r
48 return values.containsKey(aKey);
\r
51 public static class Comment {
\r
52 private String comment;
\r
54 public Comment(String aComment) {
\r
58 public String getComment() {
\r
63 public static class EmptyLine {
\r
64 public EmptyLine() {
\r
68 public static class Entry {
\r
70 private String value;
\r
72 public Entry(String aKey, String aValue) {
\r
77 public String getKey() {
\r
81 public String getValue() {
\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
91 private static String decode(String aValue) {
\r
93 SimpleParser parser = new SimpleParser(aValue);
\r
94 StringBuffer result = new StringBuffer();
\r
96 while (!parser.isAtEnd()) {
\r
97 result.append(parser.parse(PLAIN));
\r
99 if (!parser.isAtEnd()) {
\r
100 if (parser.parses(UNICODE)) {
\r
101 String unicode = parser.parse(UNICODE);
\r
103 result.append((char) Integer.parseInt(unicode.substring(2,6), 16));
\r
105 else if (parser.parses(ESCAPE)) {
\r
106 String escape = parser.parse(ESCAPE);
\r
107 result.append(escape.substring(1));
\r
110 throw new PropertiesManipulatorExc("Invalid escape code: " + parser.remainingData());
\r
114 return result.toString();
\r
116 catch (Throwable t) {
\r
117 throw new PropertiesManipulatorFailure(t);
\r
121 private static String encode(String aValue, boolean aUseUnicodeEscapes) {
\r
123 StringBuffer result = new StringBuffer();
\r
124 boolean leadingspace=true;
\r
126 for (int i = 0; i<aValue.length(); i++) {
\r
127 char c = aValue.charAt(i);
\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
138 result.append("\\\\");
\r
142 result.append("\\n");
\r
146 result.append("\\r");
\r
150 result.append("\\t");
\r
152 else if (c==' ' && leadingspace) {
\r
153 result.append("\\ ");
\r
159 leadingspace = leadingspace && c ==' ';
\r
162 return result.toString();
\r
164 catch (Throwable t) {
\r
165 throw new PropertiesManipulatorFailure(t);
\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
176 public static PropertiesManipulator readProperties(InputStream anInputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
\r
177 return readProperties(anInputStream, "ISO-8859-1");
\r
180 public static PropertiesManipulator readProperties(InputStream anInputStream, String anEncoding) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
\r
182 PropertiesManipulator result = new PropertiesManipulator();
\r
183 LineNumberReader reader = new LineNumberReader(new InputStreamReader(anInputStream, anEncoding));
\r
185 String line = reader.readLine();
\r
187 while (line != null) {
\r
188 String trimmedLine = line.trim();
\r
190 if (trimmedLine.length() == 0) {
\r
191 result.addEmptyLine();
\r
193 else if (trimmedLine.startsWith("!") || trimmedLine.startsWith("#")) {
\r
194 result.addComment(line);
\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
206 line = reader.readLine();
\r
208 throw new PropertiesManipulatorExc("Unexpected end of file");
\r
210 parser = new SimpleParser(line);
\r
211 parser.skip(SPACE);
\r
212 value = value + parser.parse(VALUE);
\r
215 result.addEntry(decode(key), decode(value));
\r
217 line = reader.readLine();
\r
224 catch (PropertiesManipulatorExc t) {
\r
227 catch (Throwable t) {
\r
228 throw new PropertiesManipulatorFailure(t);
\r
232 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
\r
233 writeProperties(aProperties, anOutputStream, "ISO-8859-1", true);
\r
236 public static void writeProperties(PropertiesManipulator aProperties, OutputStream anOutputStream, String anEncoding, boolean aUseUnicodeEscapes) throws PropertiesManipulatorExc, PropertiesManipulatorFailure {
\r
238 PrintWriter p = new PrintWriter(new OutputStreamWriter(anOutputStream, anEncoding));
\r
241 Iterator i = aProperties.getEntries();
\r
243 while (i.hasNext()) {
\r
244 Object entry = i.next();
\r
246 if (entry instanceof EmptyLine) {
\r
249 else if (entry instanceof Comment) {
\r
250 p.println(((Comment) entry).getComment());
\r
252 else if (entry instanceof Entry) {
\r
253 String key = encode( ( (Entry) entry).getKey(), aUseUnicodeEscapes);
\r
255 if ( ( (Entry) entry).getValue() != null)
\r
256 value = encode( ( (Entry) entry).getValue(), aUseUnicodeEscapes);
\r
258 String line = key + " = " + value;
\r
263 else throw new PropertiesManipulatorExc("Unknown entry class: " +entry.getClass().getName());
\r
270 catch (Throwable t) {
\r
271 throw new PropertiesManipulatorFailure(t);
\r
275 public static class PropertiesManipulatorFailure extends Failure {
\r
276 public PropertiesManipulatorFailure(Throwable aThrowable) {
\r
277 super(aThrowable.getMessage(), aThrowable);
\r
280 public PropertiesManipulatorFailure(String aMessage, Throwable aThrowable) {
\r
281 super(aMessage, aThrowable);
\r
285 public static class PropertiesManipulatorExc extends Exc {
\r
286 public PropertiesManipulatorExc(String aMessage) {
\r