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