001 package org.apache.tapestry.json;
002
003 /*
004 Copyright (c) 2002 JSON.org
005
006 Permission is hereby granted, free of charge, to any person obtaining a copy
007 of this software and associated documentation files (the "Software"), to deal
008 in the Software without restriction, including without limitation the rights
009 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 copies of the Software, and to permit persons to whom the Software is
011 furnished to do so, subject to the following conditions:
012
013 The above copyright notice and this permission notice shall be included in all
014 copies or substantial portions of the Software.
015
016 The Software shall be used for Good, not Evil.
017
018 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
021 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
023 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
024 SOFTWARE.
025 */
026
027 import java.text.ParseException;
028
029 /**
030 * This provides static methods to convert comma delimited text into a
031 * JSONArray, and to covert a JSONArray into comma delimited text. Comma
032 * delimited text is a very popular format for data interchange. It is
033 * understood by most database, spreadsheet, and organizer programs.
034 * <p>
035 * Each row of text represents a row in a table or a data record. Each row ends
036 * with a NEWLINE character. Each row contains one or more values. Values are
037 * separated by commas. A value can contain any character except for comma,
038 * unless is is wrapped in single quotes or double quotes.
039 * <p>
040 * The first row usually contains the names of the columns.
041 * <p>
042 * A comma delimited list can be converted into a JSONArray of JSONObjects. The
043 * names for the elements in the JSONObjects can be taken from the names in the
044 * first row.
045 */
046 public final class CDL
047 {
048
049 /* defeat instantiation */
050 private CDL()
051 {
052 }
053
054 /**
055 * Get the next value. The value can be wrapped in quotes. The value can be
056 * empty.
057 *
058 * @param x
059 * A JSONTokener of the source text.
060 * @return The value string, or null if empty.
061 * @throws java.text.ParseException
062 * if the quoted string is badly formed.
063 */
064 private static String getValue(JSONTokener x)
065 throws java.text.ParseException
066 {
067 char c;
068 do
069 {
070 c = x.next();
071 } while(c <= ' ' && c != 0);
072 switch(c)
073 {
074 case 0:
075 return null;
076 case '"':
077 case '\'':
078 return x.nextString(c);
079 case ',':
080 x.back();
081 return "";
082 default:
083 x.back();
084 return x.nextTo(',');
085 }
086 }
087
088 /**
089 * Produce a JSONArray of strings from a row of comma delimited values.
090 *
091 * @param x
092 * A JSONTokener of the source text.
093 * @return A JSONArray of strings.
094 * @throws ParseException
095 */
096 public static JSONArray rowToJSONArray(JSONTokener x)
097 throws ParseException
098 {
099 JSONArray ja = new JSONArray();
100 while(true)
101 {
102 String value = getValue(x);
103 if (value == null) { return null; }
104 ja.put(value);
105 while(true)
106 {
107 char c = x.next();
108 if (c == ',')
109 {
110 break;
111 }
112 if (c != ' ')
113 {
114 if (c == '\n' || c == '\r' || c == 0) { return ja; }
115 throw x.syntaxError("Bad character '" + c + "' (" + (int) c
116 + ").");
117 }
118 }
119 }
120 }
121
122 /**
123 * Produce a JSONObject from a row of comma delimited text, using a parallel
124 * JSONArray of strings to provides the names of the elements.
125 *
126 * @param names
127 * A JSONArray of names. This is commonly obtained from the first
128 * row of a comma delimited text file using the rowToJSONArray
129 * method.
130 * @param x
131 * A JSONTokener of the source text.
132 * @return A JSONObject combining the names and values.
133 * @throws ParseException
134 */
135 public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
136 throws ParseException
137 {
138 JSONArray ja = rowToJSONArray(x);
139 return ja != null ? ja.toJSONObject(names) : null;
140 }
141
142 /**
143 * Produce a JSONArray of JSONObjects from a comma delimited text string,
144 * using the first row as a source of names.
145 *
146 * @param string
147 * The comma delimited text.
148 * @return A JSONArray of JSONObjects.
149 * @throws ParseException
150 */
151 public static JSONArray toJSONArray(String string)
152 throws ParseException
153 {
154 return toJSONArray(new JSONTokener(string));
155 }
156
157 /**
158 * Produce a JSONArray of JSONObjects from a comma delimited text string,
159 * using the first row as a source of names.
160 *
161 * @param x
162 * The JSONTokener containing the comma delimited text.
163 * @return A JSONArray of JSONObjects.
164 * @throws ParseException
165 */
166 public static JSONArray toJSONArray(JSONTokener x)
167 throws ParseException
168 {
169 return toJSONArray(rowToJSONArray(x), x);
170 }
171
172 /**
173 * Produce a JSONArray of JSONObjects from a comma delimited text string
174 * using a supplied JSONArray as the source of element names.
175 *
176 * @param names
177 * A JSONArray of strings.
178 * @param string
179 * The comma delimited text.
180 * @return A JSONArray of JSONObjects.
181 * @throws ParseException
182 */
183 public static JSONArray toJSONArray(JSONArray names, String string)
184 throws ParseException
185 {
186 return toJSONArray(names, new JSONTokener(string));
187 }
188
189 /**
190 * Produce a JSONArray of JSONObjects from a comma delimited text string
191 * using a supplied JSONArray as the source of element names.
192 *
193 * @param names
194 * A JSONArray of strings.
195 * @param x
196 * A JSONTokener of the source text.
197 * @return A JSONArray of JSONObjects.
198 * @throws java.text.ParseException
199 */
200 public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
201 throws java.text.ParseException
202 {
203 if (names == null || names.length() == 0) { return null; }
204 JSONArray ja = new JSONArray();
205 while(true)
206 {
207 JSONObject jo = rowToJSONObject(names, x);
208 if (jo == null)
209 {
210 break;
211 }
212 ja.put(jo);
213 }
214 if (ja.length() == 0) { return null; }
215 return ja;
216 }
217
218 /**
219 * Produce a comma delimited text row from a JSONArray. Values containing
220 * the comma character will be quoted.
221 *
222 * @param ja
223 * A JSONArray of strings.
224 * @return A string ending in NEWLINE.
225 */
226 public static String rowToString(JSONArray ja)
227 {
228 StringBuffer sb = new StringBuffer();
229 for(int i = 0; i < ja.length(); i += 1)
230 {
231 if (i > 0)
232 {
233 sb.append(',');
234 }
235 Object o = ja.opt(i);
236 if (o != null)
237 {
238 String s = o.toString();
239 if (s.indexOf(',') >= 0)
240 {
241 if (s.indexOf('"') >= 0)
242 {
243 sb.append('\'');
244 sb.append(s);
245 sb.append('\'');
246 }
247 else
248 {
249 sb.append('"');
250 sb.append(s);
251 sb.append('"');
252 }
253 }
254 else
255 {
256 sb.append(s);
257 }
258 }
259 }
260 sb.append('\n');
261 return sb.toString();
262
263 }
264
265 /**
266 * Produce a comma delimited text from a JSONArray of JSONObjects. The first
267 * row will be a list of names obtained by inspecting the first JSONObject.
268 *
269 * @param ja
270 * A JSONArray of JSONObjects.
271 * @return A comma delimited text.
272 */
273 public static String toString(JSONArray ja)
274 {
275 JSONObject jo = ja.optJSONObject(0);
276 if (jo != null)
277 {
278 JSONArray names = jo.names();
279 if (names != null) { return rowToString(names)
280 + toString(names, ja); }
281 }
282 return null;
283 }
284
285 /**
286 * Produce a comma delimited text from a JSONArray of JSONObjects using a
287 * provided list of names. The list of names is not included in the output.
288 *
289 * @param names
290 * A JSONArray of strings.
291 * @param ja
292 * A JSONArray of JSONObjects.
293 * @return A comma delimited text.
294 */
295 public static String toString(JSONArray names, JSONArray ja)
296 {
297 if (names == null || names.length() == 0) { return null; }
298 StringBuffer sb = new StringBuffer();
299 for(int i = 0; i < ja.length(); i += 1)
300 {
301 JSONObject jo = ja.optJSONObject(i);
302 if (jo != null)
303 {
304 sb.append(rowToString(jo.toJSONArray(names)));
305 }
306 }
307 return sb.toString();
308 }
309 }