Support for structured fields
[mir.git] / source / mir / util / StructuredContentParser.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
31 package mir.util;
32
33 import java.util.*;
34
35
36 /**
37  *
38  * <p>Title: </p>
39  * <p>Description:
40  *   Class to parse structured content:
41  *   <tt>
42  *      {
43  *        name = 'Countries'
44  *        countries = [ spain 'united kingdom' germany ]
45  *        cities = {
46  *          spain = [ 'madrid' barcelona bilbao ]
47  *          'united kingdom' = [ 'london' 'lancaster' ]
48  *          germany = [ 'berlin' 'bremen' ]
49  *        }
50  *      }
51  *   </tt>
52  *
53  *   And put it into a <code>String</code>/<code>Map</code>/<code>List</code> structure.
54  *  </p>
55  *  <p>
56  *    Parsing is be very optimistic: no exception is ever to be thrown.
57  *  </p>
58  * <p>Copyright: Copyright (c) 2003</p>
59  * <p>Company: </p>
60  * @author not attributable
61  * @version 1.0
62  */
63 public class StructuredContentParser {
64   public StructuredContentParser() {
65   }
66
67   private static class Scanner {
68     private String data;
69     private int position;
70
71     public Scanner(String aData) {
72       data = aData;
73       position = 0;
74     }
75
76     public String scanIdentifier() {
77       StringBuffer result = new StringBuffer();
78
79       while (!isAtEnd() && Character.isJavaIdentifierPart(peek())) {
80         result.append(scan());
81       }
82
83       return result.toString();
84     }
85
86     public String scanString() {
87       StringBuffer result = new StringBuffer();
88       char delimiter='\'';
89
90       if (!isAtEnd()) {
91         delimiter=scan();
92       }
93
94       boolean finished = false;
95
96       while (!finished && !isAtEnd()) {
97         char data = scan();
98
99         if (data == delimiter) {
100           if (!isAtEnd() && peek() == delimiter) {
101             scan();
102             result.append(data);
103           }
104           else
105             finished=true;
106         }
107         else
108           result.append(data);
109       }
110
111       return result.toString();
112     }
113
114     public void skipSpace() {
115       while (!isAtEnd() && Character.isWhitespace(peek()))
116         scan();
117     }
118
119     public boolean isAtEnd() {
120       return position >= data.length();
121     }
122
123     public char peek() {
124       if (!isAtEnd()) {
125         return data.charAt(position);
126       }
127       else
128         return '\0';
129     }
130
131     public char scan() {
132       if (!isAtEnd()) {
133         char result =data.charAt(position);
134         position++;
135         return result;
136       }
137       else
138         return '\0';
139     }
140   }
141
142   public static List parseList(Scanner aScanner) {
143     List result = new Vector();
144     aScanner.skipSpace();
145     if (aScanner.peek() == '[')
146       aScanner.scan();
147     aScanner.skipSpace();
148     Object object = parseObject(aScanner);
149
150     while (object != null) {
151       result.add(object);
152       aScanner.skipSpace();
153       object = parseObject(aScanner);
154     }
155
156     aScanner.skipSpace();
157     if (aScanner.peek() == ']')
158       aScanner.scan();
159
160     return result;
161   }
162
163   public static Map parseMap(Scanner aScanner) {
164     Map result = new HashMap();
165     aScanner.skipSpace();
166     if (aScanner.peek() == '{')
167       aScanner.scan();
168     aScanner.skipSpace();
169     Object key = parseObject(aScanner);
170     aScanner.skipSpace();
171     if (aScanner.peek() == '=')
172       aScanner.scan();
173     aScanner.skipSpace();
174     Object value = parseObject(aScanner);
175
176
177     while (key != null && value!=null) {
178       result.put(key, value);
179       aScanner.skipSpace();
180       key = parseObject(aScanner);
181       aScanner.skipSpace();
182       if (aScanner.peek() == '=')
183         aScanner.scan();
184       aScanner.skipSpace();
185       value = parseObject(aScanner);
186     }
187
188     aScanner.skipSpace();
189     if (aScanner.peek() == '}')
190       aScanner.scan();
191
192     return result;
193   }
194
195   public static Object parseObject(Scanner aScanner) {
196     Object result = null;
197
198     aScanner.skipSpace();
199
200     if (!aScanner.isAtEnd()) {
201       char data = aScanner.peek();
202
203       if (data == '[')
204         return parseList(aScanner);
205       if (data == '{')
206         return parseMap(aScanner);
207       if (data == '\'' || data == '"')
208         return aScanner.scanString();
209       if (Character.isJavaIdentifierPart(data))
210         return aScanner.scanIdentifier();
211     }
212
213     return null;
214   }
215
216   public static Object parse(String aData) {
217     Scanner scanner = new Scanner(aData);
218
219     return parseObject(scanner);
220   }
221
222   public static String constructStringLiteral(String aString) {
223     final char[] CHARACTERS_TO_ESCAPE = { '\'' };
224     final String[] ESCAPE_CODES = { "\'\'" };
225
226     return "'" +  StringRoutines.replaceStringCharacters(aString, CHARACTERS_TO_ESCAPE, ESCAPE_CODES) + "'";
227   }
228 }