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