Anonymous avatar Anonymous committed fb1c449

initial version of java client

Comments (0)

Files changed (21)

 priv/log/.*
 ^priv/store
 ^priv/ringstate/*
+^client_lib/java/build/*
+^client_lib/java/dist/*

client_lib/java/Makefile

+
+all: jar
+
+compile:
+	@mkdir -p build
+	@javac -d build `find src -name "*.java"`
+
+jar: compile
+	@mkdir -p dist
+	@jar cvf dist/jiak_client.jar -C build .
+
+clean:
+	@rm -rf build dist

client_lib/java/src/com/basho/riak/JiakClient.java

+/*
+This file is provided to you under the Apache License,
+Version 2.0 (the "License"); you may not use this file
+except in compliance with the License.  You may obtain
+a copy of the License at
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.  
+*/
+
+package com.basho.riak;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import sun.net.www.protocol.http.HttpURLConnection;
+
+public class JiakClient {
+
+	private String ip;
+	private String port;
+	private String prefix;
+	
+	public JiakClient(String ip, String port) {
+		this(ip, port, "/jiak/");
+	}
+	
+	public JiakClient(String ip, String port, String prefix) {
+		this.ip = ip;
+		this.port = port;		
+		this.prefix = prefix;
+	}
+	
+	public void setBucketSchema(String bucket, 
+								List<String> allowedFields, 
+								List<String> writeMask, 
+								List<String> readMask, 
+								List<String> requiredFields) 
+		throws JSONException, IOException, JiakException {
+		
+		if (requiredFields == null) requiredFields = new ArrayList<String>();
+		if (writeMask == null) writeMask = new ArrayList<String>(allowedFields);
+		if (readMask == null) readMask = new ArrayList<String>(allowedFields);
+		
+		JSONObject schema = new JSONObject();
+		schema.put("allowed_fields", allowedFields);
+		schema.put("required_fields", requiredFields);
+		schema.put("read_mask", readMask);
+		schema.put("write_mask", writeMask);
+		JSONObject schemaReqBody = new JSONObject();
+		schemaReqBody.put("schema", schema);
+		HashMap<String, String> reqHeaders = new HashMap<String, String>();
+		reqHeaders.put("Content-Type", "application/json");
+		reqHeaders.put("Accept", "application/json");
+		String reqURI = makeURI(bucket);
+		HttpURLConnection requestConn = doRequest("PUT", 
+				reqURI, schemaReqBody, reqHeaders);
+		if (requestConn.getResponseCode() != 204) {
+			throw new JiakException(requestConn.getResponseMessage());
+		}
+	}
+	
+	public JSONObject listBucket(String bucket) 
+		throws IOException, JSONException, JiakException {
+		HashMap<String, String> reqHeaders = new HashMap<String, String>();
+		reqHeaders.put("Content-Type", "application/json");
+		reqHeaders.put("Accept", "application/json");		
+		HttpURLConnection requestConn = doRequest("GET", makeURI(bucket), null, reqHeaders);
+		return expect(200, requestConn);
+	}
+	
+	public void store(JiakObject object) 
+		throws IOException, JSONException, JiakException {
+		HashMap<String, String> reqHeaders = new HashMap<String, String>();
+		reqHeaders.put("Content-Type", "application/json");
+		reqHeaders.put("Accept", "application/json");
+		String reqURI = makeURI(object.getBucket() + "/" + object.getKey() + "?returnbody=true");
+		HttpURLConnection requestConn = doRequest("PUT", reqURI, object.toJSONObject(), reqHeaders);
+		JSONObject updated = expect(200, requestConn);
+		object.update(updated);
+	}
+	
+	public JiakObject fetch(String bucket, String key) 
+		throws IOException, JSONException, JiakException {
+		HashMap<String, String> reqHeaders = new HashMap<String, String>();
+		reqHeaders.put("Content-Type", "application/json");
+		reqHeaders.put("Accept", "application/json");
+		String reqURI = makeURI(bucket + "/" + key);
+		HttpURLConnection requestConn = doRequest("GET", reqURI, null, reqHeaders);
+		if (requestConn.getResponseCode() == 404) return null;
+		JSONObject objData = expect(200, requestConn);
+		JiakObject object = new JiakObject(bucket, key);
+		object.update(objData);
+		return object;
+	}
+	
+	public void delete(String bucket, String key) throws IOException, JiakException {
+		String reqURI = makeURI(bucket + "/" + key);
+		HashMap<String, String> reqHeaders = new HashMap<String, String>();
+		reqHeaders.put("Accept", "*/*");				
+		HttpURLConnection requestConn = doRequest("DELETE", reqURI, null, reqHeaders);
+		int responseCode = requestConn.getResponseCode();
+		if ((responseCode != 404) && (responseCode != 204)) 
+			throw new JiakException(requestConn.getResponseMessage());
+	}
+	
+	protected HttpURLConnection doRequest(String method, 
+							 			  String uri, 
+							 			  JSONObject body, 
+							 			  Map<String, String> headers) 
+		throws IOException {
+		URL requestURL = new URL(uri);
+		HttpURLConnection requestConn = (HttpURLConnection)requestURL.openConnection();
+		requestConn.setRequestMethod(method);
+		if (headers != null) {
+			for (String k : headers.keySet()) {
+				requestConn.setRequestProperty(k, headers.get(k));
+			}
+		}
+		if (body != null) {
+			requestConn.setDoOutput(true);
+			writeRequestBody(requestConn, body);
+		}
+		return requestConn;
+	}
+
+	protected JSONObject expect(int responseCode, HttpURLConnection connection) 
+		throws JSONException, JiakException, IOException {
+		if (connection.getResponseCode() == responseCode) {
+			String responseBody = readResponseBody(connection);
+			return new JSONObject(responseBody);
+		}
+		throw new JiakException(connection.getResponseMessage());
+	}
+	
+	protected String makeURI(String path) {
+		return "http://" + this.ip + ":" + this.port + this.prefix + path;
+	}
+	
+	protected static void writeRequestBody(URLConnection connection, 
+										   JSONObject body) {
+		OutputStream outputStream;
+		try {
+			outputStream = connection.getOutputStream();
+		} catch (IOException e1) {
+			e1.printStackTrace();
+			return;
+		}
+		OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+		try {
+			outputStreamWriter.write(body.toString());
+			outputStreamWriter.flush();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		finally {
+			try {
+				outputStream.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	protected static String readResponseBody(HttpURLConnection connection)  {
+		String urlContent = null;
+		InputStream inputStream;
+		try {
+			inputStream = connection.getInputStream();
+		} catch (IOException e) {
+			e.printStackTrace();
+			return urlContent;
+		}
+		try {
+			urlContent = downloadStream(inputStream);
+		} 
+		catch (IOException e) {
+			e.printStackTrace();
+		}
+		finally {
+			try {
+				inputStream.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		return urlContent;
+	}
+
+	public static String downloadStream(InputStream in) 
+		throws IOException {
+
+		OutputStream out = new ByteArrayOutputStream();
+	    try {
+	    	copy(in, out);
+	    } finally {
+	    	in.close();
+	    	out.close();
+	    }
+	    return out.toString();
+	     
+	}
+	
+	private static void copy(InputStream in, OutputStream out) 
+		throws IOException {
+		
+		byte[] buffer = new byte[1024];
+	    while (true) {
+	    	int readCount = in.read(buffer);
+	    	if (readCount == -1) {
+	    		break;
+	    	}
+	    	out.write(buffer, 0, readCount);
+	    }
+	}
+}

client_lib/java/src/com/basho/riak/JiakException.java

+/*
+This file is provided to you under the Apache License,
+Version 2.0 (the "License"); you may not use this file
+except in compliance with the License.  You may obtain
+a copy of the License at
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.  
+*/
+
+package com.basho.riak;
+
+public class JiakException extends Exception {
+
+	private static final long serialVersionUID = -4739928528628198267L;
+
+	public JiakException() {}
+
+	public JiakException(String message) {
+		super(message);
+	}
+
+	public JiakException(Throwable cause) {
+		super(cause);
+	}
+
+	public JiakException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+}

client_lib/java/src/com/basho/riak/JiakObject.java

+/*
+This file is provided to you under the Apache License,
+Version 2.0 (the "License"); you may not use this file
+except in compliance with the License.  You may obtain
+a copy of the License at
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.  
+*/
+
+package com.basho.riak;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class JiakObject {
+
+	private String bucket;
+	private String key;
+	private JSONArray links;
+	private JSONObject object;
+	private String vclock;
+	private String lastmod;
+	private String vtag;
+
+	public JiakObject(String bucket, String key) {
+		this(bucket, key, new JSONArray());
+	}
+	
+	public JiakObject(String bucket, String key, JSONArray links) {
+		this(bucket, key, links, new JSONObject());
+	}
+	
+	public JiakObject(String bucket, String key, JSONArray links, JSONObject object) {
+		this.bucket = bucket;
+		this.key = key;
+		this.links = links;
+		this.object = object;
+		this.vclock = null;
+		this.lastmod = null;
+		this.vtag = null;
+	}
+
+	public String getBucket() {
+		return bucket;
+	}
+
+	public void setBucket(String bucket) {
+		this.bucket = bucket;
+	}
+
+	public String getKey() {
+		return key;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public JSONArray getLinks() {
+		return links;
+	}
+
+	public void setLinks(JSONArray links) {
+		this.links = links;
+	}
+
+	public JSONObject getObject() {
+		return object;
+	}
+
+	public void setObject(JSONObject object) {
+		this.object = object;
+	}
+
+	public String getVclock() {
+		return vclock;
+	}
+
+	public void setVclock(String vclock) {
+		this.vclock = vclock;
+	}
+
+	public String getLastmod() {
+		return lastmod;
+	}
+
+	public void setLastmod(String lastmod) {
+		this.lastmod = lastmod;
+	}
+
+	public String getVtag() {
+		return vtag;
+	}
+
+	public void setVtag(String vtag) {
+		this.vtag = vtag;
+	}
+
+	public void update(JSONObject data) throws JSONException {
+		this.vclock = data.getString("vclock");
+		this.lastmod = data.getString("lastmod");
+		this.vtag = data.getString("vtag");
+		this.object = data.getJSONObject("object");
+		this.links = data.getJSONArray("links");
+	}
+
+	public Object get(String key) throws JSONException {
+		return this.object.get(key);
+	}
+	
+	public void set(String key, Object value) throws JSONException {
+		this.object.put(key, value);
+	}
+	
+	public JSONObject toJSONObject() throws JSONException { 
+		JSONObject o = new JSONObject();
+		o.put("vclock", vclock);
+		o.put("lastmod", lastmod);
+		o.put("vtag", vtag);
+		o.put("bucket", bucket);
+		o.put("key", key);
+		o.put("links", links);
+		o.put("object", object);
+		return o;
+	}
+	
+	public String toJSONString() throws JSONException { 
+		return toJSONObject().toString();
+	}
+}

client_lib/java/src/org/json/CDL.java

+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * This provides static methods to convert comma delimited text into a
+ * JSONArray, and to covert a JSONArray into comma delimited text. Comma
+ * delimited text is a very popular format for data interchange. It is
+ * understood by most database, spreadsheet, and organizer programs.
+ * <p>
+ * Each row of text represents a row in a table or a data record. Each row
+ * ends with a NEWLINE character. Each row contains one or more values.
+ * Values are separated by commas. A value can contain any character except
+ * for comma, unless is is wrapped in single quotes or double quotes.
+ * <p>
+ * The first row usually contains the names of the columns.
+ * <p>
+ * A comma delimited list can be converted into a JSONArray of JSONObjects.
+ * The names for the elements in the JSONObjects can be taken from the names
+ * in the first row.
+ * @author JSON.org
+ * @version 2009-06-18
+ */
+public class CDL {
+
+    /**
+     * Get the next value. The value can be wrapped in quotes. The value can
+     * be empty.
+     * @param x A JSONTokener of the source text.
+     * @return The value string, or null if empty.
+     * @throws JSONException if the quoted string is badly formed.
+     */
+    private static String getValue(JSONTokener x) throws JSONException {
+        char c;
+        char q;
+        StringBuffer sb;
+        do {
+            c = x.next();
+        } while (c == ' ' || c == '\t');
+        switch (c) {
+        case 0:
+            return null;
+        case '"':
+        case '\'':
+        	q = c;
+        	sb = new StringBuffer();
+        	for (;;) {
+        		c = x.next();
+        		if (c == q) {
+        			break;
+        		}
+                if (c == 0 || c == '\n' || c == '\r') {
+                    throw x.syntaxError("Missing close quote '" + q + "'.");
+                }
+                sb.append(c);
+        	}
+            return sb.toString();
+        case ',':
+            x.back();
+            return "";
+        default:
+            x.back();
+            return x.nextTo(',');
+        }
+    }
+
+    /**
+     * Produce a JSONArray of strings from a row of comma delimited values.
+     * @param x A JSONTokener of the source text.
+     * @return A JSONArray of strings.
+     * @throws JSONException
+     */
+    public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
+        JSONArray ja = new JSONArray();
+        for (;;) {
+            String value = getValue(x);
+            if (value == null || (ja.length() == 0 && value.length() == 0)) {
+                return null;
+            }
+            ja.put(value);
+            for (;;) {
+                char c = x.next();
+                if (c == ',') {
+                    break;
+                }
+                if (c != ' ') {
+                    if (c == '\n' || c == '\r' || c == 0) {
+                        return ja;
+                    }
+                    throw x.syntaxError("Bad character '" + c + "' (" +
+                            (int)c + ").");
+                }
+            }
+        }
+    }
+
+    /**
+     * Produce a JSONObject from a row of comma delimited text, using a
+     * parallel JSONArray of strings to provides the names of the elements.
+     * @param names A JSONArray of names. This is commonly obtained from the
+     *  first row of a comma delimited text file using the rowToJSONArray
+     *  method.
+     * @param x A JSONTokener of the source text.
+     * @return A JSONObject combining the names and values.
+     * @throws JSONException
+     */
+    public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
+            throws JSONException {
+        JSONArray ja = rowToJSONArray(x);
+        return ja != null ? ja.toJSONObject(names) :  null;
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string,
+     * using the first row as a source of names.
+     * @param string The comma delimited text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(String string) throws JSONException {
+        return toJSONArray(new JSONTokener(string));
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string,
+     * using the first row as a source of names.
+     * @param x The JSONTokener containing the comma delimited text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
+        return toJSONArray(rowToJSONArray(x), x);
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string
+     * using a supplied JSONArray as the source of element names.
+     * @param names A JSONArray of strings.
+     * @param string The comma delimited text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(JSONArray names, String string)
+            throws JSONException {
+        return toJSONArray(names, new JSONTokener(string));
+    }
+
+    /**
+     * Produce a JSONArray of JSONObjects from a comma delimited text string
+     * using a supplied JSONArray as the source of element names.
+     * @param names A JSONArray of strings.
+     * @param x A JSONTokener of the source text.
+     * @return A JSONArray of JSONObjects.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
+            throws JSONException {
+        if (names == null || names.length() == 0) {
+            return null;
+        }
+        JSONArray ja = new JSONArray();
+        for (;;) {
+            JSONObject jo = rowToJSONObject(names, x);
+            if (jo == null) {
+                break;
+            }
+            ja.put(jo);
+        }
+        if (ja.length() == 0) {
+            return null;
+        }
+        return ja;
+    }
+
+
+    /**
+     * Produce a comma delimited text row from a JSONArray. Values containing
+     * the comma character will be quoted. Troublesome characters may be 
+     * removed.
+     * @param ja A JSONArray of strings.
+     * @return A string ending in NEWLINE.
+     */
+    public static String rowToString(JSONArray ja) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < ja.length(); i += 1) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            Object o = ja.opt(i);
+            if (o != null) {
+                String s = o.toString();
+                if (s.indexOf(',') >= 0 || s.indexOf('\n') >= 0 || 
+                		s.indexOf('\r') >= 0 || s.indexOf(0) >= 0 || 
+                		s.charAt(0) == '"') {
+                    sb.append('"');
+                	int length = s.length();
+                	for (int j = 0; j < length; j += 1) {
+                		char c = s.charAt(j);
+                		if (c >= ' ' && c != '"') {
+                			sb.append(c);
+                		}
+                    }
+                    sb.append('"');
+                } else {
+                    sb.append(s);
+                }
+            }
+        }
+        sb.append('\n');
+        return sb.toString();
+    }
+
+    /**
+     * Produce a comma delimited text from a JSONArray of JSONObjects. The
+     * first row will be a list of names obtained by inspecting the first
+     * JSONObject.
+     * @param ja A JSONArray of JSONObjects.
+     * @return A comma delimited text.
+     * @throws JSONException
+     */
+    public static String toString(JSONArray ja) throws JSONException {
+        JSONObject jo = ja.optJSONObject(0);
+        if (jo != null) {
+            JSONArray names = jo.names();
+            if (names != null) {
+                return rowToString(names) + toString(names, ja);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Produce a comma delimited text from a JSONArray of JSONObjects using
+     * a provided list of names. The list of names is not included in the
+     * output.
+     * @param names A JSONArray of strings.
+     * @param ja A JSONArray of JSONObjects.
+     * @return A comma delimited text.
+     * @throws JSONException
+     */
+    public static String toString(JSONArray names, JSONArray ja)
+            throws JSONException {
+        if (names == null || names.length() == 0) {
+            return null;
+        }
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < ja.length(); i += 1) {
+            JSONObject jo = ja.optJSONObject(i);
+            if (jo != null) {
+                sb.append(rowToString(jo.toJSONArray(names)));
+            }
+        }
+        return sb.toString();
+    }
+}

client_lib/java/src/org/json/Cookie.java

+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * Convert a web browser cookie specification to a JSONObject and back.
+ * JSON and Cookies are both notations for name/value pairs.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class Cookie {
+
+    /**
+     * Produce a copy of a string in which the characters '+', '%', '=', ';'
+     * and control characters are replaced with "%hh". This is a gentle form
+     * of URL encoding, attempting to cause as little distortion to the
+     * string as possible. The characters '=' and ';' are meta characters in
+     * cookies. By convention, they are escaped using the URL-encoding. This is
+     * only a convention, not a standard. Often, cookies are expected to have
+     * encoded values. We encode '=' and ';' because we must. We encode '%' and
+     * '+' because they are meta characters in URL encoding.
+     * @param string The source string.
+     * @return       The escaped result.
+     */
+    public static String escape(String string) {
+        char         c;
+        String       s = string.trim();
+        StringBuffer sb = new StringBuffer();
+        int          len = s.length();
+        for (int i = 0; i < len; i += 1) {
+            c = s.charAt(i);
+            if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
+                sb.append('%');
+                sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
+                sb.append(Character.forDigit((char)(c & 0x0f), 16));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * Convert a cookie specification string into a JSONObject. The string
+     * will contain a name value pair separated by '='. The name and the value
+     * will be unescaped, possibly converting '+' and '%' sequences. The
+     * cookie properties may follow, separated by ';', also represented as
+     * name=value (except the secure property, which does not have a value).
+     * The name will be stored under the key "name", and the value will be
+     * stored under the key "value". This method does not do checking or
+     * validation of the parameters. It only converts the cookie string into
+     * a JSONObject.
+     * @param string The cookie specification string.
+     * @return A JSONObject containing "name", "value", and possibly other
+     *  members.
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(String string) throws JSONException {
+        String         n;
+        JSONObject     o = new JSONObject();
+        Object         v;
+        JSONTokener x = new JSONTokener(string);
+        o.put("name", x.nextTo('='));
+        x.next('=');
+        o.put("value", x.nextTo(';'));
+        x.next();
+        while (x.more()) {
+            n = unescape(x.nextTo("=;"));
+            if (x.next() != '=') {
+                if (n.equals("secure")) {
+                    v = Boolean.TRUE;
+                } else {
+                    throw x.syntaxError("Missing '=' in cookie parameter.");
+                }
+            } else {
+                v = unescape(x.nextTo(';'));
+                x.next();
+            }
+            o.put(n, v);
+        }
+        return o;
+    }
+
+
+    /**
+     * Convert a JSONObject into a cookie specification string. The JSONObject
+     * must contain "name" and "value" members.
+     * If the JSONObject contains "expires", "domain", "path", or "secure"
+     * members, they will be appended to the cookie specification string.
+     * All other members are ignored.
+     * @param o A JSONObject
+     * @return A cookie specification string
+     * @throws JSONException
+     */
+    public static String toString(JSONObject o) throws JSONException {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append(escape(o.getString("name")));
+        sb.append("=");
+        sb.append(escape(o.getString("value")));
+        if (o.has("expires")) {
+            sb.append(";expires=");
+            sb.append(o.getString("expires"));
+        }
+        if (o.has("domain")) {
+            sb.append(";domain=");
+            sb.append(escape(o.getString("domain")));
+        }
+        if (o.has("path")) {
+            sb.append(";path=");
+            sb.append(escape(o.getString("path")));
+        }
+        if (o.optBoolean("secure")) {
+            sb.append(";secure");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Convert <code>%</code><i>hh</i> sequences to single characters, and
+     * convert plus to space.
+     * @param s A string that may contain
+     *      <code>+</code>&nbsp;<small>(plus)</small> and
+     *      <code>%</code><i>hh</i> sequences.
+     * @return The unescaped string.
+     */
+    public static String unescape(String s) {
+        int len = s.length();
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < len; ++i) {
+            char c = s.charAt(i);
+            if (c == '+') {
+                c = ' ';
+            } else if (c == '%' && i + 2 < len) {
+                int d = JSONTokener.dehexchar(s.charAt(i + 1));
+                int e = JSONTokener.dehexchar(s.charAt(i + 2));
+                if (d >= 0 && e >= 0) {
+                    c = (char)(d * 16 + e);
+                    i += 2;
+                }
+            }
+            b.append(c);
+        }
+        return b.toString();
+    }
+}

client_lib/java/src/org/json/CookieList.java

+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.util.Iterator;
+
+/**
+ * Convert a web browser cookie list string to a JSONObject and back.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class CookieList {
+
+    /**
+     * Convert a cookie list into a JSONObject. A cookie list is a sequence
+     * of name/value pairs. The names are separated from the values by '='.
+     * The pairs are separated by ';'. The names and the values
+     * will be unescaped, possibly converting '+' and '%' sequences.
+     *
+     * To add a cookie to a cooklist,
+     * cookielistJSONObject.put(cookieJSONObject.getString("name"),
+     *     cookieJSONObject.getString("value"));
+     * @param string  A cookie list string
+     * @return A JSONObject
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(String string) throws JSONException {
+        JSONObject o = new JSONObject();
+        JSONTokener x = new JSONTokener(string);
+        while (x.more()) {
+            String name = Cookie.unescape(x.nextTo('='));
+            x.next('=');
+            o.put(name, Cookie.unescape(x.nextTo(';')));
+            x.next();
+        }
+        return o;
+    }
+
+
+    /**
+     * Convert a JSONObject into a cookie list. A cookie list is a sequence
+     * of name/value pairs. The names are separated from the values by '='.
+     * The pairs are separated by ';'. The characters '%', '+', '=', and ';'
+     * in the names and values are replaced by "%hh".
+     * @param o A JSONObject
+     * @return A cookie list string
+     * @throws JSONException
+     */
+    public static String toString(JSONObject o) throws JSONException {
+        boolean      b = false;
+        Iterator     keys = o.keys();
+        String       s;
+        StringBuffer sb = new StringBuffer();
+        while (keys.hasNext()) {
+            s = keys.next().toString();
+            if (!o.isNull(s)) {
+                if (b) {
+                    sb.append(';');
+                }
+                sb.append(Cookie.escape(s));
+                sb.append("=");
+                sb.append(Cookie.escape(o.getString(s)));
+                b = true;
+            }
+        }
+        return sb.toString();
+    }
+}

client_lib/java/src/org/json/HTTP.java

+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.util.Iterator;
+
+/**
+ * Convert an HTTP header to a JSONObject and back.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class HTTP {
+
+    /** Carriage return/line feed. */
+    public static final String CRLF = "\r\n";
+
+    /**
+     * Convert an HTTP header string into a JSONObject. It can be a request
+     * header or a response header. A request header will contain
+     * <pre>{
+     *    Method: "POST" (for example),
+     *    "Request-URI": "/" (for example),
+     *    "HTTP-Version": "HTTP/1.1" (for example)
+     * }</pre>
+     * A response header will contain
+     * <pre>{
+     *    "HTTP-Version": "HTTP/1.1" (for example),
+     *    "Status-Code": "200" (for example),
+     *    "Reason-Phrase": "OK" (for example)
+     * }</pre>
+     * In addition, the other parameters in the header will be captured, using
+     * the HTTP field names as JSON names, so that <pre>
+     *    Date: Sun, 26 May 2002 18:06:04 GMT
+     *    Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
+     *    Cache-Control: no-cache</pre>
+     * become
+     * <pre>{...
+     *    Date: "Sun, 26 May 2002 18:06:04 GMT",
+     *    Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
+     *    "Cache-Control": "no-cache",
+     * ...}</pre>
+     * It does no further checking or conversion. It does not parse dates.
+     * It does not do '%' transforms on URLs.
+     * @param string An HTTP header string.
+     * @return A JSONObject containing the elements and attributes
+     * of the XML string.
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(String string) throws JSONException {
+        JSONObject     o = new JSONObject();
+        HTTPTokener    x = new HTTPTokener(string);
+        String         t;
+
+        t = x.nextToken();
+        if (t.toUpperCase().startsWith("HTTP")) {
+
+// Response
+
+            o.put("HTTP-Version", t);
+            o.put("Status-Code", x.nextToken());
+            o.put("Reason-Phrase", x.nextTo('\0'));
+            x.next();
+
+        } else {
+
+// Request
+
+            o.put("Method", t);
+            o.put("Request-URI", x.nextToken());
+            o.put("HTTP-Version", x.nextToken());
+        }
+
+// Fields
+
+        while (x.more()) {
+            String name = x.nextTo(':');
+            x.next(':');
+            o.put(name, x.nextTo('\0'));
+            x.next();
+        }
+        return o;
+    }
+
+
+    /**
+     * Convert a JSONObject into an HTTP header. A request header must contain
+     * <pre>{
+     *    Method: "POST" (for example),
+     *    "Request-URI": "/" (for example),
+     *    "HTTP-Version": "HTTP/1.1" (for example)
+     * }</pre>
+     * A response header must contain
+     * <pre>{
+     *    "HTTP-Version": "HTTP/1.1" (for example),
+     *    "Status-Code": "200" (for example),
+     *    "Reason-Phrase": "OK" (for example)
+     * }</pre>
+     * Any other members of the JSONObject will be output as HTTP fields.
+     * The result will end with two CRLF pairs.
+     * @param o A JSONObject
+     * @return An HTTP header string.
+     * @throws JSONException if the object does not contain enough
+     *  information.
+     */
+    public static String toString(JSONObject o) throws JSONException {
+        Iterator     keys = o.keys();
+        String       s;
+        StringBuffer sb = new StringBuffer();
+        if (o.has("Status-Code") && o.has("Reason-Phrase")) {
+            sb.append(o.getString("HTTP-Version"));
+            sb.append(' ');
+            sb.append(o.getString("Status-Code"));
+            sb.append(' ');
+            sb.append(o.getString("Reason-Phrase"));
+        } else if (o.has("Method") && o.has("Request-URI")) {
+            sb.append(o.getString("Method"));
+            sb.append(' ');
+            sb.append('"');
+            sb.append(o.getString("Request-URI"));
+            sb.append('"');
+            sb.append(' ');
+            sb.append(o.getString("HTTP-Version"));
+        } else {
+            throw new JSONException("Not enough material for an HTTP header.");
+        }
+        sb.append(CRLF);
+        while (keys.hasNext()) {
+            s = keys.next().toString();
+            if (!s.equals("HTTP-Version")      && !s.equals("Status-Code") &&
+                    !s.equals("Reason-Phrase") && !s.equals("Method") &&
+                    !s.equals("Request-URI")   && !o.isNull(s)) {
+                sb.append(s);
+                sb.append(": ");
+                sb.append(o.getString(s));
+                sb.append(CRLF);
+            }
+        }
+        sb.append(CRLF);
+        return sb.toString();
+    }
+}

client_lib/java/src/org/json/HTTPTokener.java

+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * The HTTPTokener extends the JSONTokener to provide additional methods
+ * for the parsing of HTTP headers.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class HTTPTokener extends JSONTokener {
+
+    /**
+     * Construct an HTTPTokener from a string.
+     * @param s A source string.
+     */
+    public HTTPTokener(String s) {
+        super(s);
+    }
+
+
+    /**
+     * Get the next token or string. This is used in parsing HTTP headers.
+     * @throws JSONException
+     * @return A String.
+     */
+    public String nextToken() throws JSONException {
+        char c;
+        char q;
+        StringBuffer sb = new StringBuffer();
+        do {
+            c = next();
+        } while (Character.isWhitespace(c));
+        if (c == '"' || c == '\'') {
+            q = c;
+            for (;;) {
+                c = next();
+                if (c < ' ') {
+                    throw syntaxError("Unterminated string.");
+                }
+                if (c == q) {
+                    return sb.toString();
+                }
+                sb.append(c);
+            }
+        } 
+        for (;;) {
+            if (c == 0 || Character.isWhitespace(c)) {
+                return sb.toString();
+            }
+            sb.append(c);
+            c = next();
+        }
+    }
+}

client_lib/java/src/org/json/JSONArray.java

+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A JSONArray is an ordered sequence of values. Its external text form is a
+ * string wrapped in square brackets with commas separating the values. The
+ * internal form is an object having <code>get</code> and <code>opt</code>
+ * methods for accessing the values by index, and <code>put</code> methods for
+ * adding or replacing values. The values can be any of these types:
+ * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
+ * <code>Number</code>, <code>String</code>, or the
+ * <code>JSONObject.NULL object</code>.
+ * <p>
+ * The constructor can convert a JSON text into a Java object. The
+ * <code>toString</code> method converts to JSON text.
+ * <p>
+ * A <code>get</code> method returns a value if one can be found, and throws an
+ * exception if one cannot be found. An <code>opt</code> method returns a
+ * default value instead of throwing an exception, and so is useful for
+ * obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * JSON syntax rules. The constructors are more forgiving in the texts they will
+ * accept:
+ * <ul>
+ * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
+ *     before the closing bracket.</li>
+ * <li>The <code>null</code> value will be inserted when there
+ *     is <code>,</code>&nbsp;<small>(comma)</small> elision.</li>
+ * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
+ *     quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ *     or single quote, and if they do not contain leading or trailing spaces,
+ *     and if they do not contain any of these characters:
+ *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
+ *     and if they are not the reserved words <code>true</code>,
+ *     <code>false</code>, or <code>null</code>.</li>
+ * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
+ *     well as by <code>,</code> <small>(comma)</small>.</li>
+ * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
+ *     <code>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+
+ * @author JSON.org
+ * @version 2009-04-13
+ */
+public class JSONArray {
+
+
+    /**
+     * The arrayList where the JSONArray's properties are kept.
+     */
+    private ArrayList myArrayList;
+
+
+    /**
+     * Construct an empty JSONArray.
+     */
+    public JSONArray() {
+        this.myArrayList = new ArrayList();
+    }
+
+    /**
+     * Construct a JSONArray from a JSONTokener.
+     * @param x A JSONTokener
+     * @throws JSONException If there is a syntax error.
+     */
+    public JSONArray(JSONTokener x) throws JSONException {
+        this();
+        char c = x.nextClean();
+        char q;
+        if (c == '[') {
+            q = ']';
+        } else if (c == '(') {
+            q = ')';
+        } else {
+            throw x.syntaxError("A JSONArray text must start with '['");
+        }
+        if (x.nextClean() == ']') {
+            return;
+        }
+        x.back();
+        for (;;) {
+            if (x.nextClean() == ',') {
+                x.back();
+                this.myArrayList.add(null);
+            } else {
+                x.back();
+                this.myArrayList.add(x.nextValue());
+            }
+            c = x.nextClean();
+            switch (c) {
+            case ';':
+            case ',':
+                if (x.nextClean() == ']') {
+                    return;
+                }
+                x.back();
+                break;
+            case ']':
+            case ')':
+                if (q != c) {
+                    throw x.syntaxError("Expected a '" + new Character(q) + "'");
+                }
+                return;
+            default:
+                throw x.syntaxError("Expected a ',' or ']'");
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONArray from a source JSON text.
+     * @param source     A string that begins with
+     * <code>[</code>&nbsp;<small>(left bracket)</small>
+     *  and ends with <code>]</code>&nbsp;<small>(right bracket)</small>.
+     *  @throws JSONException If there is a syntax error.
+     */
+    public JSONArray(String source) throws JSONException {
+        this(new JSONTokener(source));
+    }
+
+
+    /**
+     * Construct a JSONArray from a Collection.
+     * @param collection     A Collection.
+     */
+    public JSONArray(Collection collection) {
+        this.myArrayList = (collection == null) ?
+            new ArrayList() :
+            new ArrayList(collection);
+    }
+
+    /**
+     * Construct a JSONArray from a collection of beans.
+     * The collection should have Java Beans.
+     * 
+     * @throws JSONException If not an array.
+     */
+
+    public JSONArray(Collection collection, boolean includeSuperClass) {
+		this.myArrayList = new ArrayList();
+		if (collection != null) {
+			Iterator iter = collection.iterator();;
+			while (iter.hasNext()) {
+			    Object o = iter.next();
+			    if (o instanceof Map) {
+			    	this.myArrayList.add(new JSONObject((Map)o, includeSuperClass));
+			    } else if (!JSONObject.isStandardProperty(o.getClass())) {
+			    	this.myArrayList.add(new JSONObject(o, includeSuperClass));
+			    } else {
+                    this.myArrayList.add(o);  
+				}
+			}
+		}
+    }
+
+    
+    /**
+     * Construct a JSONArray from an array
+     * @throws JSONException If not an array.
+     */
+    public JSONArray(Object array) throws JSONException {
+        this();
+        if (array.getClass().isArray()) {
+            int length = Array.getLength(array);
+            for (int i = 0; i < length; i += 1) {
+                this.put(Array.get(array, i));
+            }
+        } else {
+            throw new JSONException("JSONArray initial value should be a string or collection or array.");
+        }
+    }
+
+    /**
+     * Construct a JSONArray from an array with a bean.
+     * The array should have Java Beans.
+     * 
+     * @throws JSONException If not an array.
+     */
+    public JSONArray(Object array,boolean includeSuperClass) throws JSONException {
+        this();
+        if (array.getClass().isArray()) {
+            int length = Array.getLength(array);
+            for (int i = 0; i < length; i += 1) {
+                Object o = Array.get(array, i);
+                if (JSONObject.isStandardProperty(o.getClass())) {
+                    this.myArrayList.add(o);  
+                } else {
+                    this.myArrayList.add(new JSONObject(o,includeSuperClass));  
+                }
+            }
+        } else {
+            throw new JSONException("JSONArray initial value should be a string or collection or array.");
+        }
+    }
+
+    
+    
+    /**
+     * Get the object value associated with an index.
+     * @param index
+     *  The index must be between 0 and length() - 1.
+     * @return An object value.
+     * @throws JSONException If there is no value for the index.
+     */
+    public Object get(int index) throws JSONException {
+        Object o = opt(index);
+        if (o == null) {
+            throw new JSONException("JSONArray[" + index + "] not found.");
+        }
+        return o;
+    }
+
+
+    /**
+     * Get the boolean value associated with an index.
+     * The string values "true" and "false" are converted to boolean.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The truth.
+     * @throws JSONException If there is no value for the index or if the
+     *  value is not convertable to boolean.
+     */
+    public boolean getBoolean(int index) throws JSONException {
+        Object o = get(index);
+        if (o.equals(Boolean.FALSE) ||
+                (o instanceof String &&
+                ((String)o).equalsIgnoreCase("false"))) {
+            return false;
+        } else if (o.equals(Boolean.TRUE) ||
+                (o instanceof String &&
+                ((String)o).equalsIgnoreCase("true"))) {
+            return true;
+        }
+        throw new JSONException("JSONArray[" + index + "] is not a Boolean.");
+    }
+
+
+    /**
+     * Get the double value associated with an index.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     * @throws   JSONException If the key is not found or if the value cannot
+     *  be converted to a number.
+     */
+    public double getDouble(int index) throws JSONException {
+        Object o = get(index);
+        try {
+            return o instanceof Number ?
+                ((Number)o).doubleValue() :
+                Double.valueOf((String)o).doubleValue();
+        } catch (Exception e) {
+            throw new JSONException("JSONArray[" + index +
+                "] is not a number.");
+        }
+    }
+
+
+    /**
+     * Get the int value associated with an index.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     * @throws   JSONException If the key is not found or if the value cannot
+     *  be converted to a number.
+     *  if the value cannot be converted to a number.
+     */
+    public int getInt(int index) throws JSONException {
+        Object o = get(index);
+        return o instanceof Number ?
+                ((Number)o).intValue() : (int)getDouble(index);
+    }
+
+
+    /**
+     * Get the JSONArray associated with an index.
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A JSONArray value.
+     * @throws JSONException If there is no value for the index. or if the
+     * value is not a JSONArray
+     */
+    public JSONArray getJSONArray(int index) throws JSONException {
+        Object o = get(index);
+        if (o instanceof JSONArray) {
+            return (JSONArray)o;
+        }
+        throw new JSONException("JSONArray[" + index +
+                "] is not a JSONArray.");
+    }
+
+
+    /**
+     * Get the JSONObject associated with an index.
+     * @param index subscript
+     * @return      A JSONObject value.
+     * @throws JSONException If there is no value for the index or if the
+     * value is not a JSONObject
+     */
+    public JSONObject getJSONObject(int index) throws JSONException {
+        Object o = get(index);
+        if (o instanceof JSONObject) {
+            return (JSONObject)o;
+        }
+        throw new JSONException("JSONArray[" + index +
+            "] is not a JSONObject.");
+    }
+
+
+    /**
+     * Get the long value associated with an index.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     * @throws   JSONException If the key is not found or if the value cannot
+     *  be converted to a number.
+     */
+    public long getLong(int index) throws JSONException {
+        Object o = get(index);
+        return o instanceof Number ?
+                ((Number)o).longValue() : (long)getDouble(index);
+    }
+
+
+    /**
+     * Get the string associated with an index.
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A string value.
+     * @throws JSONException If there is no value for the index.
+     */
+    public String getString(int index) throws JSONException {
+        return get(index).toString();
+    }
+
+
+    /**
+     * Determine if the value is null.
+     * @param index The index must be between 0 and length() - 1.
+     * @return true if the value at the index is null, or if there is no value.
+     */
+    public boolean isNull(int index) {
+        return JSONObject.NULL.equals(opt(index));
+    }
+
+
+    /**
+     * Make a string from the contents of this JSONArray. The
+     * <code>separator</code> string is inserted between each element.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param separator A string that will be inserted between the elements.
+     * @return a string.
+     * @throws JSONException If the array contains an invalid number.
+     */
+    public String join(String separator) throws JSONException {
+        int len = length();
+        StringBuffer sb = new StringBuffer();
+
+        for (int i = 0; i < len; i += 1) {
+            if (i > 0) {
+                sb.append(separator);
+            }
+            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * Get the number of elements in the JSONArray, included nulls.
+     *
+     * @return The length (or size).
+     */
+    public int length() {
+        return this.myArrayList.size();
+    }
+
+
+    /**
+     * Get the optional object value associated with an index.
+     * @param index The index must be between 0 and length() - 1.
+     * @return      An object value, or null if there is no
+     *              object at that index.
+     */
+    public Object opt(int index) {
+        return (index < 0 || index >= length()) ?
+            null : this.myArrayList.get(index);
+    }
+
+
+    /**
+     * Get the optional boolean value associated with an index.
+     * It returns false if there is no value at that index,
+     * or if the value is not Boolean.TRUE or the String "true".
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The truth.
+     */
+    public boolean optBoolean(int index)  {
+        return optBoolean(index, false);
+    }
+
+
+    /**
+     * Get the optional boolean value associated with an index.
+     * It returns the defaultValue if there is no value at that index or if
+     * it is not a Boolean or the String "true" or "false" (case insensitive).
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     A boolean default.
+     * @return      The truth.
+     */
+    public boolean optBoolean(int index, boolean defaultValue)  {
+        try {
+            return getBoolean(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional double value associated with an index.
+     * NaN is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     */
+    public double optDouble(int index) {
+        return optDouble(index, Double.NaN);
+    }
+
+
+    /**
+     * Get the optional double value associated with an index.
+     * The defaultValue is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index subscript
+     * @param defaultValue     The default value.
+     * @return      The value.
+     */
+    public double optDouble(int index, double defaultValue) {
+        try {
+            return getDouble(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional int value associated with an index.
+     * Zero is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     */
+    public int optInt(int index) {
+        return optInt(index, 0);
+    }
+
+
+    /**
+     * Get the optional int value associated with an index.
+     * The defaultValue is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     The default value.
+     * @return      The value.
+     */
+    public int optInt(int index, int defaultValue) {
+        try {
+            return getInt(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional JSONArray associated with an index.
+     * @param index subscript
+     * @return      A JSONArray value, or null if the index has no value,
+     * or if the value is not a JSONArray.
+     */
+    public JSONArray optJSONArray(int index) {
+        Object o = opt(index);
+        return o instanceof JSONArray ? (JSONArray)o : null;
+    }
+
+
+    /**
+     * Get the optional JSONObject associated with an index.
+     * Null is returned if the key is not found, or null if the index has
+     * no value, or if the value is not a JSONObject.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A JSONObject value.
+     */
+    public JSONObject optJSONObject(int index) {
+        Object o = opt(index);
+        return o instanceof JSONObject ? (JSONObject)o : null;
+    }
+
+
+    /**
+     * Get the optional long value associated with an index.
+     * Zero is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      The value.
+     */
+    public long optLong(int index) {
+        return optLong(index, 0);
+    }
+
+
+    /**
+     * Get the optional long value associated with an index.
+     * The defaultValue is returned if there is no value for the index,
+     * or if the value is not a number and cannot be converted to a number.
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     The default value.
+     * @return      The value.
+     */
+    public long optLong(int index, long defaultValue) {
+        try {
+            return getLong(index);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get the optional string value associated with an index. It returns an
+     * empty string if there is no value at that index. If the value
+     * is not a string and is not null, then it is coverted to a string.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @return      A String value.
+     */
+    public String optString(int index) {
+        return optString(index, "");
+    }
+
+
+    /**
+     * Get the optional string associated with an index.
+     * The defaultValue is returned if the key is not found.
+     *
+     * @param index The index must be between 0 and length() - 1.
+     * @param defaultValue     The default value.
+     * @return      A String value.
+     */
+    public String optString(int index, String defaultValue) {
+        Object o = opt(index);
+        return o != null ? o.toString() : defaultValue;
+    }
+
+
+    /**
+     * Append a boolean value. This increases the array's length by one.
+     *
+     * @param value A boolean value.
+     * @return this.
+     */
+    public JSONArray put(boolean value) {
+        put(value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONArray which is produced from a Collection.
+     * @param value A Collection value.
+     * @return      this.
+     */
+    public JSONArray put(Collection value) {
+        put(new JSONArray(value));
+        return this;
+    }
+
+
+    /**
+     * Append a double value. This increases the array's length by one.
+     *
+     * @param value A double value.
+     * @throws JSONException if the value is not finite.
+     * @return this.
+     */
+    public JSONArray put(double value) throws JSONException {
+        Double d = new Double(value);
+        JSONObject.testValidity(d);
+        put(d);
+        return this;
+    }
+
+
+    /**
+     * Append an int value. This increases the array's length by one.
+     *
+     * @param value An int value.
+     * @return this.
+     */
+    public JSONArray put(int value) {
+        put(new Integer(value));
+        return this;
+    }
+
+
+    /**
+     * Append an long value. This increases the array's length by one.
+     *
+     * @param value A long value.
+     * @return this.
+     */
+    public JSONArray put(long value) {
+        put(new Long(value));
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONObject which is produced from a Map.
+     * @param value A Map value.
+     * @return      this.
+     */
+    public JSONArray put(Map value) {
+        put(new JSONObject(value));
+        return this;
+    }
+
+
+    /**
+     * Append an object value. This increases the array's length by one.
+     * @param value An object value.  The value should be a
+     *  Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
+     *  JSONObject.NULL object.
+     * @return this.
+     */
+    public JSONArray put(Object value) {
+        this.myArrayList.add(value);
+        return this;
+    }
+
+
+    /**
+     * Put or replace a boolean value in the JSONArray. If the index is greater
+     * than the length of the JSONArray, then null elements will be added as
+     * necessary to pad it out.
+     * @param index The subscript.
+     * @param value A boolean value.
+     * @return this.
+     * @throws JSONException If the index is negative.
+     */
+    public JSONArray put(int index, boolean value) throws JSONException {
+        put(index, value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONArray which is produced from a Collection.
+     * @param index The subscript.
+     * @param value A Collection value.
+     * @return      this.
+     * @throws JSONException If the index is negative or if the value is
+     * not finite.
+     */
+    public JSONArray put(int index, Collection value) throws JSONException {
+        put(index, new JSONArray(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace a double value. If the index is greater than the length of
+     *  the JSONArray, then null elements will be added as necessary to pad
+     *  it out.
+     * @param index The subscript.
+     * @param value A double value.
+     * @return this.
+     * @throws JSONException If the index is negative or if the value is
+     * not finite.
+     */
+    public JSONArray put(int index, double value) throws JSONException {
+        put(index, new Double(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace an int value. If the index is greater than the length of
+     *  the JSONArray, then null elements will be added as necessary to pad
+     *  it out.
+     * @param index The subscript.
+     * @param value An int value.
+     * @return this.
+     * @throws JSONException If the index is negative.
+     */
+    public JSONArray put(int index, int value) throws JSONException {
+        put(index, new Integer(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace a long value. If the index is greater than the length of
+     *  the JSONArray, then null elements will be added as necessary to pad
+     *  it out.
+     * @param index The subscript.
+     * @param value A long value.
+     * @return this.
+     * @throws JSONException If the index is negative.
+     */
+    public JSONArray put(int index, long value) throws JSONException {
+        put(index, new Long(value));
+        return this;
+    }
+
+
+    /**
+     * Put a value in the JSONArray, where the value will be a
+     * JSONObject which is produced from a Map.
+     * @param index The subscript.
+     * @param value The Map value.
+     * @return      this.
+     * @throws JSONException If the index is negative or if the the value is
+     *  an invalid number.
+     */
+    public JSONArray put(int index, Map value) throws JSONException {
+        put(index, new JSONObject(value));
+        return this;
+    }
+
+
+    /**
+     * Put or replace an object value in the JSONArray. If the index is greater
+     *  than the length of the JSONArray, then null elements will be added as
+     *  necessary to pad it out.
+     * @param index The subscript.
+     * @param value The value to put into the array. The value should be a
+     *  Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
+     *  JSONObject.NULL object.
+     * @return this.
+     * @throws JSONException If the index is negative or if the the value is
+     *  an invalid number.
+     */
+    public JSONArray put(int index, Object value) throws JSONException {
+        JSONObject.testValidity(value);
+        if (index < 0) {
+            throw new JSONException("JSONArray[" + index + "] not found.");
+        }
+        if (index < length()) {
+            this.myArrayList.set(index, value);
+        } else {
+            while (index != length()) {
+                put(JSONObject.NULL);
+            }
+            put(value);
+        }
+        return this;
+    }
+    
+    
+    /**
+     * Remove a index and close the hole.
+     * @param index The index of the element to be removed.
+     * @return The value that was associated with the index,
+     * or null if there was no value.
+     */
+    public Object remove(int index) {
+    	Object o = opt(index);
+        this.myArrayList.remove(index);
+        return o;
+    }
+
+
+    /**
+     * Produce a JSONObject by combining a JSONArray of names with the values
+     * of this JSONArray.
+     * @param names A JSONArray containing a list of key strings. These will be
+     * paired with the values.
+     * @return A JSONObject, or null if there are no names or if this JSONArray
+     * has no values.
+     * @throws JSONException If any of the names are null.
+     */