[infinispan-commits] Infinispan SVN: r1730 - in trunk/server: websocket and 20 other directories.

infinispan-commits at lists.jboss.org infinispan-commits at lists.jboss.org
Wed Apr 28 10:43:26 EDT 2010


Author: tfennelly
Date: 2010-04-28 10:43:23 -0400 (Wed, 28 Apr 2010)
New Revision: 1730

Added:
   trunk/server/websocket/
   trunk/server/websocket/README.html
   trunk/server/websocket/gui-demo/
   trunk/server/websocket/gui-demo/gui-demo.html
   trunk/server/websocket/gui-demo/run-websocket-server.sh
   trunk/server/websocket/infinispan.xml
   trunk/server/websocket/pom.xml
   trunk/server/websocket/src/
   trunk/server/websocket/src/main/
   trunk/server/websocket/src/main/java/
   trunk/server/websocket/src/main/java/org/
   trunk/server/websocket/src/main/java/org/infinispan/
   trunk/server/websocket/src/main/java/org/infinispan/websocket/
   trunk/server/websocket/src/main/java/org/infinispan/websocket/CacheListener.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/ChannelUtils.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/OpHandler.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServer.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServerHandler.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/
   trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/GetHandler.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/NotifyHandler.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/PutHandler.java
   trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/RemoveHandler.java
   trunk/server/websocket/src/main/java/org/json/
   trunk/server/websocket/src/main/java/org/json/CDL.java
   trunk/server/websocket/src/main/java/org/json/Cookie.java
   trunk/server/websocket/src/main/java/org/json/CookieList.java
   trunk/server/websocket/src/main/java/org/json/HTTP.java
   trunk/server/websocket/src/main/java/org/json/HTTPTokener.java
   trunk/server/websocket/src/main/java/org/json/JSONArray.java
   trunk/server/websocket/src/main/java/org/json/JSONException.java
   trunk/server/websocket/src/main/java/org/json/JSONML.java
   trunk/server/websocket/src/main/java/org/json/JSONObject.java
   trunk/server/websocket/src/main/java/org/json/JSONString.java
   trunk/server/websocket/src/main/java/org/json/JSONStringer.java
   trunk/server/websocket/src/main/java/org/json/JSONTokener.java
   trunk/server/websocket/src/main/java/org/json/JSONWriter.java
   trunk/server/websocket/src/main/java/org/json/Test.java
   trunk/server/websocket/src/main/java/org/json/XML.java
   trunk/server/websocket/src/main/java/org/json/XMLTokener.java
   trunk/server/websocket/src/main/resources/
   trunk/server/websocket/src/main/resources/org/
   trunk/server/websocket/src/main/resources/org/infinispan/
   trunk/server/websocket/src/main/resources/org/infinispan/websocket/
   trunk/server/websocket/src/main/resources/org/infinispan/websocket/infinispan-ws.js
   trunk/server/websocket/src/test/
   trunk/server/websocket/src/test/java/
   trunk/server/websocket/src/test/java/org/
   trunk/server/websocket/src/test/java/org/infinispan/
   trunk/server/websocket/src/test/java/org/infinispan/websocket/
   trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannel.java
   trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannelHandlerContext.java
   trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/
   trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/MockClient.java
   trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/OpHandlerTest.java
   trunk/server/websocket/src/test/resources/
   trunk/server/websocket/src/test/resources/query-cache.html
Log:
[ISPN-405] WebSocket Server

Added: trunk/server/websocket/README.html
===================================================================
--- trunk/server/websocket/README.html	                        (rev 0)
+++ trunk/server/websocket/README.html	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1 @@
+<a href="http://www.screencast.com/t/OGU2NWZl">See Screencast &gt;&gt;</a>
\ No newline at end of file

Added: trunk/server/websocket/gui-demo/gui-demo.html
===================================================================
--- trunk/server/websocket/gui-demo/gui-demo.html	                        (rev 0)
+++ trunk/server/websocket/gui-demo/gui-demo.html	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,44 @@
+<html>
+	<head>
+		<title>Infinispan Cache Query</title>
+		
+		<script type="text/javascript" src="http://localhost:61999/infinispan-ws.js" />
+		<script type="text/javascript">
+			<!--
+            var cache = new Cache();
+            var curKey;
+
+            cache.registerCallback(cacheCallback); 
+
+			function cacheCallback(key, value) {
+				if(key == curKey) {
+					var node = document.getElementById("val");				
+					node.value = value;
+				}
+			}
+
+			function keyChange(newKey) {
+				// turn off notification for old key...
+				cache.unnotify(curKey);
+				// turn on notification of new key...
+				cache.notify(newKey);
+				// record the new curkey so we can turn off notification on next keychange... 
+				curKey = newKey;
+			}
+			-->
+		</script>
+	</head>
+	<body>
+		<form onsubmit="return false;">
+		     Key: 
+		     <input type="text" id="key" onchange="keyChange(this.form.key.value)" /> 
+		     Value: 
+		     <input type="text" id="val" /><br/>
+		     
+			 <input type="button" value="Put" onclick="cache.put(this.form.key.value, this.form.val.value)" />
+			 <input type="button" value="Get" onclick="cache.get(this.form.key.value)" />
+			 <input type="button" value="Remove" onclick="cache.remove(this.form.key.value)" />
+		</form>
+		
+	</body>
+</html>

Added: trunk/server/websocket/gui-demo/run-websocket-server.sh
===================================================================
--- trunk/server/websocket/gui-demo/run-websocket-server.sh	                        (rev 0)
+++ trunk/server/websocket/gui-demo/run-websocket-server.sh	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+echo "Downloading dependencies..."
+mvn -f ../pom.xml dependency:copy-dependencies > /dev/null
+
+for i in ../target/dependency/*.jar ; do
+   CP=${i}:${CP}
+done
+
+JVM_PARAMS="${JVM_PARAMS} -Dorg.infinispan.ws.cache.config-file=${ISPN_HOME}/etc/config-samples/gui-demo-cache-config.xml -Dbind.address=127.0.0.1 -Djava.net.preferIPv4Stack=true -Dlog4j.configuration=file:${ISPN_HOME}/etc/log4j.xml"
+
+# Sample JPDA settings for remote socket debuging
+#JVM_PARAMS="$JVM_PARAMS -Xrunjdwp:transport=dt_socket,address=8686,server=y,suspend=y"
+
+CP=../target/infinispan-server-websocket.jar:${CP}
+
+COMMAND=" -cp ${CP} ${JVM_PARAMS} org.infinispan.websocket.WebSocketServer"
+# echo ${COMMAND}
+
+echo "Starting Infinispan Websocket Server..."
+java ${COMMAND}


Property changes on: trunk/server/websocket/gui-demo/run-websocket-server.sh
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/infinispan.xml
===================================================================
--- trunk/server/websocket/infinispan.xml	                        (rev 0)
+++ trunk/server/websocket/infinispan.xml	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1 @@
+<infinispan xmlns="urn:infinispan:config:4.0" />
\ No newline at end of file

Added: trunk/server/websocket/pom.xml
===================================================================
--- trunk/server/websocket/pom.xml	                        (rev 0)
+++ trunk/server/websocket/pom.xml	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,27 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+   <modelVersion>4.0.0</modelVersion>
+
+   <parent>
+      <groupId>org.infinispan</groupId>
+      <artifactId>infinispan-server-parent</artifactId>
+      <version>4.1.0-SNAPSHOT</version>
+      <relativePath>../pom.xml</relativePath>
+   </parent>
+
+   <artifactId>infinispan-server-websocket</artifactId>
+   <name>Infinispan WebSocket Server</name>
+   <description>WebSocket interface for Infinispan</description>
+
+	<dependencies>
+		
+		<dependency>
+			<groupId>org.jboss.netty</groupId>
+			<artifactId>netty</artifactId>
+			<version>3.2.0.BETA1</version>
+			<scope>compile</scope>
+		</dependency>
+		
+	</dependencies>
+
+</project>

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/CacheListener.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/CacheListener.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/CacheListener.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,202 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.infinispan.Cache;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
+import org.infinispan.notifications.cachelistener.event.Event;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Cache listener.
+ * <p/>
+ * Used to notify websocket clients of cache entry updates.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+ at Listener
+public class CacheListener {
+	
+	private List<ChannelNotifyParams> channels = new CopyOnWriteArrayList<ChannelNotifyParams>();
+
+	@CacheEntryCreated
+	public void cacheEntryCreated(CacheEntryCreatedEvent event) {
+		notifyChannels(event, event.getType());
+	}
+
+	@CacheEntryModified
+	public void cacheEntryModified(CacheEntryModifiedEvent event) {
+		notifyChannels(event, event.getType());
+	}
+
+	@CacheEntryRemoved
+	public void cacheEntryRemoved(CacheEntryRemovedEvent event) {
+		notifyChannels(event, event.getType());
+	}
+	
+	private void notifyChannels(CacheEntryEvent event, Event.Type eventType) {
+		if(event.isPre()) {
+			return;
+		}
+		
+		JSONObject jsonObject;
+		
+		try {
+			Cache<Object, Object> cache = event.getCache();
+			Object key = event.getKey();
+			Object value;
+			
+			switch(eventType) {
+			case CACHE_ENTRY_CREATED:
+				// TODO: Add optimization ... don't get from cache if non of the channels are interested in creates...
+				value = cache.get(key);
+				jsonObject = ChannelUtils.toJSON(key.toString(), value, cache.getName());
+				break;
+			case CACHE_ENTRY_MODIFIED:
+				value = ((CacheEntryModifiedEvent)event).getValue();
+				jsonObject = ChannelUtils.toJSON(key.toString(), value, cache.getName());
+				break;
+			case CACHE_ENTRY_REMOVED:
+				jsonObject = ChannelUtils.toJSON(key.toString(), null, cache.getName());
+				break;
+			default:
+				return;	
+			}
+			
+			jsonObject.put("eventType", eventType.toString());
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			return;
+		}
+
+		String jsonString = jsonObject.toString();
+		for(ChannelNotifyParams channel : channels) {
+			if(channel.channel.isOpen() && channel.onEvents.contains(eventType)) {
+				if(channel.key != null) {
+					if(event.getKey().equals(channel.key) || channel.key.equals("*")) {
+						channel.channel.write(new DefaultWebSocketFrame(jsonString));
+					}
+				} else {					
+					channel.channel.write(new DefaultWebSocketFrame(jsonString));
+				}
+			}
+		}
+	}
+	
+	public void addChannel(ChannelNotifyParams channel) {
+		if(!channels.contains(channel)) {
+			channels.add(channel);
+			channel.channel.getCloseFuture().addListener(new ChannelCloseFutureListener());
+		}
+	}
+	
+	public void removeChannel(ChannelNotifyParams channel) {
+		channels.remove(channel);
+	}
+	
+	public static class ChannelNotifyParams {
+		
+		private static final String[] DEFAULT_EVENTS = {Event.Type.CACHE_ENTRY_MODIFIED.toString(), Event.Type.CACHE_ENTRY_REMOVED.toString()};
+		
+		private Channel channel;
+		private String key;
+		private List<Event.Type> onEvents = new ArrayList<Event.Type>();		
+		
+		public ChannelNotifyParams(Channel channel, String key, String[] onEvents) {
+			if(channel == null) {
+				throw new IllegalArgumentException("null 'channel' arg in constructor call.");
+			}
+			String[] onEventsSpec = onEvents;
+			
+			this.channel = channel;
+			this.key = key;
+			
+			if(onEventsSpec ==  null) {
+				onEventsSpec = DEFAULT_EVENTS;
+			}
+			for(String eventType : onEventsSpec) {
+				try {
+					this.onEvents.add(Event.Type.valueOf(eventType));
+				} catch(RuntimeException e) {
+					// Ignore for now
+				}
+			}
+			
+			if(onEvents == null && key.equals("*")) {
+				this.onEvents.add(Event.Type.CACHE_ENTRY_CREATED);
+			}			
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if(obj instanceof ChannelNotifyParams) {
+				ChannelNotifyParams channelNotifyParams = (ChannelNotifyParams) obj;
+				if(channelNotifyParams.channel == channel) {
+					if(key == null) {
+						return (channelNotifyParams.key == null);
+					} else {
+						return key.equals(channelNotifyParams.key);
+					}
+				}
+			}
+			
+			return false;
+		}
+
+		@Override
+		public int hashCode() {
+			if(key != null) {				
+				return super.hashCode() + channel.hashCode() + key.hashCode();
+			} else {				
+				return super.hashCode() + channel.hashCode();
+			}
+		}
+	}
+	
+	private class ChannelCloseFutureListener implements ChannelFutureListener {
+
+		public void operationComplete(ChannelFuture channelCloseFuture) throws Exception {
+			for(ChannelNotifyParams channel : channels) {
+				if(channelCloseFuture.getChannel() ==  channel.channel) {
+					removeChannel(channel);
+				}
+			}
+		}		
+	}
+}

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/ChannelUtils.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/ChannelUtils.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/ChannelUtils.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,83 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import org.infinispan.Cache;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Channel Utilities.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class ChannelUtils {
+
+	/**
+	 * Push a cache entry value out onto the websocket channel (to the browser).
+	 * @param key The cache entry key whose value is to be pushed to the browser.
+	 * @param cache The cache containing the key.
+	 * @param ctx The channel context associated with the browser websocket channel..
+	 * @throws JSONException Error generating JSON string.
+	 */
+	public static void pushCacheValue(String key, Cache<Object, Object> cache, ChannelHandlerContext ctx) throws JSONException {
+		Object value = cache.get(key);
+		
+		JSONObject responseObject = toJSON(key, value, cache.getName());
+		
+		// Write the JSON response out onto the channel...
+		ctx.getChannel().write(new DefaultWebSocketFrame(responseObject.toString()));
+	}
+
+	/**
+	 * Cache key, value and cache-name to JSON string.
+	 * @param key The cache key.
+	 * @param value The cache value.
+	 * @param cacheName The cache name.
+	 * @return JSON Object representing a cache entry payload for transmission to the browser channel.
+	 * @throws JSONException Error generating JSON string.
+	 */
+	public static JSONObject toJSON(String key, Object value, String cacheName) throws JSONException {
+		JSONObject jsonObject = new JSONObject();
+	
+		jsonObject.put(OpHandler.CACHE_NAME, cacheName);
+		jsonObject.put(OpHandler.KEY, key);
+		
+		if(value != null) {
+			// Encode the cache value as JSON...
+			JSONObject valueObject = new JSONObject(value);
+			if(valueObject.get("bytes") == null) {
+				jsonObject.put(OpHandler.VALUE, valueObject.toString());
+				jsonObject.put(OpHandler.MIME, "application/json");
+			} else {
+				jsonObject.put(OpHandler.VALUE, value);
+				jsonObject.put(OpHandler.MIME, "text/plain");
+			}
+		} else {
+			jsonObject.put(OpHandler.VALUE, JSONObject.NULL);
+		}
+		
+		return jsonObject;
+	}
+}


Property changes on: trunk/server/websocket/src/main/java/org/infinispan/websocket/ChannelUtils.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/OpHandler.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/OpHandler.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/OpHandler.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import org.infinispan.Cache;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Websocket cache operation handler.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public interface OpHandler {
+	
+	public static final String OP_CODE = "opCode";
+	public static final String CACHE_NAME = "cacheName";
+	public static final String KEY = "key";
+	public static final String VALUE = "value";
+	public static final String MIME = "mime";
+
+	/**
+	 * Handle a websocket channel operation.
+	 * 
+	 * @param opPayload Operation payload.
+	 * @param cache The target cache.
+	 * @param ctx The Netty websocket channel handler.
+	 */
+	void handleOp(JSONObject opPayload, Cache<Object, Object> cache, ChannelHandlerContext ctx) throws JSONException;
+}

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServer.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServer.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServer.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,188 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+
+import org.infinispan.Cache;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.websocket.handlers.GetHandler;
+import org.infinispan.websocket.handlers.NotifyHandler;
+import org.infinispan.websocket.handlers.PutHandler;
+import org.infinispan.websocket.handlers.RemoveHandler;
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
+import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
+import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
+
+/**
+ * An HTTP server which serves Web Socket requests on an Infinispan cacheManager.
+ * <p/>
+ * Websocket specific code lifted from Netty WebSocket Server example.
+ */
+public class WebSocketServer {
+
+	public static final String ORG_INFINISPAN_WS_HOST = "org.infinispan.ws.host";
+	public static final String ORG_INFINISPAN_WS_PORT = "org.infinispan.ws.port";
+	public static final String ORG_INFINISPAN_WS_CACHE_CONFIG_FILE = "org.infinispan.ws.cache.config-file";
+	public static final String ORG_INFINISPAN_WS_CACHE_CONFIG_FILE_DEFAULT = "./infinispan.xml";
+	public static final String INFINISPAN_WS_JS_FILENAME = "infinispan-ws.js";
+	
+	private static String javascript;
+
+	public WebSocketServer(InetSocketAddress bindAddress, InputStream cacheConfigStream) throws IOException {
+		if(cacheConfigStream == null) {
+			throw new IllegalArgumentException("null 'cacheConfigStream' in constructor call.");
+		}
+		if(bindAddress == null) {
+			throw new IllegalArgumentException("null 'bindAddress' in constructor call.");
+		}
+		
+		try {
+			CacheManager manager = new DefaultCacheManager(cacheConfigStream);
+	
+	        // Configure the server.
+	        ServerBootstrap bootstrap = new ServerBootstrap(
+	                new NioServerSocketChannelFactory(
+	                        Executors.newCachedThreadPool(),
+	                        Executors.newCachedThreadPool()));
+	
+	        // Set up the event pipeline factory.
+	        bootstrap.setPipelineFactory(new WebSocketServerPipelineFactory(manager));
+	
+	        // Bind and start to accept incoming connections.
+	        bootstrap.bind(bindAddress);
+		} finally {
+			cacheConfigStream.close();
+		}
+	}
+	
+    public static void main(String[] args) throws Exception {
+    	String host = System.getProperty(ORG_INFINISPAN_WS_HOST);
+    	String portConfig = System.getProperty(ORG_INFINISPAN_WS_PORT, "61999").trim();
+    	File cacheConfigFile = new File(System.getProperty(ORG_INFINISPAN_WS_CACHE_CONFIG_FILE, ORG_INFINISPAN_WS_CACHE_CONFIG_FILE_DEFAULT).trim());
+    	int port;
+    	
+    	try {
+    		port = Integer.parseInt(portConfig);
+    	} catch(NumberFormatException e) {
+    		throw new IllegalArgumentException("Invalid WebSocket port address '" + portConfig + "'.  Must be a valid integer.");
+    	}
+    	
+    	if(!cacheConfigFile.exists()) {
+    		throw new IllegalArgumentException("Infinispan configuration file '" + cacheConfigFile.getAbsolutePath() + "' is not available.");
+    	}
+    	if(!cacheConfigFile.isFile()) {
+    		throw new IllegalArgumentException("Infinispan configuration file '" + cacheConfigFile.getAbsolutePath() + "' is not a readable file.");
+    	}
+    	
+    	InetSocketAddress inetAddress;
+		if(host != null) {
+    		inetAddress = new InetSocketAddress(host, port);
+    	} else {
+    		inetAddress = new InetSocketAddress(port);
+    	}
+        
+		new WebSocketServer(inetAddress, new FileInputStream(cacheConfigFile));
+		
+        System.out.println("Infinispan Websocket Server listening on address '" + inetAddress + "'. Using Cache configuration '" + cacheConfigFile.getAbsolutePath() + "'.");
+        System.out.println("Infinispan Websocket Server started.");
+    }
+    
+    private static class WebSocketServerPipelineFactory implements ChannelPipelineFactory {
+
+		private CacheManager cacheManager;
+		private Map<String, OpHandler> operationHandlers;
+		private Map<String, Cache> startedCaches = new ConcurrentHashMap<String, Cache>();
+
+		public WebSocketServerPipelineFactory(CacheManager cacheManager) {
+			this.cacheManager = cacheManager;
+
+			operationHandlers = new HashMap<String, OpHandler>();
+			operationHandlers.put("put", new PutHandler());
+			operationHandlers.put("get", new GetHandler());
+			operationHandlers.put("remove", new RemoveHandler());
+			NotifyHandler notifyHandler = new NotifyHandler();
+			operationHandlers.put("notify", notifyHandler);
+			operationHandlers.put("unnotify", notifyHandler);
+		}
+
+		public ChannelPipeline getPipeline() throws Exception {
+            // Create a default pipeline implementation.
+            ChannelPipeline pipeline = Channels.pipeline();
+            
+            pipeline.addLast("decoder", new HttpRequestDecoder());
+            pipeline.addLast("aggregator", new HttpChunkAggregator(65536));
+            pipeline.addLast("encoder", new HttpResponseEncoder());
+            pipeline.addLast("handler", new WebSocketServerHandler(cacheManager, operationHandlers, startedCaches));
+            
+            return pipeline;
+        }
+    }
+    
+    public static String getJavascript() {
+    	if(javascript != null) {
+    		return javascript;
+    	}
+    	
+		BufferedReader scriptReader = new BufferedReader(new InputStreamReader(WebSocketServer.class.getResourceAsStream(INFINISPAN_WS_JS_FILENAME)));
+		
+		try {
+			StringWriter writer = new StringWriter();
+			
+			String line = scriptReader.readLine();
+			while(line != null) {
+				writer.write(line);
+				writer.write('\n');
+				line = scriptReader.readLine();
+			}
+
+			javascript = writer.toString();
+			
+			return javascript;
+		} catch (IOException e) {
+			throw new IllegalStateException("Unexpected exception while sending Websockets script to client.", e);
+		} finally {
+			try {
+				scriptReader.close();
+			} catch (IOException e) {
+				throw new IllegalStateException("Unexpected exception while closing Websockets script to client.", e);
+			}
+		}
+    }
+}
\ No newline at end of file

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServerHandler.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServerHandler.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/WebSocketServerHandler.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,213 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.*;
+import static org.jboss.netty.handler.codec.http.HttpMethod.*;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
+import static org.jboss.netty.handler.codec.http.HttpVersion.*;
+
+import java.io.StringWriter;
+import java.util.Map;
+
+import org.infinispan.Cache;
+import org.infinispan.manager.CacheManager;
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.handler.codec.http.HttpResponse;
+import org.jboss.netty.handler.codec.http.HttpResponseStatus;
+import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
+import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
+import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame;
+import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameDecoder;
+import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameEncoder;
+import org.jboss.netty.util.CharsetUtil;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Web Socket Server Handler (Netty).
+ * <p/>
+ * Websocket specific code lifted from Netty WebSocket Server example.
+ */
+public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
+
+	private static final String INFINISPAN_WS_JS_FILENAME = "infinispan-ws.js";
+	private CacheManager cacheManager;
+	private Map<String, OpHandler> operationHandlers;
+	private boolean connectionUpgraded;
+	private Map<String, Cache> startedCaches;
+
+	public WebSocketServerHandler(CacheManager cacheManager, Map<String, OpHandler> operationHandlers, Map<String, Cache> startedCaches) {
+		this.cacheManager = cacheManager;		
+		this.operationHandlers = operationHandlers;
+		this.startedCaches = startedCaches;
+	}
+
+	@Override
+    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+        Object msg = e.getMessage();
+        if (msg instanceof HttpRequest) {
+            handleHttpRequest(ctx, (HttpRequest) msg);
+        } else if (msg instanceof WebSocketFrame) {
+            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
+        }
+    }
+
+    private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) {
+        // Allow only GET methods.
+        if (req.getMethod() != GET) {
+            sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
+            return;
+        }
+
+        if(!connectionUpgraded && req.getUri().equalsIgnoreCase("/" + INFINISPAN_WS_JS_FILENAME)) {
+            DefaultHttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
+            loadScriptToResponse(req, res);
+			sendHttpResponse(ctx, req, res);
+            return;        	
+        } else if (Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION)) &&
+            WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {
+
+        	// Serve the WebSocket handshake request.
+
+            // Create the WebSocket handshake response.
+            HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
+            res.addHeader(Names.UPGRADE, WEBSOCKET);
+            res.addHeader(CONNECTION, Values.UPGRADE);
+            res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
+            res.addHeader(WEBSOCKET_LOCATION, getWebSocketLocation(req));
+            String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
+            if (protocol != null) {
+                res.addHeader(WEBSOCKET_PROTOCOL, protocol);
+            }
+
+            // Upgrade the connection and send the handshake response.
+            ChannelPipeline p = ctx.getChannel().getPipeline();
+            p.remove("aggregator");
+            p.replace("decoder", "wsdecoder", new WebSocketFrameDecoder());
+
+            ctx.getChannel().write(res);
+
+            p.replace("encoder", "wsencoder", new WebSocketFrameEncoder());
+            return;
+        }
+
+        // Send an error page otherwise.
+        sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
+    }
+
+	private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
+    	try {
+			JSONObject payload = new JSONObject(frame.getTextData());
+			String opCode = (String) payload.get(OpHandler.OP_CODE);
+			String cacheName = (String) payload.opt(OpHandler.CACHE_NAME);
+			Cache<Object, Object> cache = getCache(cacheName);
+			
+			OpHandler handler = operationHandlers.get(opCode);
+			if(handler != null) {
+				handler.handleOp(payload, cache, ctx);
+			}
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+    }
+
+	private Cache<Object, Object> getCache(final String cacheName) {
+		String key = cacheName;
+		Cache<Object, Object> cache;
+		
+		if(key == null) {
+			key = "";
+		}
+		
+		cache = startedCaches.get(key);
+		
+		if(cache == null) {
+			synchronized (startedCaches) {
+				cache = startedCaches.get(key);
+				if(cache == null) {
+					if(cacheName != null) {
+						cache = cacheManager.getCache(key);
+					} else {
+						cache = cacheManager.getCache();
+					}
+					startedCaches.put(key, cache);
+					cache.start();
+				}
+			}
+		}
+		
+		return cache;
+	}
+
+    private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
+        // Generate an error page if response status code is not OK (200).
+        if (res.getStatus().getCode() != 200) {
+            res.setContent(ChannelBuffers.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
+            setContentLength(res, res.getContent().readableBytes());
+        }
+
+        // Send the response and close the connection if necessary.
+        ChannelFuture f = ctx.getChannel().write(res);
+        if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
+            f.addListener(ChannelFutureListener.CLOSE);
+        }
+    }
+
+	private void loadScriptToResponse(HttpRequest req, DefaultHttpResponse res) {
+		String wsAddress = getWebSocketLocation(req);
+		
+		StringWriter writer = new StringWriter();
+		writer.write("var defaultWSAddress = '" + wsAddress + "';");
+		writer.write(WebSocketServer.getJavascript());
+		
+		ChannelBuffer content = ChannelBuffers.copiedBuffer(writer.toString(), CharsetUtil.UTF_8);
+		
+		res.setHeader(CONTENT_TYPE, "text/javascript; charset=UTF-8");
+		setContentLength(res, content.readableBytes());
+		res.setContent(content);
+	}
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+        e.getCause().printStackTrace();
+        e.getChannel().close();
+    }
+
+    private String getWebSocketLocation(HttpRequest req) {
+        return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + "/";
+    }
+}
\ No newline at end of file

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/GetHandler.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/GetHandler.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/GetHandler.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket.handlers;
+
+import org.infinispan.Cache;
+import org.infinispan.websocket.ChannelUtils;
+import org.infinispan.websocket.OpHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Cache "get" operation handler.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class GetHandler implements OpHandler {
+
+	public void handleOp(JSONObject opPayload, Cache<Object, Object> cache, ChannelHandlerContext ctx) throws JSONException {
+		String key = (String) opPayload.get(OpHandler.KEY);
+		
+		ChannelUtils.pushCacheValue(key, cache, ctx);
+	}
+}

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/NotifyHandler.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/NotifyHandler.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/NotifyHandler.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket.handlers;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.infinispan.Cache;
+import org.infinispan.websocket.CacheListener;
+import org.infinispan.websocket.ChannelUtils;
+import org.infinispan.websocket.OpHandler;
+import org.infinispan.websocket.CacheListener.ChannelNotifyParams;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Handler for the "notify" and "unnotify" operations.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class NotifyHandler implements OpHandler {
+	
+	private Map<Cache, CacheListener> listeners = new ConcurrentHashMap<Cache, CacheListener>();
+
+	public void handleOp(JSONObject opPayload, Cache<Object, Object> cache, ChannelHandlerContext ctx) throws JSONException {
+		String opCode = (String) opPayload.get(OpHandler.OP_CODE);
+		String key = (String) opPayload.opt(OpHandler.KEY);
+		String[] onEvents = (String[]) opPayload.opt("onEvents");
+		CacheListener listener = listeners.get(cache);
+		
+		if(key == null) {
+			// If key not specified... notify on all...
+			key = "*";
+		}
+		
+		if(listener == null) {
+			synchronized (this) {
+				listener = listeners.get(cache);
+				if(listener == null) {
+					listener = new CacheListener();
+					listeners.put(cache, listener);	
+					cache.addListener(listener);
+				}
+			}
+		}
+		
+		String[] keyTokens = key.split(",");		
+		for(String keyToken : keyTokens) {
+			ChannelNotifyParams notifyParams = new ChannelNotifyParams(ctx.getChannel(), keyToken, onEvents);		
+			
+			if(opCode.equals("notify")) {
+				listener.addChannel(notifyParams);
+				// And push the value to the channel (if it's not wildcard)...
+				if(!keyToken.equals("*")) {
+					ChannelUtils.pushCacheValue(keyToken, cache, ctx);
+				}
+			} else if(opCode.equals("unnotify")) {
+				listener.removeChannel(notifyParams);
+			}
+		}		
+	}
+}

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/PutHandler.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/PutHandler.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/PutHandler.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket.handlers;
+
+import org.infinispan.Cache;
+import org.infinispan.websocket.OpHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Cache "get" operation handler.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class PutHandler implements OpHandler {
+
+	public void handleOp(JSONObject opPayload, Cache<Object, Object> cache, ChannelHandlerContext ctx) throws JSONException {
+		String key = (String) opPayload.get(OpHandler.KEY);
+		String value = (String) opPayload.get(OpHandler.VALUE);
+		String mimeType = (String) opPayload.get(OpHandler.MIME);
+
+		if(mimeType.equals("application/json")) {
+			// Decode the payload to a JSON string...
+			
+			// TODO:  Need some way to populate the JSON object string to an Object graph.
+			// Something plugable... allowing JAXB, Smooks etc
+			
+			throw new UnsupportedOperationException("Complex object graphs not yet supported!! Cannot cache value:\n" + value);
+		} else {
+			// Put the raw value into the cache...
+			cache.put(key, value);
+		}
+	}
+}

Added: trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/RemoveHandler.java
===================================================================
--- trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/RemoveHandler.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/infinispan/websocket/handlers/RemoveHandler.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket.handlers;
+
+import org.infinispan.Cache;
+import org.infinispan.websocket.OpHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Cache "remove" operation handler.
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class RemoveHandler implements OpHandler {
+
+	public void handleOp(JSONObject opPayload, Cache<Object, Object> cache, ChannelHandlerContext ctx) throws JSONException {
+		String key = (String) opPayload.get(OpHandler.KEY);
+		
+		cache.remove(key);
+	}
+}

Added: trunk/server/websocket/src/main/java/org/json/CDL.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/CDL.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/CDL.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,279 @@
+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-09-11
+ */
+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);
+            char c = x.next();
+            if (value == null || 
+            		(ja.length() == 0 && value.length() == 0 && c != ',')) {
+                return null;
+            }
+            ja.put(value);
+            for (;;) {                
+                if (c == ',') {
+                    break;
+                }
+                if (c != ' ') {
+                    if (c == '\n' || c == '\r' || c == 0) {
+                        return ja;
+                    }
+                    throw x.syntaxError("Bad character '" + c + "' (" +
+                            (int)c + ").");
+                }
+                c = x.next();
+            }
+        }
+    }
+
+    /**
+     * 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.length() > 0 && (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();
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/CDL.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/Cookie.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/Cookie.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/Cookie.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,169 @@
+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();
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/Cookie.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/CookieList.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/CookieList.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/CookieList.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,90 @@
+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();
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/CookieList.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/HTTP.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/HTTP.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/HTTP.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,163 @@
+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();
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/HTTP.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/HTTPTokener.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/HTTPTokener.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/HTTPTokener.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,77 @@
+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();
+        }
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/HTTPTokener.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONArray.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONArray.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONArray.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,917 @@
+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>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+
+ * @author JSON.org
+ * @version 2009-04-14
+ */
+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 = new ArrayList();
+		if (collection != null) {
+			Iterator iter = collection.iterator();;
+			while (iter.hasNext()) {
+			    Object o = iter.next();
+                this.myArrayList.add(JSONObject.wrap(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(JSONObject.wrap(Array.get(array, i)));
+            }
+        } 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 an 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.
+     */
+    public JSONObject toJSONObject(JSONArray names) throws JSONException {
+        if (names == null || names.length() == 0 || length() == 0) {
+            return null;
+        }
+        JSONObject jo = new JSONObject();
+        for (int i = 0; i < names.length(); i += 1) {
+            jo.put(names.getString(i), this.opt(i));
+        }
+        return jo;
+    }
+
+
+    /**
+     * Make a JSON text of this JSONArray. For compactness, no
+     * unnecessary whitespace is added. If it is not possible to produce a
+     * syntactically correct JSON text then null will be returned instead. This
+     * could occur if the array contains an invalid number.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return a printable, displayable, transmittable
+     *  representation of the array.
+     */
+    public String toString() {
+        try {
+            return '[' + join(",") + ']';
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONArray.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>[</code>&nbsp;<small>(left bracket)</small> and ending
+     *  with <code>]</code>&nbsp;<small>(right bracket)</small>.
+     * @throws JSONException
+     */
+    public String toString(int indentFactor) throws JSONException {
+        return toString(indentFactor, 0);
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONArray.
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @param indent The indention of the top level.
+     * @return a printable, displayable, transmittable
+     *  representation of the array.
+     * @throws JSONException
+     */
+    String toString(int indentFactor, int indent) throws JSONException {
+        int len = length();
+        if (len == 0) {
+            return "[]";
+        }
+        int i;
+        StringBuffer sb = new StringBuffer("[");
+        if (len == 1) {
+            sb.append(JSONObject.valueToString(this.myArrayList.get(0),
+                    indentFactor, indent));
+        } else {
+            int newindent = indent + indentFactor;
+            sb.append('\n');
+            for (i = 0; i < len; i += 1) {
+                if (i > 0) {
+                    sb.append(",\n");
+                }
+                for (int j = 0; j < newindent; j += 1) {
+                    sb.append(' ');
+                }
+                sb.append(JSONObject.valueToString(this.myArrayList.get(i),
+                        indentFactor, newindent));
+            }
+            sb.append('\n');
+            for (i = 0; i < indent; i += 1) {
+                sb.append(' ');
+            }
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+
+
+    /**
+     * Write the contents of the JSONArray as JSON text to a writer.
+     * For compactness, no whitespace is added.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return The writer.
+     * @throws JSONException
+     */
+    public Writer write(Writer writer) throws JSONException {
+        try {
+            boolean b = false;
+            int     len = length();
+
+            writer.write('[');
+
+            for (int i = 0; i < len; i += 1) {
+                if (b) {
+                    writer.write(',');
+                }
+                Object v = this.myArrayList.get(i);
+                if (v instanceof JSONObject) {
+                    ((JSONObject)v).write(writer);
+                } else if (v instanceof JSONArray) {
+                    ((JSONArray)v).write(writer);
+                } else {
+                    writer.write(JSONObject.valueToString(v));
+                }
+                b = true;
+            }
+            writer.write(']');
+            return writer;
+        } catch (IOException e) {
+           throw new JSONException(e);
+        }
+    }
+}
\ No newline at end of file


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONArray.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONException.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONException.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONException.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,31 @@
+package org.json;
+
+/**
+ * The JSONException is thrown by the JSON.org classes when things are amiss.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class JSONException extends Exception {
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 0;
+	private Throwable cause;
+
+    /**
+     * Constructs a JSONException with an explanatory message.
+     * @param message Detail about the reason for the exception.
+     */
+    public JSONException(String message) {
+        super(message);
+    }
+
+    public JSONException(Throwable t) {
+        super(t.getMessage());
+        this.cause = t;
+    }
+
+    public Throwable getCause() {
+        return this.cause;
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONException.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONML.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONML.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONML.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,455 @@
+package org.json;
+
+/*
+Copyright (c) 2008 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;
+
+
+/**
+ * This provides static methods to convert an XML text into a JSONArray or 
+ * JSONObject, and to covert a JSONArray or JSONObject into an XML text using 
+ * the JsonML transform.
+ * @author JSON.org
+ * @version 2010-02-12
+ */
+public class JSONML {
+		
+    /**
+     * Parse XML values and store them in a JSONArray.
+     * @param x       The XMLTokener containing the source string.
+     * @param arrayForm true if array form, false if object form.
+     * @param ja      The JSONArray that is containing the current tag or null
+     *     if we are at the outermost level.
+     * @return A JSONArray if the value is the outermost tag, otherwise null.
+     * @throws JSONException
+     */
+    private static Object parse(XMLTokener x, boolean arrayForm, 
+    		JSONArray ja) throws JSONException {
+        String     attribute;
+        char       c;
+        String	   closeTag = null;
+        int        i;
+        JSONArray  newja = null;
+        JSONObject newjo = null;
+        Object     token;
+        String	   tagName = null;
+        
+// Test for and skip past these forms:
+//      <!-- ... -->
+//      <![  ... ]]>
+//      <!   ...   >
+//      <?   ...  ?>
+        
+        while (true) {
+        	token = x.nextContent();
+    		if (token == XML.LT) {
+    			token = x.nextToken();
+    			if (token instanceof Character) {
+			        if (token == XML.SLASH) {
+
+// Close tag </
+
+			        	token = x.nextToken();
+			        	if (!(token instanceof String)) {
+			        		throw new JSONException(
+			        				"Expected a closing name instead of '" + 
+			        				token + "'.");
+			        	}
+			            if (x.nextToken() != XML.GT) {
+			                throw x.syntaxError("Misshaped close tag");
+			            }
+			            return token;
+			        } else if (token == XML.BANG) {
+        		
+// <!
+        	
+			            c = x.next();
+			            if (c == '-') {
+			                if (x.next() == '-') {
+			                    x.skipPast("-->");
+			                }
+			                x.back();
+			            } else if (c == '[') {
+			                token = x.nextToken();
+			                if (token.equals("CDATA") && x.next() == '[') {
+			                	if (ja != null) {
+			                		ja.put(x.nextCDATA());
+			                	}
+			                } else {
+			                	throw x.syntaxError("Expected 'CDATA['");
+			                }
+			            } else {
+				            i = 1;
+				            do {
+				                token = x.nextMeta();
+				                if (token == null) {
+				                    throw x.syntaxError("Missing '>' after '<!'.");
+				                } else if (token == XML.LT) {
+				                    i += 1;
+				                } else if (token == XML.GT) {
+				                    i -= 1;
+				                }
+				            } while (i > 0);
+			            }
+			        } else if (token == XML.QUEST) {
+
+// <?
+
+			        	x.skipPast("?>");
+			        } else {
+			            throw x.syntaxError("Misshaped tag");
+			        }
+
+// Open tag <
+
+		        } else {
+		        	if (!(token instanceof String)) {
+			            throw x.syntaxError("Bad tagName '" + token + "'.");		        		
+		        	}
+		        	tagName = (String)token;
+		            newja = new JSONArray();		
+		            newjo = new JSONObject();
+		        	if (arrayForm) {
+			            newja.put(tagName);
+			            if (ja != null) {
+			            	ja.put(newja);
+			            }
+			        } else {
+		        		newjo.put("tagName", tagName);
+		        		if (ja != null) {
+			            	ja.put(newjo);
+			            }
+			        }
+		            token = null;
+		            for (;;) {
+		                if (token == null) {
+		                    token = x.nextToken();
+		                }
+		                if (token == null) {
+		                	throw x.syntaxError("Misshaped tag");
+		                }
+		                if (!(token instanceof String)) {
+		                	break;
+		                }
+
+//		              attribute = value
+
+	                    attribute = (String)token;
+			        	if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) {
+                            throw x.syntaxError("Reserved attribute.");			        		
+			        	}
+	                    token = x.nextToken();
+	                    if (token == XML.EQ) {
+	                        token = x.nextToken();
+	                        if (!(token instanceof String)) {
+	                            throw x.syntaxError("Missing value");
+	                        }
+	                        newjo.accumulate(attribute, JSONObject.stringToValue((String)token));
+	                        token = null;
+	                    } else {
+	                    	newjo.accumulate(attribute, "");
+	                    }
+		            }
+                    if (arrayForm && newjo.length() > 0) {
+                    	newja.put(newjo);
+                    }
+
+// Empty tag <.../>
+
+	                if (token == XML.SLASH) {
+	                    if (x.nextToken() != XML.GT) {
+	                        throw x.syntaxError("Misshaped tag");
+	                    }
+	                    if (ja == null) {
+	                    	if (arrayForm) {
+	                    		return newja;
+	                    	} else {
+	                    		return newjo;
+	                    	}
+	                    }
+
+// Content, between <...> and </...>
+
+	                } else {
+	                	if (token != XML.GT) {
+	                		throw x.syntaxError("Misshaped tag");
+	                	}
+	                	closeTag = (String)parse(x, arrayForm, newja);
+	                	if (closeTag != null) {
+		                	if (!closeTag.equals(tagName)) {
+		                		throw x.syntaxError("Mismatched '" + tagName + 
+		                				"' and '" + closeTag + "'");
+					        }
+		                	tagName = null;
+		            		if (!arrayForm && newja.length() > 0) {
+		            			newjo.put("childNodes", newja);
+		            		}
+		                	if (ja == null) {
+		                    	if (arrayForm) {
+		                    		return newja;
+		                    	} else {
+		                    		return newjo;
+		                    	}
+		                	}
+	                	}
+                	}
+	            }
+		    } else {
+		    	if (ja != null) {
+		    		ja.put(token instanceof String ? 
+		    				JSONObject.stringToValue((String)token) : token);
+		    	}
+		    }
+        }
+    }
+
+
+    /**
+     * Convert a well-formed (but not necessarily valid) XML string into a
+     * JSONArray using the JsonML transform. Each XML tag is represented as
+     * a JSONArray in which the first element is the tag name. If the tag has
+     * attributes, then the second element will be JSONObject containing the
+     * name/value pairs. If the tag contains children, then strings and
+     * JSONArrays will represent the child tags.
+     * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+     * @param string The source string.
+     * @return A JSONArray containing the structured data from the XML string.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(String string) throws JSONException {
+    	return toJSONArray(new XMLTokener(string));
+    }
+
+
+    /**
+     * Convert a well-formed (but not necessarily valid) XML string into a
+     * JSONArray using the JsonML transform. Each XML tag is represented as
+     * a JSONArray in which the first element is the tag name. If the tag has
+     * attributes, then the second element will be JSONObject containing the
+     * name/value pairs. If the tag contains children, then strings and
+     * JSONArrays will represent the child content and tags.
+     * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+     * @param x An XMLTokener.
+     * @return A JSONArray containing the structured data from the XML string.
+     * @throws JSONException
+     */
+    public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
+    	return (JSONArray)parse(x, true, null);
+    }
+
+
+    
+    /**
+     * Convert a well-formed (but not necessarily valid) XML string into a
+     * JSONObject using the JsonML transform. Each XML tag is represented as
+     * a JSONObject with a "tagName" property. If the tag has attributes, then 
+     * the attributes will be in the JSONObject as properties. If the tag 
+     * contains children, the object will have a "childNodes" property which 
+     * will be an array of strings and JsonML JSONObjects.
+
+     * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+     * @param x An XMLTokener of the XML source text.
+     * @return A JSONObject containing the structured data from the XML string.
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
+       	return (JSONObject)parse(x, false, null);
+    }
+    /**
+     * Convert a well-formed (but not necessarily valid) XML string into a
+     * JSONObject using the JsonML transform. Each XML tag is represented as
+     * a JSONObject with a "tagName" property. If the tag has attributes, then 
+     * the attributes will be in the JSONObject as properties. If the tag 
+     * contains children, the object will have a "childNodes" property which 
+     * will be an array of strings and JsonML JSONObjects.
+
+     * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+     * @param string The XML source text.
+     * @return A JSONObject containing the structured data from the XML string.
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(String string) throws JSONException {
+    	return toJSONObject(new XMLTokener(string));
+    }
+
+
+    /**
+     * Reverse the JSONML transformation, making an XML text from a JSONArray.
+     * @param ja A JSONArray.
+     * @return An XML string.
+     * @throws JSONException
+     */
+    public static String toString(JSONArray ja) throws JSONException {
+    	Object		 e;
+    	int			 i;
+    	JSONObject   jo;
+    	String       k;
+	    Iterator     keys;
+	    int			 length;
+    	StringBuffer sb = new StringBuffer();
+	    String       tagName;
+	    String       v;
+	    
+// Emit <tagName	    
+    	
+    	tagName = ja.getString(0);
+		XML.noSpace(tagName);
+		tagName = XML.escape(tagName);
+		sb.append('<');
+		sb.append(tagName);
+		
+		e = ja.opt(1);
+		if (e instanceof JSONObject) {
+			i = 2;
+			jo = (JSONObject)e;
+			
+// Emit the attributes
+			
+	        keys = jo.keys();
+	        while (keys.hasNext()) {
+	            k = keys.next().toString();
+            	XML.noSpace(k);
+	            v = jo.optString(k);
+	            if (v != null) {
+		            sb.append(' ');
+		            sb.append(XML.escape(k));
+		            sb.append('=');
+		            sb.append('"');
+		            sb.append(XML.escape(v));
+		            sb.append('"');
+	            }
+	        }  
+		} else {
+			i = 1;
+		}
+	     	
+//Emit content in body
+	    	
+		length = ja.length();
+		if (i >= length) {
+	        sb.append('/');
+	        sb.append('>');
+		} else {
+	        sb.append('>');
+			do {
+			    e = ja.get(i);
+			    i += 1;
+			    if (e != null) {
+			    	if (e instanceof String) {
+			    		sb.append(XML.escape(e.toString()));
+					} else if (e instanceof JSONObject) {
+						sb.append(toString((JSONObject)e));
+					} else if (e instanceof JSONArray) {
+						sb.append(toString((JSONArray)e));
+					}
+			    }
+			} while (i < length);
+			sb.append('<');
+	        sb.append('/');
+			sb.append(tagName);
+	        sb.append('>');
+	    }
+        return sb.toString();
+    }
+    
+    /**
+     * Reverse the JSONML transformation, making an XML text from a JSONObject.
+     * The JSONObject must contain a "tagName" property. If it has children, 
+     * then it must have a "childNodes" property containing an array of objects. 
+     * The other properties are attributes with string values.
+     * @param jo A JSONObject.
+     * @return An XML string.
+     * @throws JSONException
+     */
+	public static String toString(JSONObject jo) throws JSONException {
+	    StringBuffer sb = new StringBuffer();
+	    Object		 e;
+	    int          i;
+	    JSONArray    ja;
+	    String       k;
+	    Iterator     keys;
+	    int          len;
+	    String       tagName;
+	    String       v;
+	
+//Emit <tagName
+	
+		tagName = jo.optString("tagName");
+		if (tagName == null) {
+			return XML.escape(jo.toString());
+		}
+		XML.noSpace(tagName);
+		tagName = XML.escape(tagName);
+		sb.append('<');
+		sb.append(tagName);
+	
+//Emit the attributes
+	
+        keys = jo.keys();
+        while (keys.hasNext()) {
+            k = keys.next().toString();
+            if (!k.equals("tagName") && !k.equals("childNodes")) {
+            	XML.noSpace(k);
+	            v = jo.optString(k);
+	            if (v != null) {
+		            sb.append(' ');
+		            sb.append(XML.escape(k));
+		            sb.append('=');
+		            sb.append('"');
+		            sb.append(XML.escape(v));
+		            sb.append('"');
+	            }
+            }
+        }    
+		     	
+//Emit content in body
+	
+		ja = jo.optJSONArray("childNodes");
+		if (ja == null) {
+	        sb.append('/');
+	        sb.append('>');
+		} else {
+	        sb.append('>');
+			len = ja.length();
+			for (i = 0; i < len; i += 1) {
+			    e = ja.get(i);
+			    if (e != null) {
+			    	if (e instanceof String) {
+			    		sb.append(XML.escape(e.toString()));
+					} else if (e instanceof JSONObject) {
+						sb.append(toString((JSONObject)e));
+					} else if (e instanceof JSONArray) {
+						sb.append(toString((JSONArray)e));
+					}
+			    }
+			}
+			sb.append('<');
+	        sb.append('/');
+			sb.append(tagName);
+	        sb.append('>');
+	    }
+        return sb.toString();
+    }
+}
\ No newline at end of file


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONML.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONObject.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONObject.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONObject.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,1573 @@
+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.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * A JSONObject is an unordered collection of name/value pairs. Its
+ * external form is a string wrapped in curly braces with colons between the
+ * names and values, and commas between the values and names. The internal form
+ * is an object having <code>get</code> and <code>opt</code> methods for
+ * accessing the values by name, and <code>put</code> methods for adding or
+ * replacing values by name. 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</code>
+ * object. A JSONObject constructor can be used to convert an external form
+ * JSON text into an internal form whose values can be retrieved with the
+ * <code>get</code> and <code>opt</code> methods, or to convert values into a
+ * JSON text using the <code>put</code> and <code>toString</code> methods.
+ * 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 <code>put</code> methods adds values to an object. For example, <pre>
+ *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
+ * produces the string <code>{"JSON": "Hello, World"}</code>.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * the 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 brace.</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>Keys can be followed by <code>=</code> or <code>=></code> as well as
+ *     by <code>:</code>.</li>
+ * <li>Values can be followed 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 2010-01-05
+ */
+public class JSONObject {
+
+    /**
+     * JSONObject.NULL is equivalent to the value that JavaScript calls null,
+     * whilst Java's null is equivalent to the value that JavaScript calls
+     * undefined.
+     */
+     private static final class Null {
+
+        /**
+         * There is only intended to be a single instance of the NULL object,
+         * so the clone method returns itself.
+         * @return     NULL.
+         */
+        protected final Object clone() {
+            return this;
+        }
+
+
+        /**
+         * A Null object is equal to the null value and to itself.
+         * @param object    An object to test for nullness.
+         * @return true if the object parameter is the JSONObject.NULL object
+         *  or null.
+         */
+        public boolean equals(Object object) {
+            return object == null || object == this;
+        }
+
+
+        /**
+         * Get the "null" string value.
+         * @return The string "null".
+         */
+        public String toString() {
+            return "null";
+        }
+    }
+
+
+    /**
+     * The map where the JSONObject's properties are kept.
+     */
+    private Map map;
+
+
+    /**
+     * It is sometimes more convenient and less ambiguous to have a
+     * <code>NULL</code> object than to use Java's <code>null</code> value.
+     * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
+     * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
+     */
+    public static final Object NULL = new Null();
+
+
+    /**
+     * Construct an empty JSONObject.
+     */
+    public JSONObject() {
+        this.map = new HashMap();
+    }
+
+
+    /**
+     * Construct a JSONObject from a subset of another JSONObject.
+     * An array of strings is used to identify the keys that should be copied.
+     * Missing keys are ignored.
+     * @param jo A JSONObject.
+     * @param names An array of strings.
+     * @throws JSONException 
+     * @exception JSONException If a value is a non-finite number or if a name is duplicated.
+     */
+    public JSONObject(JSONObject jo, String[] names) {
+        this();
+        for (int i = 0; i < names.length; i += 1) {
+        	try {
+        		putOnce(names[i], jo.opt(names[i]));
+        	} catch (Exception ignore) {
+        	}
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from a JSONTokener.
+     * @param x A JSONTokener object containing the source string.
+     * @throws JSONException If there is a syntax error in the source string
+     *  or a duplicated key.
+     */
+    public JSONObject(JSONTokener x) throws JSONException {
+        this();
+        char c;
+        String key;
+
+        if (x.nextClean() != '{') {
+            throw x.syntaxError("A JSONObject text must begin with '{'");
+        }
+        for (;;) {
+            c = x.nextClean();
+            switch (c) {
+            case 0:
+                throw x.syntaxError("A JSONObject text must end with '}'");
+            case '}':
+                return;
+            default:
+                x.back();
+                key = x.nextValue().toString();
+            }
+
+            /*
+             * The key is followed by ':'. We will also tolerate '=' or '=>'.
+             */
+
+            c = x.nextClean();
+            if (c == '=') {
+                if (x.next() != '>') {
+                    x.back();
+                }
+            } else if (c != ':') {
+                throw x.syntaxError("Expected a ':' after a key");
+            }
+            putOnce(key, x.nextValue());
+
+            /*
+             * Pairs are separated by ','. We will also tolerate ';'.
+             */
+
+            switch (x.nextClean()) {
+            case ';':
+            case ',':
+                if (x.nextClean() == '}') {
+                    return;
+                }
+                x.back();
+                break;
+            case '}':
+                return;
+            default:
+                throw x.syntaxError("Expected a ',' or '}'");
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from a Map.
+     *
+     * @param map A map object that can be used to initialize the contents of
+     *  the JSONObject.
+     * @throws JSONException 
+     */
+    public JSONObject(Map map) {
+        this.map = new HashMap();
+        if (map != null) {
+            Iterator i = map.entrySet().iterator();
+            while (i.hasNext()) {
+                Map.Entry e = (Map.Entry)i.next();
+                this.map.put(e.getKey(), wrap(e.getValue()));
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from an Object using bean getters.
+     * It reflects on all of the public methods of the object.
+     * For each of the methods with no parameters and a name starting
+     * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
+     * the method is invoked, and a key and the value returned from the getter method
+     * are put into the new JSONObject.
+     *
+     * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
+     * If the second remaining character is not upper case, then the first
+     * character is converted to lower case.
+     *
+     * For example, if an object has a method named <code>"getName"</code>, and
+     * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
+     * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
+     *
+     * @param bean An object that has getter methods that should be used
+     * to make a JSONObject.
+     */
+    public JSONObject(Object bean) {
+        this();
+        populateMap(bean);
+    }
+
+
+    /**
+     * Construct a JSONObject from an Object, using reflection to find the
+     * public members. The resulting JSONObject's keys will be the strings
+     * from the names array, and the values will be the field values associated
+     * with those keys in the object. If a key is not found or not visible,
+     * then it will not be copied into the new JSONObject.
+     * @param object An object that has fields that should be used to make a
+     * JSONObject.
+     * @param names An array of strings, the names of the fields to be obtained
+     * from the object.
+     */
+    public JSONObject(Object object, String names[]) {
+        this();
+        Class c = object.getClass();
+        for (int i = 0; i < names.length; i += 1) {
+            String name = names[i];
+            try {
+                putOpt(name, c.getField(name).get(object));
+            } catch (Exception ignore) {
+            }
+        }
+    }
+
+
+    /**
+     * Construct a JSONObject from a source JSON text string.
+     * This is the most commonly used JSONObject constructor.
+     * @param source    A string beginning
+     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @exception JSONException If there is a syntax error in the source
+     *  string or a duplicated key.
+     */
+    public JSONObject(String source) throws JSONException {
+        this(new JSONTokener(source));
+    }
+
+
+    /**
+     * Accumulate values under a key. It is similar to the put method except
+     * that if there is already an object stored under the key then a
+     * JSONArray is stored under the key to hold all of the accumulated values.
+     * If there is already a JSONArray, then the new value is appended to it.
+     * In contrast, the put method replaces the previous value.
+     * @param key   A key string.
+     * @param value An object to be accumulated under the key.
+     * @return this.
+     * @throws JSONException If the value is an invalid number
+     *  or if the key is null.
+     */
+    public JSONObject accumulate(String key, Object value)
+            throws JSONException {
+        testValidity(value);
+        Object o = opt(key);
+        if (o == null) {
+            put(key, value instanceof JSONArray ?
+                    new JSONArray().put(value) :
+                    value);
+        } else if (o instanceof JSONArray) {
+            ((JSONArray)o).put(value);
+        } else {
+            put(key, new JSONArray().put(o).put(value));
+        }
+        return this;
+    }
+
+
+    /**
+     * Append values to the array under a key. If the key does not exist in the
+     * JSONObject, then the key is put in the JSONObject with its value being a
+     * JSONArray containing the value parameter. If the key was already
+     * associated with a JSONArray, then the value parameter is appended to it.
+     * @param key   A key string.
+     * @param value An object to be accumulated under the key.
+     * @return this.
+     * @throws JSONException If the key is null or if the current value
+     *  associated with the key is not a JSONArray.
+     */
+    public JSONObject append(String key, Object value) throws JSONException {
+        testValidity(value);
+        Object o = opt(key);
+        if (o == null) {
+            put(key, new JSONArray().put(value));
+        } else if (o instanceof JSONArray) {
+            put(key, ((JSONArray)o).put(value));
+        } else {
+            throw new JSONException("JSONObject[" + key +
+                    "] is not a JSONArray.");
+        }
+        return this;
+    }
+
+
+    /**
+     * Produce a string from a double. The string "null" will be returned if
+     * the number is not finite.
+     * @param  d A double.
+     * @return A String.
+     */
+    static public String doubleToString(double d) {
+        if (Double.isInfinite(d) || Double.isNaN(d)) {
+            return "null";
+        }
+
+// Shave off trailing zeros and decimal point, if possible.
+
+        String s = Double.toString(d);
+        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
+            while (s.endsWith("0")) {
+                s = s.substring(0, s.length() - 1);
+            }
+            if (s.endsWith(".")) {
+                s = s.substring(0, s.length() - 1);
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Get the value object associated with a key.
+     *
+     * @param key   A key string.
+     * @return      The object associated with the key.
+     * @throws   JSONException if the key is not found.
+     */
+    public Object get(String key) throws JSONException {
+        Object o = opt(key);
+        if (o == null) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                    "] not found.");
+        }
+        return o;
+    }
+
+
+    /**
+     * Get the boolean value associated with a key.
+     *
+     * @param key   A key string.
+     * @return      The truth.
+     * @throws   JSONException
+     *  if the value is not a Boolean or the String "true" or "false".
+     */
+    public boolean getBoolean(String key) throws JSONException {
+        Object o = get(key);
+        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("JSONObject[" + quote(key) +
+                "] is not a Boolean.");
+    }
+
+
+    /**
+     * Get the double value associated with a key.
+     * @param key   A key string.
+     * @return      The numeric value.
+     * @throws JSONException if the key is not found or
+     *  if the value is not a Number object and cannot be converted to a number.
+     */
+    public double getDouble(String key) throws JSONException {
+        Object o = get(key);
+        try {
+            return o instanceof Number ?
+                ((Number)o).doubleValue() :
+                Double.valueOf((String)o).doubleValue();
+        } catch (Exception e) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a number.");
+        }
+    }
+
+
+    /**
+     * Get the int value associated with a key. If the number value is too
+     * large for an int, it will be clipped.
+     *
+     * @param key   A key string.
+     * @return      The integer value.
+     * @throws   JSONException if the key is not found or if the value cannot
+     *  be converted to an integer.
+     */
+    public int getInt(String key) throws JSONException {
+        Object o = get(key);
+        return o instanceof Number ?
+                ((Number)o).intValue() : (int)getDouble(key);
+    }
+
+
+    /**
+     * Get the JSONArray value associated with a key.
+     *
+     * @param key   A key string.
+     * @return      A JSONArray which is the value.
+     * @throws   JSONException if the key is not found or
+     *  if the value is not a JSONArray.
+     */
+    public JSONArray getJSONArray(String key) throws JSONException {
+        Object o = get(key);
+        if (o instanceof JSONArray) {
+            return (JSONArray)o;
+        }
+        throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a JSONArray.");
+    }
+
+
+    /**
+     * Get the JSONObject value associated with a key.
+     *
+     * @param key   A key string.
+     * @return      A JSONObject which is the value.
+     * @throws   JSONException if the key is not found or
+     *  if the value is not a JSONObject.
+     */
+    public JSONObject getJSONObject(String key) throws JSONException {
+        Object o = get(key);
+        if (o instanceof JSONObject) {
+            return (JSONObject)o;
+        }
+        throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a JSONObject.");
+    }
+
+
+    /**
+     * Get the long value associated with a key. If the number value is too
+     * long for a long, it will be clipped.
+     *
+     * @param key   A key string.
+     * @return      The long value.
+     * @throws   JSONException if the key is not found or if the value cannot
+     *  be converted to a long.
+     */
+    public long getLong(String key) throws JSONException {
+        Object o = get(key);
+        return o instanceof Number ?
+                ((Number)o).longValue() : (long)getDouble(key);
+    }
+
+
+    /**
+     * Get an array of field names from a JSONObject.
+     *
+     * @return An array of field names, or null if there are no names.
+     */
+    public static String[] getNames(JSONObject jo) {
+        int length = jo.length();
+        if (length == 0) {
+            return null;
+        }
+        Iterator i = jo.keys();
+        String[] names = new String[length];
+        int j = 0;
+        while (i.hasNext()) {
+            names[j] = (String)i.next();
+            j += 1;
+        }
+        return names;
+    }
+
+
+    /**
+     * Get an array of field names from an Object.
+     *
+     * @return An array of field names, or null if there are no names.
+     */
+    public static String[] getNames(Object object) {
+        if (object == null) {
+            return null;
+        }
+        Class klass = object.getClass();
+        Field[] fields = klass.getFields();
+        int length = fields.length;
+        if (length == 0) {
+            return null;
+        }
+        String[] names = new String[length];
+        for (int i = 0; i < length; i += 1) {
+            names[i] = fields[i].getName();
+        }
+        return names;
+    }
+
+
+    /**
+     * Get the string associated with a key.
+     *
+     * @param key   A key string.
+     * @return      A string which is the value.
+     * @throws   JSONException if the key is not found.
+     */
+    public String getString(String key) throws JSONException {
+        return get(key).toString();
+    }
+
+
+    /**
+     * Determine if the JSONObject contains a specific key.
+     * @param key   A key string.
+     * @return      true if the key exists in the JSONObject.
+     */
+    public boolean has(String key) {
+        return this.map.containsKey(key);
+    }
+    
+    
+    /**
+     * Increment a property of a JSONObject. If there is no such property,
+     * create one with a value of 1. If there is such a property, and if
+     * it is an Integer, Long, Double, or Float, then add one to it.
+     * @param key  A key string.
+     * @return this.
+     * @throws JSONException If there is already a property with this name
+     * that is not an Integer, Long, Double, or Float.
+     */
+    public JSONObject increment(String key) throws JSONException {
+    	Object value = opt(key);
+    	if (value == null) {
+    		put(key, 1);
+    	} else {
+    		if (value instanceof Integer) {
+    			put(key, ((Integer)value).intValue() + 1);
+    		} else if (value instanceof Long) {
+    			put(key, ((Long)value).longValue() + 1);    			
+    		} else if (value instanceof Double) {
+	    		put(key, ((Double)value).doubleValue() + 1);    			
+    		} else if (value instanceof Float) {
+	    		put(key, ((Float)value).floatValue() + 1);    			
+		    } else {
+		    	throw new JSONException("Unable to increment [" + key + "].");
+		    }
+	    }
+    	return this;
+    }
+
+
+    /**
+     * Determine if the value associated with the key is null or if there is
+     *  no value.
+     * @param key   A key string.
+     * @return      true if there is no value associated with the key or if
+     *  the value is the JSONObject.NULL object.
+     */
+    public boolean isNull(String key) {
+        return JSONObject.NULL.equals(opt(key));
+    }
+
+
+    /**
+     * Get an enumeration of the keys of the JSONObject.
+     *
+     * @return An iterator of the keys.
+     */
+    public Iterator keys() {
+        return this.map.keySet().iterator();
+    }
+
+
+    /**
+     * Get the number of keys stored in the JSONObject.
+     *
+     * @return The number of keys in the JSONObject.
+     */
+    public int length() {
+        return this.map.size();
+    }
+
+
+    /**
+     * Produce a JSONArray containing the names of the elements of this
+     * JSONObject.
+     * @return A JSONArray containing the key strings, or null if the JSONObject
+     * is empty.
+     */
+    public JSONArray names() {
+        JSONArray ja = new JSONArray();
+        Iterator  keys = keys();
+        while (keys.hasNext()) {
+            ja.put(keys.next());
+        }
+        return ja.length() == 0 ? null : ja;
+    }
+
+    /**
+     * Produce a string from a Number.
+     * @param  n A Number
+     * @return A String.
+     * @throws JSONException If n is a non-finite number.
+     */
+    static public String numberToString(Number n)
+            throws JSONException {
+        if (n == null) {
+            throw new JSONException("Null pointer");
+        }
+        testValidity(n);
+
+// Shave off trailing zeros and decimal point, if possible.
+
+        String s = n.toString();
+        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
+            while (s.endsWith("0")) {
+                s = s.substring(0, s.length() - 1);
+            }
+            if (s.endsWith(".")) {
+                s = s.substring(0, s.length() - 1);
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Get an optional value associated with a key.
+     * @param key   A key string.
+     * @return      An object which is the value, or null if there is no value.
+     */
+    public Object opt(String key) {
+        return key == null ? null : this.map.get(key);
+    }
+
+
+    /**
+     * Get an optional boolean associated with a key.
+     * It returns false if there is no such key, or if the value is not
+     * Boolean.TRUE or the String "true".
+     *
+     * @param key   A key string.
+     * @return      The truth.
+     */
+    public boolean optBoolean(String key) {
+        return optBoolean(key, false);
+    }
+
+
+    /**
+     * Get an optional boolean associated with a key.
+     * It returns the defaultValue if there is no such key, or if it is not
+     * a Boolean or the String "true" or "false" (case insensitive).
+     *
+     * @param key              A key string.
+     * @param defaultValue     The default.
+     * @return      The truth.
+     */
+    public boolean optBoolean(String key, boolean defaultValue) {
+        try {
+            return getBoolean(key);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional double associated with a key,
+     * or NaN if there is no such key or if its value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A string which is the key.
+     * @return      An object which is the value.
+     */
+    public double optDouble(String key) {
+        return optDouble(key, Double.NaN);
+    }
+
+
+    /**
+     * Get an optional double associated with a key, or the
+     * defaultValue if there is no such key or if its value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      An object which is the value.
+     */
+    public double optDouble(String key, double defaultValue) {
+        try {
+            Object o = opt(key);
+            return o instanceof Number ? ((Number)o).doubleValue() :
+                new Double((String)o).doubleValue();
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional int value associated with a key,
+     * or zero if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @return      An object which is the value.
+     */
+    public int optInt(String key) {
+        return optInt(key, 0);
+    }
+
+
+    /**
+     * Get an optional int value associated with a key,
+     * or the default if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      An object which is the value.
+     */
+    public int optInt(String key, int defaultValue) {
+        try {
+            return getInt(key);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional JSONArray associated with a key.
+     * It returns null if there is no such key, or if its value is not a
+     * JSONArray.
+     *
+     * @param key   A key string.
+     * @return      A JSONArray which is the value.
+     */
+    public JSONArray optJSONArray(String key) {
+        Object o = opt(key);
+        return o instanceof JSONArray ? (JSONArray)o : null;
+    }
+
+
+    /**
+     * Get an optional JSONObject associated with a key.
+     * It returns null if there is no such key, or if its value is not a
+     * JSONObject.
+     *
+     * @param key   A key string.
+     * @return      A JSONObject which is the value.
+     */
+    public JSONObject optJSONObject(String key) {
+        Object o = opt(key);
+        return o instanceof JSONObject ? (JSONObject)o : null;
+    }
+
+
+    /**
+     * Get an optional long value associated with a key,
+     * or zero if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @return      An object which is the value.
+     */
+    public long optLong(String key) {
+        return optLong(key, 0);
+    }
+
+
+    /**
+     * Get an optional long value associated with a key,
+     * or the default if there is no such key or if the value is not a number.
+     * If the value is a string, an attempt will be made to evaluate it as
+     * a number.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      An object which is the value.
+     */
+    public long optLong(String key, long defaultValue) {
+        try {
+            return getLong(key);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+
+    /**
+     * Get an optional string associated with a key.
+     * It returns an empty string if there is no such key. If the value is not
+     * a string and is not null, then it is coverted to a string.
+     *
+     * @param key   A key string.
+     * @return      A string which is the value.
+     */
+    public String optString(String key) {
+        return optString(key, "");
+    }
+
+
+    /**
+     * Get an optional string associated with a key.
+     * It returns the defaultValue if there is no such key.
+     *
+     * @param key   A key string.
+     * @param defaultValue     The default.
+     * @return      A string which is the value.
+     */
+    public String optString(String key, String defaultValue) {
+        Object o = opt(key);
+        return o != null ? o.toString() : defaultValue;
+    }
+
+
+    private void populateMap(Object bean) {
+        Class klass = bean.getClass();
+
+// If klass is a System class then set includeSuperClass to false. 
+
+        boolean includeSuperClass = klass.getClassLoader() != null;
+
+        Method[] methods = (includeSuperClass) ?
+                klass.getMethods() : klass.getDeclaredMethods();
+        for (int i = 0; i < methods.length; i += 1) {
+            try {
+                Method method = methods[i];
+                if (Modifier.isPublic(method.getModifiers())) {
+                    String name = method.getName();
+                    String key = "";
+                    if (name.startsWith("get")) {
+                    	if (name.equals("getClass") || 
+                    			name.equals("getDeclaringClass")) {
+                    		key = "";
+                    	} else {
+                    		key = name.substring(3);
+                    	}
+                    } else if (name.startsWith("is")) {
+                        key = name.substring(2);
+                    }
+                    if (key.length() > 0 &&
+                            Character.isUpperCase(key.charAt(0)) &&
+                            method.getParameterTypes().length == 0) {
+                        if (key.length() == 1) {
+                            key = key.toLowerCase();
+                        } else if (!Character.isUpperCase(key.charAt(1))) {
+                            key = key.substring(0, 1).toLowerCase() +
+                                key.substring(1);
+                        }
+
+                        Object result = method.invoke(bean, (Object[])null);
+
+                        map.put(key, wrap(result));
+                    }
+                }
+            } catch (Exception ignore) {
+            }
+        }
+    }
+
+
+    /**
+     * Put a key/boolean pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value A boolean which is the value.
+     * @return this.
+     * @throws JSONException If the key is null.
+     */
+    public JSONObject put(String key, boolean value) throws JSONException {
+        put(key, value ? Boolean.TRUE : Boolean.FALSE);
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, where the value will be a
+     * JSONArray which is produced from a Collection.
+     * @param key   A key string.
+     * @param value A Collection value.
+     * @return      this.
+     * @throws JSONException
+     */
+    public JSONObject put(String key, Collection value) throws JSONException {
+        put(key, new JSONArray(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/double pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value A double which is the value.
+     * @return this.
+     * @throws JSONException If the key is null or if the number is invalid.
+     */
+    public JSONObject put(String key, double value) throws JSONException {
+        put(key, new Double(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/int pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value An int which is the value.
+     * @return this.
+     * @throws JSONException If the key is null.
+     */
+    public JSONObject put(String key, int value) throws JSONException {
+        put(key, new Integer(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/long pair in the JSONObject.
+     *
+     * @param key   A key string.
+     * @param value A long which is the value.
+     * @return this.
+     * @throws JSONException If the key is null.
+     */
+    public JSONObject put(String key, long value) throws JSONException {
+        put(key, new Long(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, where the value will be a
+     * JSONObject which is produced from a Map.
+     * @param key   A key string.
+     * @param value A Map value.
+     * @return      this.
+     * @throws JSONException
+     */
+    public JSONObject put(String key, Map value) throws JSONException {
+        put(key, new JSONObject(value));
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject. If the value is null,
+     * then the key will be removed from the JSONObject if it is present.
+     * @param key   A key string.
+     * @param value An object which is the value. It should be of one of these
+     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+     *  or the JSONObject.NULL object.
+     * @return this.
+     * @throws JSONException If the value is non-finite number
+     *  or if the key is null.
+     */
+    public JSONObject put(String key, Object value) throws JSONException {
+        if (key == null) {
+            throw new JSONException("Null key.");
+        }
+        if (value != null) {
+            testValidity(value);
+            this.map.put(key, value);
+        } else {
+            remove(key);
+        }
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, but only if the key and the
+     * value are both non-null, and only if there is not already a member
+     * with that name.
+     * @param key
+     * @param value
+     * @return his.
+     * @throws JSONException if the key is a duplicate
+     */
+    public JSONObject putOnce(String key, Object value) throws JSONException {
+        if (key != null && value != null) {
+            if (opt(key) != null) {
+                throw new JSONException("Duplicate key \"" + key + "\"");
+            }
+            put(key, value);
+        }
+        return this;
+    }
+
+
+    /**
+     * Put a key/value pair in the JSONObject, but only if the
+     * key and the value are both non-null.
+     * @param key   A key string.
+     * @param value An object which is the value. It should be of one of these
+     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+     *  or the JSONObject.NULL object.
+     * @return this.
+     * @throws JSONException If the value is a non-finite number.
+     */
+    public JSONObject putOpt(String key, Object value) throws JSONException {
+        if (key != null && value != null) {
+            put(key, value);
+        }
+        return this;
+    }
+
+
+    /**
+     * Produce a string in double quotes with backslash sequences in all the
+     * right places. A backslash will be inserted within </, allowing JSON
+     * text to be delivered in HTML. In JSON text, a string cannot contain a
+     * control character or an unescaped quote or backslash.
+     * @param string A String
+     * @return  A String correctly formatted for insertion in a JSON text.
+     */
+    public static String quote(String string) {
+        if (string == null || string.length() == 0) {
+            return "\"\"";
+        }
+
+        char         b;
+        char         c = 0;
+        int          i;
+        int          len = string.length();
+        StringBuffer sb = new StringBuffer(len + 4);
+        String       t;
+
+        sb.append('"');
+        for (i = 0; i < len; i += 1) {
+            b = c;
+            c = string.charAt(i);
+            switch (c) {
+            case '\\':
+            case '"':
+                sb.append('\\');
+                sb.append(c);
+                break;
+            case '/':
+                if (b == '<') {
+                    sb.append('\\');
+                }
+                sb.append(c);
+                break;
+            case '\b':
+                sb.append("\\b");
+                break;
+            case '\t':
+                sb.append("\\t");
+                break;
+            case '\n':
+                sb.append("\\n");
+                break;
+            case '\f':
+                sb.append("\\f");
+                break;
+            case '\r':
+                sb.append("\\r");
+                break;
+            default:
+                if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
+                               (c >= '\u2000' && c < '\u2100')) {
+                    t = "000" + Integer.toHexString(c);
+                    sb.append("\\u" + t.substring(t.length() - 4));
+                } else {
+                    sb.append(c);
+                }
+            }
+        }
+        sb.append('"');
+        return sb.toString();
+    }
+
+    /**
+     * Remove a name and its value, if present.
+     * @param key The name to be removed.
+     * @return The value that was associated with the name,
+     * or null if there was no value.
+     */
+    public Object remove(String key) {
+        return this.map.remove(key);
+    }
+
+    /**
+     * Get an enumeration of the keys of the JSONObject.
+     * The keys will be sorted alphabetically.
+     *
+     * @return An iterator of the keys.
+     */
+    public Iterator sortedKeys() {
+      return new TreeSet(this.map.keySet()).iterator();
+    }
+
+    /**
+     * Try to convert a string into a number, boolean, or null. If the string
+     * can't be converted, return the string.
+     * @param s A String.
+     * @return A simple JSON value.
+     */
+    static public Object stringToValue(String s) {
+        if (s.equals("")) {
+            return s;
+        }
+        if (s.equalsIgnoreCase("true")) {
+            return Boolean.TRUE;
+        }
+        if (s.equalsIgnoreCase("false")) {
+            return Boolean.FALSE;
+        }
+        if (s.equalsIgnoreCase("null")) {
+            return JSONObject.NULL;
+        }
+
+        /*
+         * If it might be a number, try converting it. 
+         * We support the non-standard 0x- convention. 
+         * If a number cannot be produced, then the value will just
+         * be a string. Note that the 0x-, plus, and implied string
+         * conventions are non-standard. A JSON parser may accept
+         * non-JSON forms as long as it accepts all correct JSON forms.
+         */
+
+        char b = s.charAt(0);
+        if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
+            if (b == '0' && s.length() > 2 &&
+                        (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
+                try {
+                    return new Integer(Integer.parseInt(s.substring(2), 16));
+                } catch (Exception ignore) {
+                }
+            }
+            try {
+                if (s.indexOf('.') > -1 || s.indexOf('e') > -1 || s.indexOf('E') > -1) {
+                    return Double.valueOf(s);
+                } else {
+                    Long myLong = new Long(s);
+                    if (myLong.longValue() == myLong.intValue()) {
+                        return new Integer(myLong.intValue());
+                    } else {
+                        return myLong;
+                    }
+                }
+            }  catch (Exception ignore) {
+            }
+        }
+        return s;
+    }
+
+
+    /**
+     * Throw an exception if the object is an NaN or infinite number.
+     * @param o The object to test.
+     * @throws JSONException If o is a non-finite number.
+     */
+    static void testValidity(Object o) throws JSONException {
+        if (o != null) {
+            if (o instanceof Double) {
+                if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
+                    throw new JSONException(
+                        "JSON does not allow non-finite numbers.");
+                }
+            } else if (o instanceof Float) {
+                if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
+                    throw new JSONException(
+                        "JSON does not allow non-finite numbers.");
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Produce a JSONArray containing the values of the members of this
+     * JSONObject.
+     * @param names A JSONArray containing a list of key strings. This
+     * determines the sequence of the values in the result.
+     * @return A JSONArray of values.
+     * @throws JSONException If any of the values are non-finite numbers.
+     */
+    public JSONArray toJSONArray(JSONArray names) throws JSONException {
+        if (names == null || names.length() == 0) {
+            return null;
+        }
+        JSONArray ja = new JSONArray();
+        for (int i = 0; i < names.length(); i += 1) {
+            ja.put(this.opt(names.getString(i)));
+        }
+        return ja;
+    }
+
+    /**
+     * Make a JSON text of this JSONObject. For compactness, no whitespace
+     * is added. If this would not result in a syntactically correct JSON text,
+     * then null will be returned instead.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return a printable, displayable, portable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     */
+    public String toString() {
+        try {
+            Iterator     keys = keys();
+            StringBuffer sb = new StringBuffer("{");
+
+            while (keys.hasNext()) {
+                if (sb.length() > 1) {
+                    sb.append(',');
+                }
+                Object o = keys.next();
+                sb.append(quote(o.toString()));
+                sb.append(':');
+                sb.append(valueToString(this.map.get(o)));
+            }
+            sb.append('}');
+            return sb.toString();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONObject.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @return a printable, displayable, portable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+    public String toString(int indentFactor) throws JSONException {
+        return toString(indentFactor, 0);
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of this JSONObject.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @param indent The indentation of the top level.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+    String toString(int indentFactor, int indent) throws JSONException {
+        int j;
+        int n = length();
+        if (n == 0) {
+            return "{}";
+        }
+        Iterator     keys = sortedKeys();
+        StringBuffer sb = new StringBuffer("{");
+        int          newindent = indent + indentFactor;
+        Object       o;
+        if (n == 1) {
+            o = keys.next();
+            sb.append(quote(o.toString()));
+            sb.append(": ");
+            sb.append(valueToString(this.map.get(o), indentFactor,
+                    indent));
+        } else {
+            while (keys.hasNext()) {
+                o = keys.next();
+                if (sb.length() > 1) {
+                    sb.append(",\n");
+                } else {
+                    sb.append('\n');
+                }
+                for (j = 0; j < newindent; j += 1) {
+                    sb.append(' ');
+                }
+                sb.append(quote(o.toString()));
+                sb.append(": ");
+                sb.append(valueToString(this.map.get(o), indentFactor,
+                        newindent));
+            }
+            if (sb.length() > 1) {
+                sb.append('\n');
+                for (j = 0; j < indent; j += 1) {
+                    sb.append(' ');
+                }
+            }
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+
+    /**
+     * Make a JSON text of an Object value. If the object has an
+     * value.toJSONString() method, then that method will be used to produce
+     * the JSON text. The method is required to produce a strictly
+     * conforming text. If the object does not contain a toJSONString
+     * method (which is the most common case), then a text will be
+     * produced by other means. If the value is an array or Collection,
+     * then a JSONArray will be made from it and its toJSONString method
+     * will be called. If the value is a MAP, then a JSONObject will be made
+     * from it and its toJSONString method will be called. Otherwise, the
+     * value's toString method will be called, and the result will be quoted.
+     *
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param value The value to be serialized.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the value is or contains an invalid number.
+     */
+    static String valueToString(Object value) throws JSONException {
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        if (value instanceof JSONString) {
+            Object o;
+            try {
+                o = ((JSONString)value).toJSONString();
+            } catch (Exception e) {
+                throw new JSONException(e);
+            }
+            if (o instanceof String) {
+                return (String)o;
+            }
+            throw new JSONException("Bad value from toJSONString: " + o);
+        }
+        if (value instanceof Number) {
+            return numberToString((Number) value);
+        }
+        if (value instanceof Boolean || value instanceof JSONObject ||
+                value instanceof JSONArray) {
+            return value.toString();
+        }
+        if (value instanceof Map) {
+            return new JSONObject((Map)value).toString();
+        }
+        if (value instanceof Collection) {
+            return new JSONArray((Collection)value).toString();
+        }
+        if (value.getClass().isArray()) {
+            return new JSONArray(value).toString();
+        }
+        return quote(value.toString());
+    }
+
+
+    /**
+     * Make a prettyprinted JSON text of an object value.
+     * <p>
+     * Warning: This method assumes that the data structure is acyclical.
+     * @param value The value to be serialized.
+     * @param indentFactor The number of spaces to add to each level of
+     *  indentation.
+     * @param indent The indentation of the top level.
+     * @return a printable, displayable, transmittable
+     *  representation of the object, beginning
+     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
+     * @throws JSONException If the object contains an invalid number.
+     */
+     static String valueToString(Object value, int indentFactor, int indent)
+            throws JSONException {
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        try {
+            if (value instanceof JSONString) {
+                Object o = ((JSONString)value).toJSONString();
+                if (o instanceof String) {
+                    return (String)o;
+                }
+            }
+        } catch (Exception ignore) {
+        }
+        if (value instanceof Number) {
+            return numberToString((Number) value);
+        }
+        if (value instanceof Boolean) {
+            return value.toString();
+        }
+        if (value instanceof JSONObject) {
+            return ((JSONObject)value).toString(indentFactor, indent);
+        }
+        if (value instanceof JSONArray) {
+            return ((JSONArray)value).toString(indentFactor, indent);
+        }
+        if (value instanceof Map) {
+            return new JSONObject((Map)value).toString(indentFactor, indent);
+        }
+        if (value instanceof Collection) {
+            return new JSONArray((Collection)value).toString(indentFactor, indent);
+        }
+        if (value.getClass().isArray()) {
+            return new JSONArray(value).toString(indentFactor, indent);
+        }
+        return quote(value.toString());
+    }
+
+
+     /**
+      * Wrap an object, if necessary. If the object is null, return the NULL 
+      * object. If it is an array or collection, wrap it in a JSONArray. If 
+      * it is a map, wrap it in a JSONObject. If it is a standard property 
+      * (Double, String, et al) then it is already wrapped. Otherwise, if it 
+      * comes from one of the java packages, turn it into a string. And if 
+      * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
+      * then null is returned.
+      *
+      * @param object The object to wrap
+      * @return The wrapped value
+      */
+     static Object wrap(Object object) {
+         try {
+             if (object == null) {
+                 return NULL;
+             }
+             if (object instanceof JSONObject || object instanceof JSONArray || 
+            		 object instanceof Byte   || object instanceof Character ||
+                     object instanceof Short  || object instanceof Integer   ||
+                     object instanceof Long   || object instanceof Boolean   || 
+                     object instanceof Float  || object instanceof Double    ||
+                     object instanceof String || NULL.equals(object)) {
+                 return object;
+             }
+             
+             if (object instanceof Collection) {
+                 return new JSONArray((Collection)object);
+             }
+             if (object.getClass().isArray()) {
+                 return new JSONArray(object);
+             }
+             if (object instanceof Map) {
+                 return new JSONObject((Map)object);
+             }
+             Package objectPackage = object.getClass().getPackage();
+             String objectPackageName = ( objectPackage != null ? objectPackage.getName() : "" );
+             if (objectPackageName.startsWith("java.") ||
+            		 objectPackageName.startsWith("javax.") ||
+            		 object.getClass().getClassLoader() == null) {
+                 return object.toString();
+             }
+             return new JSONObject(object);
+         } catch(Exception exception) {
+             return null;
+         }
+     }
+
+     
+     /**
+      * Write the contents of the JSONObject as JSON text to a writer.
+      * For compactness, no whitespace is added.
+      * <p>
+      * Warning: This method assumes that the data structure is acyclical.
+      *
+      * @return The writer.
+      * @throws JSONException
+      */
+     public Writer write(Writer writer) throws JSONException {
+        try {
+            boolean  b = false;
+            Iterator keys = keys();
+            writer.write('{');
+
+            while (keys.hasNext()) {
+                if (b) {
+                    writer.write(',');
+                }
+                Object k = keys.next();
+                writer.write(quote(k.toString()));
+                writer.write(':');
+                Object v = this.map.get(k);
+                if (v instanceof JSONObject) {
+                    ((JSONObject)v).write(writer);
+                } else if (v instanceof JSONArray) {
+                    ((JSONArray)v).write(writer);
+                } else {
+                    writer.write(valueToString(v));
+                }
+                b = true;
+            }
+            writer.write('}');
+            return writer;
+        } catch (IOException exception) {
+            throw new JSONException(exception);
+        }
+     }
+}
\ No newline at end of file


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONObject.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONString.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONString.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONString.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,18 @@
+package org.json;
+/**
+ * The <code>JSONString</code> interface allows a <code>toJSONString()</code> 
+ * method so that a class can change the behavior of 
+ * <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
+ * and <code>JSONWriter.value(</code>Object<code>)</code>. The 
+ * <code>toJSONString</code> method will be used instead of the default behavior 
+ * of using the Object's <code>toString()</code> method and quoting the result.
+ */
+public interface JSONString {
+	/**
+	 * The <code>toJSONString</code> method allows a class to produce its own JSON 
+	 * serialization. 
+	 * 
+	 * @return A strictly syntactically correct JSON text.
+	 */
+	public String toJSONString();
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONString.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONStringer.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONStringer.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONStringer.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,78 @@
+package org.json;
+
+/*
+Copyright (c) 2006 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.StringWriter;
+
+/**
+ * JSONStringer provides a quick and convenient way of producing JSON text.
+ * The texts produced strictly conform to JSON syntax rules. No whitespace is
+ * added, so the results are ready for transmission or storage. Each instance of
+ * JSONStringer can produce one JSON text.
+ * <p>
+ * A JSONStringer instance provides a <code>value</code> method for appending
+ * values to the
+ * text, and a <code>key</code>
+ * method for adding keys before values in objects. There are <code>array</code>
+ * and <code>endArray</code> methods that make and bound array values, and
+ * <code>object</code> and <code>endObject</code> methods which make and bound
+ * object values. All of these methods return the JSONWriter instance,
+ * permitting cascade style. For example, <pre>
+ * myString = new JSONStringer()
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject()
+ *     .toString();</pre> which produces the string <pre>
+ * {"JSON":"Hello, World!"}</pre>
+ * <p>
+ * The first method called must be <code>array</code> or <code>object</code>.
+ * There are no methods for adding commas or colons. JSONStringer adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * <p>
+ * This can sometimes be easier than using a JSONObject to build a string.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class JSONStringer extends JSONWriter {
+    /**
+     * Make a fresh JSONStringer. It can be used to build one JSON text.
+     */
+    public JSONStringer() {
+        super(new StringWriter());
+    }
+
+    /**
+     * Return the JSON text. This method is used to obtain the product of the
+     * JSONStringer instance. It will return <code>null</code> if there was a
+     * problem in the construction of the JSON text (such as the calls to
+     * <code>array</code> were not properly balanced with calls to
+     * <code>endArray</code>).
+     * @return The JSON text.
+     */
+    public String toString() {
+        return this.mode == 'd' ? this.writer.toString() : null;
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONStringer.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONTokener.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONTokener.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONTokener.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,435 @@
+package org.json;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+/*
+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.
+*/
+
+/**
+ * A JSONTokener takes a source string and extracts characters and tokens from
+ * it. It is used by the JSONObject and JSONArray constructors to parse
+ * JSON source strings.
+ * @author JSON.org
+ * @version 2010-02-02
+ */
+public class JSONTokener {
+
+    private int 	character;
+	private boolean eof;
+    private int 	index;
+    private int 	line;
+    private char 	previous;
+    private Reader 	reader;
+    private boolean usePrevious;
+
+
+    /**
+     * Construct a JSONTokener from a reader.
+     *
+     * @param reader     A reader.
+     */
+    public JSONTokener(Reader reader) {
+        this.reader = reader.markSupported() ? 
+        		reader : new BufferedReader(reader);
+        this.eof = false;
+        this.usePrevious = false;
+        this.previous = 0;
+        this.index = 0;
+        this.character = 1;
+        this.line = 1;
+    }
+
+
+    /**
+     * Construct a JSONTokener from a string.
+     *
+     * @param s     A source string.
+     */
+    public JSONTokener(String s) {
+        this(new StringReader(s));
+    }
+
+
+    /**
+     * Back up one character. This provides a sort of lookahead capability,
+     * so that you can test for a digit or letter before attempting to parse
+     * the next number or identifier.
+     */
+    public void back() throws JSONException {
+        if (usePrevious || index <= 0) {
+            throw new JSONException("Stepping back two steps is not supported");
+        }
+        this.index -= 1;
+        this.character -= 1;
+        this.usePrevious = true;
+        this.eof = false;
+    }
+
+
+    /**
+     * Get the hex value of a character (base16).
+     * @param c A character between '0' and '9' or between 'A' and 'F' or
+     * between 'a' and 'f'.
+     * @return  An int between 0 and 15, or -1 if c was not a hex digit.
+     */
+    public static int dehexchar(char c) {
+        if (c >= '0' && c <= '9') {
+            return c - '0';
+        }
+        if (c >= 'A' && c <= 'F') {
+            return c - ('A' - 10);
+        }
+        if (c >= 'a' && c <= 'f') {
+            return c - ('a' - 10);
+        }
+        return -1;
+    }
+    
+    public boolean end() {
+    	return eof && !usePrevious;    	
+    }
+
+
+    /**
+     * Determine if the source string still contains characters that next()
+     * can consume.
+     * @return true if not yet at the end of the source.
+     */
+    public boolean more() throws JSONException {
+        next();
+        if (end()) {
+            return false;
+        } 
+        back();
+        return true;
+    }
+
+
+    /**
+     * Get the next character in the source string.
+     *
+     * @return The next character, or 0 if past the end of the source string.
+     */
+    public char next() throws JSONException {
+        int c;
+        if (this.usePrevious) {
+        	this.usePrevious = false;
+            c = this.previous;
+        } else {
+	        try {
+	            c = this.reader.read();
+	        } catch (IOException exception) {
+	            throw new JSONException(exception);
+	        }
+	
+	        if (c <= 0) { // End of stream
+	        	this.eof = true;
+	        	c = 0;
+	        } 
+        }
+    	this.index += 1;
+    	if (this.previous == '\r') {
+    		this.line += 1;
+    		this.character = c == '\n' ? 0 : 1;
+    	} else if (c == '\n') {
+    		this.line += 1;
+    		this.character = 0;
+    	} else {
+    		this.character += 1;
+    	}
+    	this.previous = (char) c;
+        return this.previous;
+    }
+
+
+    /**
+     * Consume the next character, and check that it matches a specified
+     * character.
+     * @param c The character to match.
+     * @return The character.
+     * @throws JSONException if the character does not match.
+     */
+    public char next(char c) throws JSONException {
+        char n = next();
+        if (n != c) {
+            throw syntaxError("Expected '" + c + "' and instead saw '" +
+                    n + "'");
+        }
+        return n;
+    }
+
+
+    /**
+     * Get the next n characters.
+     *
+     * @param n     The number of characters to take.
+     * @return      A string of n characters.
+     * @throws JSONException
+     *   Substring bounds error if there are not
+     *   n characters remaining in the source string.
+     */
+     public String next(int n) throws JSONException {
+         if (n == 0) {
+             return "";
+         }
+
+         char[] buffer = new char[n];
+         int pos = 0;
+
+         while (pos < n) {
+             buffer[pos] = next();
+             if (end()) {
+                 throw syntaxError("Substring bounds error");                 
+             }
+             pos += 1;
+         }
+         return new String(buffer);
+     }
+
+
+    /**
+     * Get the next char in the string, skipping whitespace.
+     * @throws JSONException
+     * @return  A character, or 0 if there are no more characters.
+     */
+    public char nextClean() throws JSONException {
+        for (;;) {
+            char c = next();
+            if (c == 0 || c > ' ') {
+                return c;
+            }
+        }
+    }
+
+
+    /**
+     * Return the characters up to the next close quote character.
+     * Backslash processing is done. The formal JSON format does not
+     * allow strings in single quotes, but an implementation is allowed to
+     * accept them.
+     * @param quote The quoting character, either
+     *      <code>"</code>&nbsp;<small>(double quote)</small> or
+     *      <code>'</code>&nbsp;<small>(single quote)</small>.
+     * @return      A String.
+     * @throws JSONException Unterminated string.
+     */
+    public String nextString(char quote) throws JSONException {
+        char c;
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            c = next();
+            switch (c) {
+            case 0:
+            case '\n':
+            case '\r':
+                throw syntaxError("Unterminated string");
+            case '\\':
+                c = next();
+                switch (c) {
+                case 'b':
+                    sb.append('\b');
+                    break;
+                case 't':
+                    sb.append('\t');
+                    break;
+                case 'n':
+                    sb.append('\n');
+                    break;
+                case 'f':
+                    sb.append('\f');
+                    break;
+                case 'r':
+                    sb.append('\r');
+                    break;
+                case 'u':
+                    sb.append((char)Integer.parseInt(next(4), 16));
+                    break;
+                case '"':
+                case '\'':
+                case '\\':
+                case '/':
+                	sb.append(c);
+                	break;
+                default:
+                    throw syntaxError("Illegal escape.");
+                }
+                break;
+            default:
+                if (c == quote) {
+                    return sb.toString();
+                }
+                sb.append(c);
+            }
+        }
+    }
+
+
+    /**
+     * Get the text up but not including the specified character or the
+     * end of line, whichever comes first.
+     * @param  d A delimiter character.
+     * @return   A string.
+     */
+    public String nextTo(char d) throws JSONException {
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            char c = next();
+            if (c == d || c == 0 || c == '\n' || c == '\r') {
+                if (c != 0) {
+                    back();
+                }
+                return sb.toString().trim();
+            }
+            sb.append(c);
+        }
+    }
+
+
+    /**
+     * Get the text up but not including one of the specified delimiter
+     * characters or the end of line, whichever comes first.
+     * @param delimiters A set of delimiter characters.
+     * @return A string, trimmed.
+     */
+    public String nextTo(String delimiters) throws JSONException {
+        char c;
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            c = next();
+            if (delimiters.indexOf(c) >= 0 || c == 0 ||
+                    c == '\n' || c == '\r') {
+                if (c != 0) {
+                    back();
+                }
+                return sb.toString().trim();
+            }
+            sb.append(c);
+        }
+    }
+
+
+    /**
+     * Get the next value. The value can be a Boolean, Double, Integer,
+     * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
+     * @throws JSONException If syntax error.
+     *
+     * @return An object.
+     */
+    public Object nextValue() throws JSONException {
+        char c = nextClean();
+        String s;
+
+        switch (c) {
+            case '"':
+            case '\'':
+                return nextString(c);
+            case '{':
+                back();
+                return new JSONObject(this);
+            case '[':
+            case '(':
+                back();
+                return new JSONArray(this);
+        }
+
+        /*
+         * Handle unquoted text. This could be the values true, false, or
+         * null, or it can be a number. An implementation (such as this one)
+         * is allowed to also accept non-standard forms.
+         *
+         * Accumulate characters until we reach the end of the text or a
+         * formatting character.
+         */
+
+        StringBuffer sb = new StringBuffer();
+        while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
+            sb.append(c);
+            c = next();
+        }
+        back();
+
+        s = sb.toString().trim();
+        if (s.equals("")) {
+            throw syntaxError("Missing value");
+        }
+        return JSONObject.stringToValue(s);
+    }
+
+
+    /**
+     * Skip characters until the next character is the requested character.
+     * If the requested character is not found, no characters are skipped.
+     * @param to A character to skip to.
+     * @return The requested character, or zero if the requested character
+     * is not found.
+     */
+    public char skipTo(char to) throws JSONException {
+        char c;
+        try {
+            int startIndex = this.index;
+            int startCharacter = this.character;
+            int startLine = this.line;
+            reader.mark(Integer.MAX_VALUE);
+            do {
+                c = next();
+                if (c == 0) {
+                    reader.reset();
+                    this.index = startIndex;
+                    this.character = startCharacter;
+                    this.line = startLine;
+                    return c;
+                }
+            } while (c != to);
+        } catch (IOException exc) {
+            throw new JSONException(exc);
+        }
+
+        back();
+        return c;
+    }
+    
+
+    /**
+     * Make a JSONException to signal a syntax error.
+     *
+     * @param message The error message.
+     * @return  A JSONException object, suitable for throwing
+     */
+    public JSONException syntaxError(String message) {
+        return new JSONException(message + toString());
+    }
+
+
+    /**
+     * Make a printable string of this JSONTokener.
+     *
+     * @return " at {index} [character {character} line {line}]"
+     */
+    public String toString() {
+        return " at " + index + " [character " + this.character + " line " + this.line + "]";
+    }
+}
\ No newline at end of file


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONTokener.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/JSONWriter.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/JSONWriter.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/JSONWriter.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,323 @@
+package org.json;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/*
+Copyright (c) 2006 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.
+*/
+
+/**
+ * JSONWriter provides a quick and convenient way of producing JSON text.
+ * The texts produced strictly conform to JSON syntax rules. No whitespace is
+ * added, so the results are ready for transmission or storage. Each instance of
+ * JSONWriter can produce one JSON text.
+ * <p>
+ * A JSONWriter instance provides a <code>value</code> method for appending
+ * values to the
+ * text, and a <code>key</code>
+ * method for adding keys before values in objects. There are <code>array</code>
+ * and <code>endArray</code> methods that make and bound array values, and
+ * <code>object</code> and <code>endObject</code> methods which make and bound
+ * object values. All of these methods return the JSONWriter instance,
+ * permitting a cascade style. For example, <pre>
+ * new JSONWriter(myWriter)
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject();</pre> which writes <pre>
+ * {"JSON":"Hello, World!"}</pre>
+ * <p>
+ * The first method called must be <code>array</code> or <code>object</code>.
+ * There are no methods for adding commas or colons. JSONWriter adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * <p>
+ * This can sometimes be easier than using a JSONObject to build a string.
+ * @author JSON.org
+ * @version 2010-03-11
+ */
+public class JSONWriter {
+    private static final int maxdepth = 20;
+
+    /**
+     * The comma flag determines if a comma should be output before the next
+     * value.
+     */
+    private boolean comma;
+
+    /**
+     * The current mode. Values:
+     * 'a' (array),
+     * 'd' (done),
+     * 'i' (initial),
+     * 'k' (key),
+     * 'o' (object).
+     */
+    protected char mode;
+
+    /**
+     * The object/array stack.
+     */
+    private JSONObject stack[];
+
+    /**
+     * The stack top index. A value of 0 indicates that the stack is empty.
+     */
+    private int top;
+
+    /**
+     * The writer that will receive the output.
+     */
+    protected Writer writer;
+
+    /**
+     * Make a fresh JSONWriter. It can be used to build one JSON text.
+     */
+    public JSONWriter(Writer w) {
+        this.comma = false;
+        this.mode = 'i';
+        this.stack = new JSONObject[maxdepth];
+        this.top = 0;
+        this.writer = w;
+    }
+
+    /**
+     * Append a value.
+     * @param s A string value.
+     * @return this
+     * @throws JSONException If the value is out of sequence.
+     */
+    private JSONWriter append(String s) throws JSONException {
+        if (s == null) {
+            throw new JSONException("Null pointer");
+        }
+        if (this.mode == 'o' || this.mode == 'a') {
+            try {
+                if (this.comma && this.mode == 'a') {
+                    this.writer.write(',');
+                }
+                this.writer.write(s);
+            } catch (IOException e) {
+                throw new JSONException(e);
+            }
+            if (this.mode == 'o') {
+                this.mode = 'k';
+            }
+            this.comma = true;
+            return this;
+        }
+        throw new JSONException("Value out of sequence.");
+    }
+
+    /**
+     * Begin appending a new array. All values until the balancing
+     * <code>endArray</code> will be appended to this array. The
+     * <code>endArray</code> method must be called to mark the array's end.
+     * @return this
+     * @throws JSONException If the nesting is too deep, or if the object is
+     * started in the wrong place (for example as a key or after the end of the
+     * outermost array or object).
+     */
+    public JSONWriter array() throws JSONException {
+        if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
+            this.push(null);
+            this.append("[");
+            this.comma = false;
+            return this;
+        }
+        throw new JSONException("Misplaced array.");
+    }
+
+    /**
+     * End something.
+     * @param m Mode
+     * @param c Closing character
+     * @return this
+     * @throws JSONException If unbalanced.
+     */
+    private JSONWriter end(char m, char c) throws JSONException {
+        if (this.mode != m) {
+            throw new JSONException(m == 'a' ? "Misplaced endArray." : 
+            		"Misplaced endObject.");
+        }
+        this.pop(m);
+        try {
+            this.writer.write(c);
+        } catch (IOException e) {
+            throw new JSONException(e);
+        }
+        this.comma = true;
+        return this;
+    }
+
+    /**
+     * End an array. This method most be called to balance calls to
+     * <code>array</code>.
+     * @return this
+     * @throws JSONException If incorrectly nested.
+     */
+    public JSONWriter endArray() throws JSONException {
+        return this.end('a', ']');
+    }
+
+    /**
+     * End an object. This method most be called to balance calls to
+     * <code>object</code>.
+     * @return this
+     * @throws JSONException If incorrectly nested.
+     */
+    public JSONWriter endObject() throws JSONException {
+        return this.end('k', '}');
+    }
+
+    /**
+     * Append a key. The key will be associated with the next value. In an
+     * object, every value must be preceded by a key.
+     * @param s A key string.
+     * @return this
+     * @throws JSONException If the key is out of place. For example, keys
+     *  do not belong in arrays or if the key is null.
+     */
+    public JSONWriter key(String s) throws JSONException {
+        if (s == null) {
+            throw new JSONException("Null key.");
+        }
+        if (this.mode == 'k') {
+            try {
+                stack[top - 1].putOnce(s, Boolean.TRUE);
+                if (this.comma) {
+                    this.writer.write(',');
+                }
+                this.writer.write(JSONObject.quote(s));
+                this.writer.write(':');
+                this.comma = false;
+                this.mode = 'o';
+                return this;
+            } catch (IOException e) {
+                throw new JSONException(e);
+            }
+        }
+        throw new JSONException("Misplaced key.");
+    }
+
+
+    /**
+     * Begin appending a new object. All keys and values until the balancing
+     * <code>endObject</code> will be appended to this object. The
+     * <code>endObject</code> method must be called to mark the object's end.
+     * @return this
+     * @throws JSONException If the nesting is too deep, or if the object is
+     * started in the wrong place (for example as a key or after the end of the
+     * outermost array or object).
+     */
+    public JSONWriter object() throws JSONException {
+        if (this.mode == 'i') {
+            this.mode = 'o';
+        }
+        if (this.mode == 'o' || this.mode == 'a') {
+            this.append("{");
+            this.push(new JSONObject());
+            this.comma = false;
+            return this;
+        }
+        throw new JSONException("Misplaced object.");
+
+    }
+
+
+    /**
+     * Pop an array or object scope.
+     * @param c The scope to close.
+     * @throws JSONException If nesting is wrong.
+     */
+    private void pop(char c) throws JSONException {
+        if (this.top <= 0) {
+            throw new JSONException("Nesting error.");
+        }
+        char m = this.stack[this.top - 1] == null ? 'a' : 'k';
+        if (m != c) {
+            throw new JSONException("Nesting error.");
+        }
+        this.top -= 1;
+        this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
+    }
+
+    /**
+     * Push an array or object scope.
+     * @param c The scope to open.
+     * @throws JSONException If nesting is too deep.
+     */
+    private void push(JSONObject jo) throws JSONException {
+        if (this.top >= maxdepth) {
+            throw new JSONException("Nesting too deep.");
+        }
+        this.stack[this.top] = jo;
+        this.mode = jo == null ? 'a' : 'k';
+        this.top += 1;
+    }
+
+
+    /**
+     * Append either the value <code>true</code> or the value
+     * <code>false</code>.
+     * @param b A boolean.
+     * @return this
+     * @throws JSONException
+     */
+    public JSONWriter value(boolean b) throws JSONException {
+        return this.append(b ? "true" : "false");
+    }
+
+    /**
+     * Append a double value.
+     * @param d A double.
+     * @return this
+     * @throws JSONException If the number is not finite.
+     */
+    public JSONWriter value(double d) throws JSONException {
+        return this.value(new Double(d));
+    }
+
+    /**
+     * Append a long value.
+     * @param l A long.
+     * @return this
+     * @throws JSONException
+     */
+    public JSONWriter value(long l) throws JSONException {
+        return this.append(Long.toString(l));
+    }
+
+
+    /**
+     * Append an object value.
+     * @param o The object to append. It can be null, or a Boolean, Number,
+     *   String, JSONObject, or JSONArray, or an object with a toJSONString()
+     *   method.
+     * @return this
+     * @throws JSONException If the value is out of sequence.
+     */
+    public JSONWriter value(Object o) throws JSONException {
+        return this.append(JSONObject.valueToString(o));
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/JSONWriter.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/Test.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/Test.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/Test.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,678 @@
+package org.json;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.io.StringWriter;
+
+/**
+ * Test class. This file is not formally a member of the org.json library.
+ * It is just a casual test tool.
+ */
+public class Test {
+	
+    /**
+     * Entry point.
+     * @param args
+     */
+    public static void main(String args[]) {
+        Iterator it;
+        JSONArray a;
+        JSONObject j;
+        JSONStringer jj;
+        Object o;
+        String s;
+        
+/** 
+ *  Obj is a typical class that implements JSONString. It also
+ *  provides some beanie methods that can be used to 
+ *  construct a JSONObject. It also demonstrates constructing
+ *  a JSONObject with an array of names.
+ */
+        class Obj implements JSONString {
+        	public String aString;
+        	public double aNumber;
+        	public boolean aBoolean;
+        	
+            public Obj(String string, double n, boolean b) {
+                this.aString = string;
+                this.aNumber = n;
+                this.aBoolean = b;
+            }
+            
+            public double getNumber() {
+            	return this.aNumber;
+            }
+            
+            public String getString() {
+            	return this.aString;
+            }
+            
+            public boolean isBoolean() {
+            	return this.aBoolean;
+            }
+            
+            public String getBENT() {
+            	return "All uppercase key";
+            }
+            
+            public String getX() {
+            	return "x";
+            }
+            
+            public String toJSONString() {
+            	return "{" + JSONObject.quote(this.aString) + ":" + 
+            	JSONObject.doubleToString(this.aNumber) + "}";
+            }            
+            public String toString() {
+            	return this.getString() + " " + this.getNumber() + " " + 
+            			this.isBoolean() + "." + this.getBENT() + " " + this.getX();
+            }
+        }      
+        
+
+    	Obj obj = new Obj("A beany object", 42, true);
+        
+        try {     
+            s = "[0.1]";
+            a = new JSONArray(s);
+            System.out.println(a.toString());
+            System.out.println("");
+            
+            j = XML.toJSONObject("<![CDATA[This is a collection of test patterns and examples for org.json.]]>  Ignore the stuff past the end.  ");
+            System.out.println(j.toString());
+            System.out.println("");
+            
+            j = new JSONObject();
+            o = null;
+            j.put("booga", o);
+            j.put("wooga", JSONObject.NULL);
+            System.out.println(j.toString());
+            System.out.println("");
+           
+            j = new JSONObject();
+            j.increment("two");
+            j.increment("two");
+            System.out.println(j.toString());
+            System.out.println("");
+            
+            
+            s = "<test><blank></blank><empty/></test>";
+            j = XML.toJSONObject(s);
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            s = "{     \"list of lists\" : [         [1, 2, 3],         [4, 5, 6],     ] }";
+            j = new JSONObject(s);
+            System.out.println(j.toString(4));
+            System.out.println(XML.toString(j));
+                    
+            s = "<recipe name=\"bread\" prep_time=\"5 mins\" cook_time=\"3 hours\"> <title>Basic bread</title> <ingredient amount=\"8\" unit=\"dL\">Flour</ingredient> <ingredient amount=\"10\" unit=\"grams\">Yeast</ingredient> <ingredient amount=\"4\" unit=\"dL\" state=\"warm\">Water</ingredient> <ingredient amount=\"1\" unit=\"teaspoon\">Salt</ingredient> <instructions> <step>Mix all ingredients together.</step> <step>Knead thoroughly.</step> <step>Cover with a cloth, and leave for one hour in warm room.</step> <step>Knead again.</step> <step>Place in a bread baking tin.</step> <step>Cover with a cloth, and leave for one hour in warm room.</step> <step>Bake in the oven at 180(degrees)C for 30 minutes.</step> </instructions> </recipe> ";
+            j = XML.toJSONObject(s);
+            System.out.println(j.toString(4));
+            System.out.println();
+            
+            j = JSONML.toJSONObject(s);
+            System.out.println(j.toString());
+            System.out.println(JSONML.toString(j));
+            System.out.println();
+            
+            a = JSONML.toJSONArray(s);
+            System.out.println(a.toString(4));
+            System.out.println(JSONML.toString(a));
+            System.out.println();
+            
+            s = "<div id=\"demo\" class=\"JSONML\"><p>JSONML is a transformation between <b>JSON</b> and <b>XML</b> that preserves ordering of document features.</p><p>JSONML can work with JSON arrays or JSON objects.</p><p>Three<br/>little<br/>words</p></div>";
+            j = JSONML.toJSONObject(s);
+            System.out.println(j.toString(4));
+            System.out.println(JSONML.toString(j));
+            System.out.println();
+            
+            a = JSONML.toJSONArray(s);
+            System.out.println(a.toString(4));
+            System.out.println(JSONML.toString(a));
+            System.out.println();
+            
+            s = "<person created=\"2006-11-11T19:23\" modified=\"2006-12-31T23:59\">\n <firstName>Robert</firstName>\n <lastName>Smith</lastName>\n <address type=\"home\">\n <street>12345 Sixth Ave</street>\n <city>Anytown</city>\n <state>CA</state>\n <postalCode>98765-4321</postalCode>\n </address>\n </person>";
+            j = XML.toJSONObject(s);
+            System.out.println(j.toString(4));
+            
+            j = new JSONObject(obj);
+            System.out.println(j.toString());
+            
+            s = "{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, \"ratingCount\": null, \"averageRating\": null } }";
+            j = new JSONObject(s);
+            System.out.println(j.toString(2));
+
+            jj = new JSONStringer();
+            s = jj
+	            .object()
+	                .key("single")
+	                .value("MARIE HAA'S")
+	                .key("Johnny")
+	                .value("MARIE HAA\\'S")
+	                .key("foo")
+	                .value("bar")
+	                .key("baz")
+	                .array()
+	                    .object()
+	                        .key("quux")
+	                        .value("Thanks, Josh!")
+	                    .endObject()
+	                .endArray()
+	                .key("obj keys")
+	                .value(JSONObject.getNames(obj))
+	            .endObject()
+            .toString();
+            System.out.println(s);
+
+            System.out.println(new JSONStringer()
+                .object()
+                	.key("a")
+                	.array()
+                		.array()
+                			.array()
+                				.value("b")
+                            .endArray()
+                        .endArray()
+                    .endArray()
+                .endObject()
+                .toString());
+
+            jj = new JSONStringer();
+            jj.array();
+            jj.value(1);
+            jj.array();
+            jj.value(null);
+            jj.array();
+            jj.object();
+            jj.key("empty-array").array().endArray();
+            jj.key("answer").value(42);
+            jj.key("null").value(null);
+            jj.key("false").value(false);
+            jj.key("true").value(true);
+            jj.key("big").value(123456789e+88);
+            jj.key("small").value(123456789e-88);
+            jj.key("empty-object").object().endObject();
+            jj.key("long");
+            jj.value(9223372036854775807L);
+            jj.endObject();
+            jj.value("two");
+            jj.endArray();
+            jj.value(true);
+            jj.endArray();
+            jj.value(98.6);
+            jj.value(-100.0);
+            jj.object();
+            jj.endObject();
+            jj.object();
+            jj.key("one");
+            jj.value(1.00);
+            jj.endObject();
+            jj.value(obj);
+            jj.endArray();
+            System.out.println(jj.toString());
+
+            System.out.println(new JSONArray(jj.toString()).toString(4));
+
+        	int ar[] = {1, 2, 3};
+        	JSONArray ja = new JSONArray(ar);
+        	System.out.println(ja.toString());
+        	
+        	String sa[] = {"aString", "aNumber", "aBoolean"};            
+            j = new JSONObject(obj, sa);
+            j.put("Testing JSONString interface", obj);
+            System.out.println(j.toString(4));          
+            
+            j = new JSONObject("{slashes: '///', closetag: '</script>', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = new JSONObject(
+                "{foo: [true, false,9876543210,    0.0, 1.00000001,  1.000000000001, 1.00000000000000001," +
+                " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], " +
+                "  to   : null, op : 'Good'," +
+                "ten:10} postfix comment");
+            j.put("String", "98.6");
+            j.put("JSONObject", new JSONObject());
+            j.put("JSONArray", new JSONArray());
+            j.put("int", 57);
+            j.put("double", 123456789012345678901234567890.);
+            j.put("true", true);
+            j.put("false", false);
+            j.put("null", JSONObject.NULL);
+            j.put("bool", "true");
+            j.put("zero", -0.0);
+            j.put("\\u2028", "\u2028");
+            j.put("\\u2029", "\u2029");
+            a = j.getJSONArray("foo");
+            a.put(666);
+            a.put(2001.99);
+            a.put("so \"fine\".");
+            a.put("so <fine>.");
+            a.put(true);
+            a.put(false);
+            a.put(new JSONArray());
+            a.put(new JSONObject());
+            j.put("keys", JSONObject.getNames(j));
+            System.out.println(j.toString(4));
+            System.out.println(XML.toString(j));
+
+            System.out.println("String: " + j.getDouble("String"));
+            System.out.println("  bool: " + j.getBoolean("bool"));
+            System.out.println("    to: " + j.getString("to"));
+            System.out.println("  true: " + j.getString("true"));
+            System.out.println("   foo: " + j.getJSONArray("foo"));
+            System.out.println("    op: " + j.getString("op"));
+            System.out.println("   ten: " + j.getInt("ten"));
+            System.out.println("  oops: " + j.optBoolean("oops"));
+
+            s = "<xml one = 1 two=' \"2\" '><five></five>First \u0009&lt;content&gt;<five></five> This is \"content\". <three>  3  </three>JSON does not preserve the sequencing of elements and contents.<three>  III  </three>  <three>  T H R E E</three><four/>Content text is an implied structure in XML. <six content=\"6\"/>JSON does not have implied structure:<seven>7</seven>everything is explicit.<![CDATA[CDATA blocks<are><supported>!]]></xml>";
+            j = XML.toJSONObject(s);
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+            
+            ja = JSONML.toJSONArray(s);
+            System.out.println(ja.toString(4));
+            System.out.println(JSONML.toString(ja));
+            System.out.println("");
+            
+            s = "<xml do='0'>uno<a re='1' mi='2'>dos<b fa='3'/>tres<c>true</c>quatro</a>cinqo<d>seis<e/></d></xml>";
+            ja = JSONML.toJSONArray(s);
+            System.out.println(ja.toString(4));
+            System.out.println(JSONML.toString(ja));
+            System.out.println("");
+
+            s = "<mapping><empty/>   <class name = \"Customer\">      <field name = \"ID\" type = \"string\">         <bind-xml name=\"ID\" node=\"attribute\"/>      </field>      <field name = \"FirstName\" type = \"FirstName\"/>      <field name = \"MI\" type = \"MI\"/>      <field name = \"LastName\" type = \"LastName\"/>   </class>   <class name = \"FirstName\">      <field name = \"text\">         <bind-xml name = \"text\" node = \"text\"/>      </field>   </class>   <class name = \"MI\">      <field name = \"text\">         <bind-xml name = \"text\" node = \"text\"/>      </field>   </class>   <class name = \"LastName\">      <field name = \"text\">         <bind-xml name = \"text\" node = \"text\"/>      </field>   </class></mapping>";
+            j = XML.toJSONObject(s);
+
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+            ja = JSONML.toJSONArray(s);
+            System.out.println(ja.toString(4));
+            System.out.println(JSONML.toString(ja));
+            System.out.println("");
+
+            j = XML.toJSONObject("<?xml version=\"1.0\" ?><Book Author=\"Anonymous\"><Title>Sample Book</Title><Chapter id=\"1\">This is chapter 1. It is not very long or interesting.</Chapter><Chapter id=\"2\">This is chapter 2. Although it is longer than chapter 1, it is not any more interesting.</Chapter></Book>");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = XML.toJSONObject("<!DOCTYPE bCard 'http://www.cs.caltech.edu/~adam/schemas/bCard'><bCard><?xml default bCard        firstname = ''        lastname  = '' company   = '' email = '' homepage  = ''?><bCard        firstname = 'Rohit'        lastname  = 'Khare'        company   = 'MCI'        email     = 'khare at mci.net'        homepage  = 'http://pest.w3.org/'/><bCard        firstname = 'Adam'        lastname  = 'Rifkin'        company   = 'Caltech Infospheres Project'        email     = 'adam at cs.caltech.edu'        homepage  = 'http://www.cs.caltech.edu/~adam/'/></bCard>");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = XML.toJSONObject("<?xml version=\"1.0\"?><customer>    <firstName>        <text>Fred</text>    </firstName>    <ID>fbs0001</ID>    <lastName> <text>Scerbo</text>    </lastName>    <MI>        <text>B</text>    </MI></customer>");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = XML.toJSONObject("<!ENTITY tp-address PUBLIC '-//ABC University::Special Collections Library//TEXT (titlepage: name and address)//EN' 'tpspcoll.sgm'><list type='simple'><head>Repository Address </head><item>Special Collections Library</item><item>ABC University</item><item>Main Library, 40 Circle Drive</item><item>Ourtown, Pennsylvania</item><item>17654 USA</item></list>");
+            System.out.println(j.toString());
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = XML.toJSONObject("<test intertag status=ok><empty/>deluxe<blip sweet=true>&amp;&quot;toot&quot;&toot;&#x41;</blip><x>eks</x><w>bonus</w><w>bonus2</w></test>");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = HTTP.toJSONObject("GET / HTTP/1.0\nAccept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\nAccept-Language: en-us\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; Q312461)\nHost: www.nokko.com\nConnection: keep-alive\nAccept-encoding: gzip, deflate\n");
+            System.out.println(j.toString(2));
+            System.out.println(HTTP.toString(j));
+            System.out.println("");
+
+            j = HTTP.toJSONObject("HTTP/1.1 200 Oki Doki\nDate: Sun, 26 May 2002 17:38:52 GMT\nServer: Apache/1.3.23 (Unix) mod_perl/1.26\nKeep-Alive: timeout=15, max=100\nConnection: Keep-Alive\nTransfer-Encoding: chunked\nContent-Type: text/html\n");
+            System.out.println(j.toString(2));
+            System.out.println(HTTP.toString(j));
+            System.out.println("");
+
+            j = new JSONObject("{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}");
+            System.out.println(j.toString(2));
+            System.out.println("isNull: " + j.isNull("nix"));
+            System.out.println("   has: " + j.has("nix"));
+            System.out.println(XML.toString(j));
+            System.out.println(HTTP.toString(j));
+            System.out.println("");
+
+            j = XML.toJSONObject("<?xml version='1.0' encoding='UTF-8'?>"+"\n\n"+"<SOAP-ENV:Envelope"+
+              " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""+
+              " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\""+
+              " xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">"+
+              "<SOAP-ENV:Body><ns1:doGoogleSearch"+
+              " xmlns:ns1=\"urn:GoogleSearch\""+
+              " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"+
+              "<key xsi:type=\"xsd:string\">GOOGLEKEY</key> <q"+
+              " xsi:type=\"xsd:string\">'+search+'</q> <start"+
+              " xsi:type=\"xsd:int\">0</start> <maxResults"+
+              " xsi:type=\"xsd:int\">10</maxResults> <filter"+
+              " xsi:type=\"xsd:boolean\">true</filter> <restrict"+
+              " xsi:type=\"xsd:string\"></restrict> <safeSearch"+
+              " xsi:type=\"xsd:boolean\">false</safeSearch> <lr"+
+              " xsi:type=\"xsd:string\"></lr> <ie"+
+              " xsi:type=\"xsd:string\">latin1</ie> <oe"+
+              " xsi:type=\"xsd:string\">latin1</oe>"+
+              "</ns1:doGoogleSearch>"+
+              "</SOAP-ENV:Body></SOAP-ENV:Envelope>");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = new JSONObject("{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}");
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+
+            j = CookieList.toJSONObject("  f%oo = b+l=ah  ; o;n%40e = t.wo ");
+            System.out.println(j.toString(2));
+            System.out.println(CookieList.toString(j));
+            System.out.println("");
+
+            j = Cookie.toJSONObject("f%oo=blah; secure ;expires = April 24, 2002");
+            System.out.println(j.toString(2));
+            System.out.println(Cookie.toString(j));
+            System.out.println("");
+
+            j = new JSONObject("{script: 'It is not allowed in HTML to send a close script tag in a string<script>because it confuses browsers</script>so we insert a backslash before the /'}");
+            System.out.println(j.toString());
+            System.out.println("");
+
+            JSONTokener jt = new JSONTokener("{op:'test', to:'session', pre:1}{op:'test', to:'session', pre:2}");
+            j = new JSONObject(jt);
+            System.out.println(j.toString());
+            System.out.println("pre: " + j.optInt("pre"));
+            int i = jt.skipTo('{');
+            System.out.println(i);
+            j = new JSONObject(jt);
+            System.out.println(j.toString());
+            System.out.println("");
+
+            a = CDL.toJSONArray("Comma delimited list test, '\"Strip\"Quotes', 'quote, comma', No quotes, 'Single Quotes', \"Double Quotes\"\n1,'2',\"3\"\n,'It is \"good,\"', \"It works.\"\n\n");
+
+            s = CDL.toString(a);
+            System.out.println(s);
+            System.out.println("");
+            System.out.println(a.toString(4));
+            System.out.println("");
+            a = CDL.toJSONArray(s);
+            System.out.println(a.toString(4));
+            System.out.println("");
+
+            a = new JSONArray(" [\"<escape>\", next is an implied null , , ok,] ");
+            System.out.println(a.toString());
+            System.out.println("");
+            System.out.println(XML.toString(a));
+            System.out.println("");
+
+            j = new JSONObject("{ fun => with non-standard forms ; forgiving => This package can be used to parse formats that are similar to but not stricting conforming to JSON; why=To make it easier to migrate existing data to JSON,one = [[1.00]]; uno=[[{1=>1}]];'+':+6e66 ;pluses=+++;empty = '' , 'double':0.666,true: TRUE, false: FALSE, null=NULL;[true] = [[!,@;*]]; string=>  o. k. ; \r oct=0666; hex=0x666; dec=666; o=0999; noh=0x0x}");
+            System.out.println(j.toString(4));
+            System.out.println("");
+            if (j.getBoolean("true") && !j.getBoolean("false")) {
+                System.out.println("It's all good");
+            }
+
+            System.out.println("");
+            j = new JSONObject(j, new String[]{"dec", "oct", "hex", "missing"});
+            System.out.println(j.toString(4));
+
+            System.out.println("");
+            System.out.println(new JSONStringer().array().value(a).value(j).endArray());
+
+            j = new JSONObject("{string: \"98.6\", long: 2147483648, int: 2147483647, longer: 9223372036854775807, double: 9223372036854775808}");
+            System.out.println(j.toString(4));
+
+            System.out.println("\ngetInt");
+            System.out.println("int    " + j.getInt("int"));
+            System.out.println("long   " + j.getInt("long"));
+            System.out.println("longer " + j.getInt("longer"));
+            System.out.println("double " + j.getInt("double"));
+            System.out.println("string " + j.getInt("string"));
+
+            System.out.println("\ngetLong");
+            System.out.println("int    " + j.getLong("int"));
+            System.out.println("long   " + j.getLong("long"));
+            System.out.println("longer " + j.getLong("longer"));
+            System.out.println("double " + j.getLong("double"));
+            System.out.println("string " + j.getLong("string"));
+
+            System.out.println("\ngetDouble");
+            System.out.println("int    " + j.getDouble("int"));
+            System.out.println("long   " + j.getDouble("long"));
+            System.out.println("longer " + j.getDouble("longer"));
+            System.out.println("double " + j.getDouble("double"));
+            System.out.println("string " + j.getDouble("string"));
+
+            j.put("good sized", 9223372036854775807L);
+            System.out.println(j.toString(4));
+
+            a = new JSONArray("[2147483647, 2147483648, 9223372036854775807, 9223372036854775808]");
+            System.out.println(a.toString(4));
+
+            System.out.println("\nKeys: ");
+            it = j.keys();
+            while (it.hasNext()) {
+                s = (String)it.next();
+                System.out.println(s + ": " + j.getString(s));
+            }
+
+
+            System.out.println("\naccumulate: ");
+            j = new JSONObject();
+            j.accumulate("stooge", "Curly");
+            j.accumulate("stooge", "Larry");
+            j.accumulate("stooge", "Moe");
+            a = j.getJSONArray("stooge");
+            a.put(5, "Shemp");
+            System.out.println(j.toString(4));
+
+            System.out.println("\nwrite:");
+            System.out.println(j.write(new StringWriter()));
+
+            s = "<xml empty><a></a><a>1</a><a>22</a><a>333</a></xml>";
+            j = XML.toJSONObject(s);
+            System.out.println(j.toString(4));
+            System.out.println(XML.toString(j));
+            
+            s = "<book><chapter>Content of the first chapter</chapter><chapter>Content of the second chapter      <chapter>Content of the first subchapter</chapter>      <chapter>Content of the second subchapter</chapter></chapter><chapter>Third Chapter</chapter></book>";
+            j = XML.toJSONObject(s);
+            System.out.println(j.toString(4));
+            System.out.println(XML.toString(j));
+            
+            a = JSONML.toJSONArray(s);
+            System.out.println(a.toString(4));
+            System.out.println(JSONML.toString(a));
+            
+            Collection c = null;
+            Map m = null;
+            
+            j = new JSONObject(m);
+            a = new JSONArray(c);
+            j.append("stooge", "Joe DeRita");
+            j.append("stooge", "Shemp");
+            j.accumulate("stooges", "Curly");
+            j.accumulate("stooges", "Larry");
+            j.accumulate("stooges", "Moe");
+            j.accumulate("stoogearray", j.get("stooges"));
+            j.put("map", m);
+            j.put("collection", c);
+            j.put("array", a);
+            a.put(m);
+            a.put(c);
+            System.out.println(j.toString(4));
+            
+            s = "{plist=Apple; AnimalSmells = { pig = piggish; lamb = lambish; worm = wormy; }; AnimalSounds = { pig = oink; lamb = baa; worm = baa;  Lisa = \"Why is the worm talking like a lamb?\" } ; AnimalColors = { pig = pink; lamb = black; worm = pink; } } "; 
+            j = new JSONObject(s);
+            System.out.println(j.toString(4));
+            
+            s = " (\"San Francisco\", \"New York\", \"Seoul\", \"London\", \"Seattle\", \"Shanghai\")";
+            a = new JSONArray(s);
+            System.out.println(a.toString());
+            
+            s = "<a ichi='1' ni='2'><b>The content of b</b> and <c san='3'>The content of c</c><d>do</d><e></e><d>re</d><f/><d>mi</d></a>";
+            j = XML.toJSONObject(s);
+
+            System.out.println(j.toString(2));
+            System.out.println(XML.toString(j));
+            System.out.println("");
+            ja = JSONML.toJSONArray(s);
+            System.out.println(ja.toString(4));
+            System.out.println(JSONML.toString(ja));
+            System.out.println("");
+            
+            s = "<Root><MsgType type=\"node\"><BatchType type=\"string\">111111111111111</BatchType></MsgType></Root>";
+            j = JSONML.toJSONObject(s);
+            System.out.println(j);
+            ja = JSONML.toJSONArray(s);
+            System.out.println(ja);
+          
+            
+            System.out.println("\nTesting Exceptions: ");
+
+            System.out.print("Exception: ");
+            try {
+                a = new JSONArray("[\n\r\n\r}");
+                System.out.println(a.toString());
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            
+            System.out.print("Exception: ");
+            try {
+                a = new JSONArray("<\n\r\n\r      ");
+                System.out.println(a.toString());
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            
+            System.out.print("Exception: ");
+            try {
+                a = new JSONArray();
+                a.put(Double.NEGATIVE_INFINITY);
+                a.put(Double.NaN);
+                System.out.println(a.toString());
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+                System.out.println(j.getDouble("stooge"));
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+                System.out.println(j.getDouble("howard"));
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+                System.out.println(j.put(null, "howard"));
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+                System.out.println(a.getDouble(0));
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+                System.out.println(a.get(-1));
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+                System.out.println(a.put(Double.NaN));
+            } catch (Exception e) {
+                System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {
+            	j = XML.toJSONObject("<a><b>    ");
+            } catch (Exception e) {
+            	System.out.println(e);
+            }            
+            System.out.print("Exception: ");
+            try {
+            	j = XML.toJSONObject("<a></b>    ");
+            } catch (Exception e) {
+            	System.out.println(e);
+            }            
+            System.out.print("Exception: ");
+            try {
+            	j = XML.toJSONObject("<a></a    ");
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+            System.out.print("Exception: ");
+            try {            	
+            	ja = new JSONArray(new Object());
+            	System.out.println(ja.toString());
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+
+            System.out.print("Exception: ");
+            try {            	
+            	s = "[)";
+            	a = new JSONArray(s);
+            	System.out.println(a.toString());
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+
+            System.out.print("Exception: ");
+            try {            	
+                s = "<xml";
+                ja = JSONML.toJSONArray(s);
+                System.out.println(ja.toString(4));
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+
+            System.out.print("Exception: ");
+            try {            	
+                s = "<right></wrong>";
+                ja = JSONML.toJSONArray(s);
+                System.out.println(ja.toString(4));
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+
+            System.out.print("Exception: ");
+            try {            	
+                s = "{\"koda\": true, \"koda\": true}";
+                j = new JSONObject(s);
+                System.out.println(j.toString(4));
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+
+            System.out.print("Exception: ");
+            try {            	
+                jj = new JSONStringer();
+                s = jj
+    	            .object()
+    	                .key("bosanda")
+    	                .value("MARIE HAA'S")
+    	                .key("bosanda")
+    	                .value("MARIE HAA\\'S")
+    	            .endObject()
+    	            .toString();
+                System.out.println(j.toString(4));
+            } catch (Exception e) {
+            	System.out.println(e);
+            }
+        } catch (Exception e) {
+            System.out.println(e.toString());
+        }
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/Test.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/XML.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/XML.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/XML.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,437 @@
+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;
+
+
+/**
+ * This provides static methods to convert an XML text into a JSONObject,
+ * and to covert a JSONObject into an XML text.
+ * @author JSON.org
+ * @version 2009-12-12
+ */
+public class XML {
+
+    /** The Character '&'. */
+    public static final Character AMP   = new Character('&');
+
+    /** The Character '''. */
+    public static final Character APOS  = new Character('\'');
+
+    /** The Character '!'. */
+    public static final Character BANG  = new Character('!');
+
+    /** The Character '='. */
+    public static final Character EQ    = new Character('=');
+
+    /** The Character '>'. */
+    public static final Character GT    = new Character('>');
+
+    /** The Character '<'. */
+    public static final Character LT    = new Character('<');
+
+    /** The Character '?'. */
+    public static final Character QUEST = new Character('?');
+
+    /** The Character '"'. */
+    public static final Character QUOT  = new Character('"');
+
+    /** The Character '/'. */
+    public static final Character SLASH = new Character('/');
+
+    /**
+     * Replace special characters with XML escapes:
+     * <pre>
+     * &amp; <small>(ampersand)</small> is replaced by &amp;amp;
+     * &lt; <small>(less than)</small> is replaced by &amp;lt;
+     * &gt; <small>(greater than)</small> is replaced by &amp;gt;
+     * &quot; <small>(double quote)</small> is replaced by &amp;quot;
+     * </pre>
+     * @param string The string to be escaped.
+     * @return The escaped string.
+     */
+    public static String escape(String string) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0, len = string.length(); i < len; i++) {
+            char c = string.charAt(i);
+            switch (c) {
+            case '&':
+                sb.append("&amp;");
+                break;
+            case '<':
+                sb.append("&lt;");
+                break;
+            case '>':
+                sb.append("&gt;");
+                break;
+            case '"':
+                sb.append("&quot;");
+                break;
+            default:
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * Throw an exception if the string contains whitespace. 
+     * Whitespace is not allowed in tagNames and attributes.
+     * @param string
+     * @throws JSONException
+     */
+    public static void noSpace(String string) throws JSONException {
+    	int i, length = string.length();
+    	if (length == 0) {
+    		throw new JSONException("Empty string.");
+    	}
+    	for (i = 0; i < length; i += 1) {
+		    if (Character.isWhitespace(string.charAt(i))) {
+		    	throw new JSONException("'" + string + 
+		    			"' contains a space character.");
+		    }
+		}
+    }
+
+    /**
+     * Scan the content following the named tag, attaching it to the context.
+     * @param x       The XMLTokener containing the source string.
+     * @param context The JSONObject that will include the new material.
+     * @param name    The tag name.
+     * @return true if the close tag is processed.
+     * @throws JSONException
+     */
+    private static boolean parse(XMLTokener x, JSONObject context,
+                                 String name) throws JSONException {
+        char       c;
+        int        i;
+        String     n;
+        JSONObject o = null;
+        String     s;
+        Object     t;
+
+// Test for and skip past these forms:
+//      <!-- ... -->
+//      <!   ...   >
+//      <![  ... ]]>
+//      <?   ...  ?>
+// Report errors for these forms:
+//      <>
+//      <=
+//      <<
+
+        t = x.nextToken();
+
+// <!
+
+        if (t == BANG) {
+            c = x.next();
+            if (c == '-') {
+                if (x.next() == '-') {
+                    x.skipPast("-->");
+                    return false;
+                }
+                x.back();
+            } else if (c == '[') {
+                t = x.nextToken();
+                if (t.equals("CDATA")) {
+                    if (x.next() == '[') {
+                        s = x.nextCDATA();
+                        if (s.length() > 0) {
+                            context.accumulate("content", s);
+                        }
+                        return false;
+                    }
+                }
+                throw x.syntaxError("Expected 'CDATA['");
+            }
+            i = 1;
+            do {
+                t = x.nextMeta();
+                if (t == null) {
+                    throw x.syntaxError("Missing '>' after '<!'.");
+                } else if (t == LT) {
+                    i += 1;
+                } else if (t == GT) {
+                    i -= 1;
+                }
+            } while (i > 0);
+            return false;
+        } else if (t == QUEST) {
+
+// <?
+
+            x.skipPast("?>");
+            return false;
+        } else if (t == SLASH) {
+
+// Close tag </
+
+        	t = x.nextToken();
+            if (name == null) {
+                throw x.syntaxError("Mismatched close tag" + t);
+            }            
+            if (!t.equals(name)) {
+                throw x.syntaxError("Mismatched " + name + " and " + t);
+            }
+            if (x.nextToken() != GT) {
+                throw x.syntaxError("Misshaped close tag");
+            }
+            return true;
+
+        } else if (t instanceof Character) {
+            throw x.syntaxError("Misshaped tag");
+
+// Open tag <
+
+        } else {
+            n = (String)t;
+            t = null;
+            o = new JSONObject();
+            for (;;) {
+                if (t == null) {
+                    t = x.nextToken();
+                }
+
+// attribute = value
+
+                if (t instanceof String) {
+                    s = (String)t;
+                    t = x.nextToken();
+                    if (t == EQ) {
+                        t = x.nextToken();
+                        if (!(t instanceof String)) {
+                            throw x.syntaxError("Missing value");
+                        }
+                        o.accumulate(s, JSONObject.stringToValue((String)t));
+                        t = null;
+                    } else {
+                        o.accumulate(s, "");
+                    }
+
+// Empty tag <.../>
+
+                } else if (t == SLASH) {
+                    if (x.nextToken() != GT) {
+                        throw x.syntaxError("Misshaped tag");
+                    }
+                    context.accumulate(n, "");
+                    return false;
+
+// Content, between <...> and </...>
+
+                } else if (t == GT) {
+                    for (;;) {
+                        t = x.nextContent();
+                        if (t == null) {
+                            if (n != null) {
+                                throw x.syntaxError("Unclosed tag " + n);
+                            }
+                            return false;
+                        } else if (t instanceof String) {
+                            s = (String)t;
+                            if (s.length() > 0) {
+                                o.accumulate("content", JSONObject.stringToValue(s));
+                            }
+
+// Nested element
+
+                        } else if (t == LT) {
+                            if (parse(x, o, n)) {
+                                if (o.length() == 0) {
+                                    context.accumulate(n, "");
+                                } else if (o.length() == 1 &&
+                                       o.opt("content") != null) {
+                                    context.accumulate(n, o.opt("content"));
+                                } else {
+                                    context.accumulate(n, o);
+                                }
+                                return false;
+                            }
+                        }
+                    }
+                } else {
+                    throw x.syntaxError("Misshaped tag");
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Convert a well-formed (but not necessarily valid) XML string into a
+     * JSONObject. Some information may be lost in this transformation
+     * because JSON is a data format and XML is a document format. XML uses
+     * elements, attributes, and content text, while JSON uses unordered
+     * collections of name/value pairs and arrays of values. JSON does not
+     * does not like to distinguish between elements and attributes.
+     * Sequences of similar elements are represented as JSONArrays. Content
+     * text may be placed in a "content" member. Comments, prologs, DTDs, and
+     * <code>&lt;[ [ ]]></code> are ignored.
+     * @param string The source string.
+     * @return A JSONObject containing the structured data from the XML string.
+     * @throws JSONException
+     */
+    public static JSONObject toJSONObject(String string) throws JSONException {
+        JSONObject o = new JSONObject();
+        XMLTokener x = new XMLTokener(string);
+        while (x.more() && x.skipPast("<")) {
+            parse(x, o, null);
+        }
+        return o;
+    }
+
+
+    /**
+     * Convert a JSONObject into a well-formed, element-normal XML string.
+     * @param o A JSONObject.
+     * @return  A string.
+     * @throws  JSONException
+     */
+    public static String toString(Object o) throws JSONException {
+        return toString(o, null);
+    }
+
+
+    /**
+     * Convert a JSONObject into a well-formed, element-normal XML string.
+     * @param o A JSONObject.
+     * @param tagName The optional name of the enclosing tag.
+     * @return A string.
+     * @throws JSONException
+     */
+    public static String toString(Object o, String tagName)
+            throws JSONException {
+        StringBuffer b = new StringBuffer();
+        int          i;
+        JSONArray    ja;
+        JSONObject   jo;
+        String       k;
+        Iterator     keys;
+        int          len;
+        String       s;
+        Object       v;
+        if (o instanceof JSONObject) {
+
+// Emit <tagName>
+
+            if (tagName != null) {
+                b.append('<');
+                b.append(tagName);
+                b.append('>');
+            }
+
+// Loop thru the keys.
+
+            jo = (JSONObject)o;
+            keys = jo.keys();
+            while (keys.hasNext()) {
+                k = keys.next().toString();
+                v = jo.opt(k);
+                if (v == null) {
+                	v = "";
+                }
+                if (v instanceof String) {
+                    s = (String)v;
+                } else {
+                    s = null;
+                }
+
+// Emit content in body
+
+                if (k.equals("content")) {
+                    if (v instanceof JSONArray) {
+                        ja = (JSONArray)v;
+                        len = ja.length();
+                        for (i = 0; i < len; i += 1) {
+                            if (i > 0) {
+                                b.append('\n');
+                            }
+                            b.append(escape(ja.get(i).toString()));
+                        }
+                    } else {
+                        b.append(escape(v.toString()));
+                    }
+
+// Emit an array of similar keys
+
+                } else if (v instanceof JSONArray) {
+                    ja = (JSONArray)v;
+                    len = ja.length();
+                    for (i = 0; i < len; i += 1) {
+                    	v = ja.get(i);
+                    	if (v instanceof JSONArray) {
+                            b.append('<');
+                            b.append(k);
+                            b.append('>');
+                    		b.append(toString(v));
+                            b.append("</");
+                            b.append(k);
+                            b.append('>');
+                    	} else {
+                    		b.append(toString(v, k));
+                    	}
+                    }
+                } else if (v.equals("")) {
+                    b.append('<');
+                    b.append(k);
+                    b.append("/>");
+
+// Emit a new tag <k>
+
+                } else {
+                    b.append(toString(v, k));
+                }
+            }
+            if (tagName != null) {
+
+// Emit the </tagname> close tag
+
+                b.append("</");
+                b.append(tagName);
+                b.append('>');
+            }
+            return b.toString();
+
+// XML does not have good support for arrays. If an array appears in a place
+// where XML is lacking, synthesize an <array> element.
+
+        } else if (o instanceof JSONArray) {
+            ja = (JSONArray)o;
+            len = ja.length();
+            for (i = 0; i < len; ++i) {
+            	v = ja.opt(i);
+                b.append(toString(v, (tagName == null) ? "array" : tagName));
+            }
+            return b.toString();
+        } else {
+            s = (o == null) ? "null" : escape(o.toString());
+            return (tagName == null) ? "\"" + s + "\"" :
+                (s.length() == 0) ? "<" + tagName + "/>" :
+                "<" + tagName + ">" + s + "</" + tagName + ">";
+        }
+    }
+}
\ No newline at end of file


Property changes on: trunk/server/websocket/src/main/java/org/json/XML.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/java/org/json/XMLTokener.java
===================================================================
--- trunk/server/websocket/src/main/java/org/json/XMLTokener.java	                        (rev 0)
+++ trunk/server/websocket/src/main/java/org/json/XMLTokener.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,365 @@
+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 XMLTokener extends the JSONTokener to provide additional methods
+ * for the parsing of XML texts.
+ * @author JSON.org
+ * @version 2010-01-30
+ */
+public class XMLTokener extends JSONTokener {
+
+
+   /** The table of entity values. It initially contains Character values for
+    * amp, apos, gt, lt, quot.
+    */
+   public static final java.util.HashMap entity;
+
+   static {
+       entity = new java.util.HashMap(8);
+       entity.put("amp",  XML.AMP);
+       entity.put("apos", XML.APOS);
+       entity.put("gt",   XML.GT);
+       entity.put("lt",   XML.LT);
+       entity.put("quot", XML.QUOT);
+   }
+
+    /**
+     * Construct an XMLTokener from a string.
+     * @param s A source string.
+     */
+    public XMLTokener(String s) {
+        super(s);
+    }
+
+    /**
+     * Get the text in the CDATA block.
+     * @return The string up to the <code>]]&gt;</code>.
+     * @throws JSONException If the <code>]]&gt;</code> is not found.
+     */
+    public String nextCDATA() throws JSONException {
+        char         c;
+        int          i;
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            c = next();
+            if (end()) {
+                throw syntaxError("Unclosed CDATA");
+            }
+            sb.append(c);
+            i = sb.length() - 3;
+            if (i >= 0 && sb.charAt(i) == ']' &&
+                          sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
+                sb.setLength(i);
+                return sb.toString();
+            }
+        }
+    }
+
+
+    /**
+     * Get the next XML outer token, trimming whitespace. There are two kinds
+     * of tokens: the '<' character which begins a markup tag, and the content
+     * text between markup tags.
+     *
+     * @return  A string, or a '<' Character, or null if there is no more
+     * source text.
+     * @throws JSONException
+     */
+    public Object nextContent() throws JSONException {
+        char         c;
+        StringBuffer sb;
+        do {
+            c = next();
+        } while (Character.isWhitespace(c));
+        if (c == 0) {
+            return null;
+        }
+        if (c == '<') {
+            return XML.LT;
+        }
+        sb = new StringBuffer();
+        for (;;) {
+            if (c == '<' || c == 0) {
+                back();
+                return sb.toString().trim();
+            }
+            if (c == '&') {
+                sb.append(nextEntity(c));
+            } else {
+                sb.append(c);
+            }
+            c = next();
+        }
+    }
+
+
+    /**
+     * Return the next entity. These entities are translated to Characters:
+     *     <code>&amp;  &apos;  &gt;  &lt;  &quot;</code>.
+     * @param a An ampersand character.
+     * @return  A Character or an entity String if the entity is not recognized.
+     * @throws JSONException If missing ';' in XML entity.
+     */
+    public Object nextEntity(char a) throws JSONException {
+        StringBuffer sb = new StringBuffer();
+        for (;;) {
+            char c = next();
+            if (Character.isLetterOrDigit(c) || c == '#') {
+                sb.append(Character.toLowerCase(c));
+            } else if (c == ';') {
+                break;
+            } else {
+                throw syntaxError("Missing ';' in XML entity: &" + sb);
+            }
+        }
+        String s = sb.toString();
+        Object e = entity.get(s);
+        return e != null ? e : a + s + ";";
+    }
+
+
+    /**
+     * Returns the next XML meta token. This is used for skipping over <!...>
+     * and <?...?> structures.
+     * @return Syntax characters (<code>< > / = ! ?</code>) are returned as
+     *  Character, and strings and names are returned as Boolean. We don't care
+     *  what the values actually are.
+     * @throws JSONException If a string is not properly closed or if the XML
+     *  is badly structured.
+     */
+    public Object nextMeta() throws JSONException {
+        char c;
+        char q;
+        do {
+            c = next();
+        } while (Character.isWhitespace(c));
+        switch (c) {
+        case 0:
+            throw syntaxError("Misshaped meta tag");
+        case '<':
+            return XML.LT;
+        case '>':
+            return XML.GT;
+        case '/':
+            return XML.SLASH;
+        case '=':
+            return XML.EQ;
+        case '!':
+            return XML.BANG;
+        case '?':
+            return XML.QUEST;
+        case '"':
+        case '\'':
+            q = c;
+            for (;;) {
+                c = next();
+                if (c == 0) {
+                    throw syntaxError("Unterminated string");
+                }
+                if (c == q) {
+                    return Boolean.TRUE;
+                }
+            }
+        default:
+            for (;;) {
+                c = next();
+                if (Character.isWhitespace(c)) {
+                    return Boolean.TRUE;
+                }
+                switch (c) {
+                case 0:
+                case '<':
+                case '>':
+                case '/':
+                case '=':
+                case '!':
+                case '?':
+                case '"':
+                case '\'':
+                    back();
+                    return Boolean.TRUE;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Get the next XML Token. These tokens are found inside of angle
+     * brackets. It may be one of these characters: <code>/ > = ! ?</code> or it
+     * may be a string wrapped in single quotes or double quotes, or it may be a
+     * name.
+     * @return a String or a Character.
+     * @throws JSONException If the XML is not well formed.
+     */
+    public Object nextToken() throws JSONException {
+        char c;
+        char q;
+        StringBuffer sb;
+        do {
+            c = next();
+        } while (Character.isWhitespace(c));
+        switch (c) {
+        case 0:
+            throw syntaxError("Misshaped element");
+        case '<':
+            throw syntaxError("Misplaced '<'");
+        case '>':
+            return XML.GT;
+        case '/':
+            return XML.SLASH;
+        case '=':
+            return XML.EQ;
+        case '!':
+            return XML.BANG;
+        case '?':
+            return XML.QUEST;
+
+// Quoted string
+
+        case '"':
+        case '\'':
+            q = c;
+            sb = new StringBuffer();
+            for (;;) {
+                c = next();
+                if (c == 0) {
+                    throw syntaxError("Unterminated string");
+                }
+                if (c == q) {
+                    return sb.toString();
+                }
+                if (c == '&') {
+                    sb.append(nextEntity(c));
+                } else {
+                    sb.append(c);
+                }
+            }
+        default:
+
+// Name
+
+            sb = new StringBuffer();
+            for (;;) {
+                sb.append(c);
+                c = next();
+                if (Character.isWhitespace(c)) {
+                    return sb.toString();
+                }
+                switch (c) {
+                case 0:
+                	return sb.toString();
+                case '>':
+                case '/':
+                case '=':
+                case '!':
+                case '?':
+                case '[':
+                case ']':
+                    back();
+                    return sb.toString();
+                case '<':
+                case '"':
+                case '\'':
+                    throw syntaxError("Bad character in a name");
+                }
+            }
+        }
+    }
+    
+    
+    /**
+     * Skip characters until past the requested string.
+     * If it is not found, we are left at the end of the source with a result of false.
+     * @param to A string to skip past.
+     * @throws JSONException
+     */
+    public boolean skipPast(String to) throws JSONException {
+    	boolean b;
+    	char c;
+    	int i;
+    	int j;
+    	int offset = 0;
+    	int n = to.length();
+        char[] circle = new char[n];
+        
+        /*
+         * First fill the circle buffer with as many characters as are in the
+         * to string. If we reach an early end, bail.
+         */
+        
+    	for (i = 0; i < n; i += 1) {
+    		c = next();
+    		if (c == 0) {
+    			return false;
+    		}
+    		circle[i] = c;
+    	}
+    	/*
+    	 * We will loop, possibly for all of the remaining characters.
+    	 */
+    	for (;;) {
+    		j = offset;
+    		b = true;
+    		/*
+    		 * Compare the circle buffer with the to string. 
+    		 */
+    		for (i = 0; i < n; i += 1) {
+    			if (circle[j] != to.charAt(i)) {
+    				b = false;
+    				break;
+    			}
+    			j += 1;
+    			if (j >= n) {
+    				j -= n;
+    			}
+    		}
+    		/*
+    		 * If we exit the loop with b intact, then victory is ours.
+    		 */
+    		if (b) {
+    			return true;
+    		}
+    		/*
+    		 * Get the next character. If there isn't one, then defeat is ours.
+    		 */
+    		c = next();
+    		if (c == 0) {
+    			return false;
+    		}
+    		/*
+    		 * Shove the character in the circle buffer and advance the 
+    		 * circle offset. The offset is mod n.
+    		 */
+    		circle[offset] = c;
+    		offset += 1;
+    		if (offset >= n) {
+    			offset -= n;
+    		}
+    	}
+    }
+}


Property changes on: trunk/server/websocket/src/main/java/org/json/XMLTokener.java
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/server/websocket/src/main/resources/org/infinispan/websocket/infinispan-ws.js
===================================================================
--- trunk/server/websocket/src/main/resources/org/infinispan/websocket/infinispan-ws.js	                        (rev 0)
+++ trunk/server/websocket/src/main/resources/org/infinispan/websocket/infinispan-ws.js	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,126 @@
+ // defaultWSAddress is inserted by the WS Server, which serves this script.
+
+function Cache(cacheName, wsAddress) {
+	
+	var websocket;
+	var queuedMessages = [];
+	var callback;
+	
+	openWebSocket();
+	
+	function openWebSocket() {
+		if (window.WebSocket) {
+			if(wsAddress == null) {
+				wsAddress = defaultWSAddress;
+			}
+			
+			websocket = new WebSocket(wsAddress);
+			
+			websocket.onopen = function(event) {
+				for (i in queuedMessages) {
+					send(queuedMessages[i]);
+				}
+				queuedMessages = null;
+			}
+		} else {
+			alert("Sorry, cannot connect to Infinispan Cache.  Your browser does not support WebSocket.");
+		}
+		
+		websocket.onmessage = function(event) {
+			var jsonObj = JSON.parse(event.data);
+			
+			if(jsonObj.value != null) {
+				if(jsonObj.mime == "application/json") {
+					var decodedObj = JSON.parse(jsonObj.value);
+					callback(jsonObj.key, decodedObj);
+				} else if(jsonObj.mime == "text/plain") {
+					callback(jsonObj.key, jsonObj.value);
+				}
+			} else {
+				callback(jsonObj.key, null);
+			}
+		};
+	}		
+	
+	this.registerCallback = function (callbackFunction) {
+		callback = callbackFunction;
+	}
+
+	this.put = function (key, value) {
+		var encodedObject = JSON.stringify(value);
+		if(encodedObject.charAt(0) == '{') {
+			put(key, encodedObject, "application/json") ;
+		} else {
+			put(key, value, "text/plain") ;
+		}
+	}
+
+	this.get = function (key) {
+		var jsonObj = {
+	        "opCode" : "get",
+	        "cacheName" : cacheName,
+			"key" : key,
+		};
+		
+		send(jsonObj);
+	}
+
+	this.remove = function (key) {
+		var jsonObj = {
+	        "opCode" : "remove",
+	        "cacheName" : cacheName,
+			"key" : key,
+		};
+		
+		send(jsonObj);
+	}
+
+	this.notify = function (key, onEvents) {
+		var jsonObj = {
+		        "opCode" : "notify",
+		        "cacheName" : cacheName,
+				"key" : key,
+		        "onEvents" : onEvents
+			};
+			
+			send(jsonObj);
+	}
+
+	this.unnotify = function (key) {
+		var jsonObj = {
+		        "opCode" : "unnotify",
+		        "cacheName" : cacheName,
+				"key" : key,
+			};
+			
+			send(jsonObj);
+	}
+
+	function put(key, value, mimeType) {
+		var jsonObj = {
+	        "opCode" : "put",
+	        "cacheName" : cacheName,
+			"key" : key,
+			"value" : value,
+			"mime" : mimeType
+		};
+		
+		send(jsonObj);
+	}
+	
+	function send(jsonObj) {
+		if (websocket.readyState == WebSocket.OPEN) {
+			var jsonString = JSON.stringify(jsonObj);
+			websocket.send(jsonString);
+		} else {
+			if(queuedMessages == null) {
+				// reopen the websocket...
+				openWebSocket();
+				queuedMessages = [];				
+			}
+			
+			// Queue the message for sending once the socket is open...
+			queuedMessages[queuedMessages.length] = jsonObj;
+		}
+	}		
+}

Added: trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannel.java
===================================================================
--- trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannel.java	                        (rev 0)
+++ trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannel.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,294 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import java.io.StringWriter;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.SocketAddress;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelConfig;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * 
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class MockChannel implements Channel {
+	
+	private StringWriter writer = new StringWriter();
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getConfig()
+	 */
+	@Override
+	public ChannelConfig getConfig() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getLocalAddress()
+	 */
+	@Override
+	public SocketAddress getLocalAddress() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getRemoteAddress()
+	 */
+	@Override
+	public SocketAddress getRemoteAddress() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#isBound()
+	 */
+	@Override
+	public boolean isBound() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#isConnected()
+	 */
+	@Override
+	public boolean isConnected() {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.AbstractChannel#write(java.lang.Object)
+	 */
+	@Override
+	public ChannelFuture write(Object message) {
+		if(message instanceof DefaultWebSocketFrame) {
+			writer.write(((DefaultWebSocketFrame)message).getTextData());
+		} else {
+			throw new IllegalStateException("Expected a DefaultWebSocketFrame.");
+		}
+		return null;
+	}
+	
+	public JSONObject getJSONPayload() {
+		if(writer.getBuffer().length() == 0) {
+			return null;
+		}
+		return getJSONPayload(0);
+	}
+	
+	public JSONObject getJSONPayload(long waitTimeout) {
+		long start = System.currentTimeMillis();
+		while(writer.getBuffer().length() == 0) {
+			if(System.currentTimeMillis() > start + waitTimeout) {
+				throw new RuntimeException("Timed out waiting for data to be pushed onto the channel.");
+			}
+		}
+		
+		try {
+			return new JSONObject(writer.toString());
+		} catch (JSONException e) {
+			throw new RuntimeException("Invalid JSON payload [" + writer.toString() + "].", e);
+		} finally {
+			clear();
+		}
+	}
+	
+	public void clear() {
+		writer.getBuffer().setLength(0);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#bind(java.net.SocketAddress)
+	 */
+	@Override
+	public ChannelFuture bind(SocketAddress arg0) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#close()
+	 */
+	@Override
+	public ChannelFuture close() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#connect(java.net.SocketAddress)
+	 */
+	@Override
+	public ChannelFuture connect(SocketAddress arg0) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#disconnect()
+	 */
+	@Override
+	public ChannelFuture disconnect() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getCloseFuture()
+	 */
+	@Override
+	public ChannelFuture getCloseFuture() {
+		return (ChannelFuture) Proxy.newProxyInstance(getClass().getClassLoader(),
+                new Class[] { ChannelFuture.class },
+                new InvocationHandler() {
+					public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
+						return null;
+					}
+		});
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getFactory()
+	 */
+	@Override
+	public ChannelFactory getFactory() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getId()
+	 */
+	@Override
+	public Integer getId() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getInterestOps()
+	 */
+	@Override
+	public int getInterestOps() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getParent()
+	 */
+	@Override
+	public Channel getParent() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#getPipeline()
+	 */
+	@Override
+	public ChannelPipeline getPipeline() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#isOpen()
+	 */
+	@Override
+	public boolean isOpen() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#isReadable()
+	 */
+	@Override
+	public boolean isReadable() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#isWritable()
+	 */
+	@Override
+	public boolean isWritable() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#setInterestOps(int)
+	 */
+	@Override
+	public ChannelFuture setInterestOps(int arg0) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#setReadable(boolean)
+	 */
+	@Override
+	public ChannelFuture setReadable(boolean arg0) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#unbind()
+	 */
+	@Override
+	public ChannelFuture unbind() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.Channel#write(java.lang.Object, java.net.SocketAddress)
+	 */
+	@Override
+	public ChannelFuture write(Object arg0, SocketAddress arg1) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.lang.Comparable#compareTo(java.lang.Object)
+	 */
+	@Override
+	public int compareTo(Channel o) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+}


Property changes on: trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannel.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannelHandlerContext.java
===================================================================
--- trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannelHandlerContext.java	                        (rev 0)
+++ trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannelHandlerContext.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipeline;
+
+/**
+ * 
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class MockChannelHandlerContext implements ChannelHandlerContext {
+	
+	private MockChannel channel;
+
+	/**
+	 * @param channel
+	 */
+	public MockChannelHandlerContext(MockChannel channel) {
+		this.channel = channel;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#canHandleDownstream()
+	 */
+	@Override
+	public boolean canHandleDownstream() {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#canHandleUpstream()
+	 */
+	@Override
+	public boolean canHandleUpstream() {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#getAttachment()
+	 */
+	@Override
+	public Object getAttachment() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#getChannel()
+	 */
+	@Override
+	public Channel getChannel() {
+		return channel;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#getHandler()
+	 */
+	@Override
+	public ChannelHandler getHandler() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#getName()
+	 */
+	@Override
+	public String getName() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#getPipeline()
+	 */
+	@Override
+	public ChannelPipeline getPipeline() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#sendDownstream(org.jboss.netty.channel.ChannelEvent)
+	 */
+	@Override
+	public void sendDownstream(ChannelEvent arg0) {
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#sendUpstream(org.jboss.netty.channel.ChannelEvent)
+	 */
+	@Override
+	public void sendUpstream(ChannelEvent arg0) {
+	}
+
+	/* (non-Javadoc)
+	 * @see org.jboss.netty.channel.ChannelHandlerContext#setAttachment(java.lang.Object)
+	 */
+	@Override
+	public void setAttachment(Object arg0) {
+	}
+}


Property changes on: trunk/server/websocket/src/test/java/org/infinispan/websocket/MockChannelHandlerContext.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/MockClient.java
===================================================================
--- trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/MockClient.java	                        (rev 0)
+++ trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/MockClient.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket.handlers;
+
+import org.infinispan.Cache;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.websocket.MockChannel;
+import org.infinispan.websocket.MockChannelHandlerContext;
+import org.infinispan.websocket.OpHandler;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+public class MockClient {
+	
+	private String cacheName;
+	private CacheManager cacheManager;
+	private Cache<Object, Object> cache;
+
+	private OpHandler putHandler = new PutHandler(); 
+	private OpHandler getHandler = new GetHandler(); 
+	private OpHandler removeHandler = new RemoveHandler(); 
+	private OpHandler notifyHandler = new NotifyHandler();
+	private MockChannelHandlerContext ctx;
+	
+	public MockClient(String cacheName, MockChannel mockChannel) {
+		this.cacheName = cacheName;
+		this.ctx = new MockChannelHandlerContext(mockChannel);
+		
+		cacheManager = new DefaultCacheManager();
+		cache = cacheManager.getCache(cacheName);
+	}
+	
+	public void put(String key, String value) {
+		callHandler(putHandler, toPut(key, value, "text/plain"));
+	}
+	
+	public void put(String key, JSONObject value) {
+		callHandler(putHandler, toPut(key, value.toString(), "application/json"));
+	}
+
+	public void get(String key) {
+		callHandler(getHandler, toGet(key));
+	}
+
+	public void remove(String key) {
+		callHandler(removeHandler, toRemove(key));
+	}
+
+	public void notify(String key) {
+		callHandler(notifyHandler, toNotify(key));
+	}
+
+	public void unnotify(String key) {
+		callHandler(notifyHandler, toUnnotify(key));
+	}
+	
+	public Cache<Object, Object> getCache() {
+		return cache;
+	}
+
+	private void callHandler(OpHandler handler, JSONObject jsonObj) {
+		try {
+			handler.handleOp(jsonObj, cache, ctx);
+		} catch (JSONException e) {
+			throw new RuntimeException("JSON Exception", e);
+		}
+	}
+	
+	private JSONObject toPut(String key, String value, String mimeType) {
+		JSONObject jsonObj = new JSONObject();
+		
+		try {
+			jsonObj.put(OpHandler.OP_CODE, "put");
+			jsonObj.put(OpHandler.CACHE_NAME, cacheName);
+			jsonObj.put(OpHandler.KEY, key);
+			jsonObj.put(OpHandler.VALUE, value);
+			jsonObj.put(OpHandler.MIME, mimeType);
+		} catch (JSONException e) {
+			throw new RuntimeException("JSON Exception", e);
+		}
+		
+		return jsonObj;
+	}
+	
+	private JSONObject toGet(String key) {
+		JSONObject jsonObj = new JSONObject();
+		
+		try {
+			jsonObj.put(OpHandler.OP_CODE, "get");
+			jsonObj.put(OpHandler.CACHE_NAME, cacheName);
+			jsonObj.put(OpHandler.KEY, key);
+		} catch (JSONException e) {
+			throw new RuntimeException("JSON Exception", e);
+		}
+		
+		return jsonObj;
+	}
+	
+	private JSONObject toRemove(String key) {
+		JSONObject jsonObj = new JSONObject();
+		
+		try {
+			jsonObj.put(OpHandler.OP_CODE, "remove");
+			jsonObj.put(OpHandler.CACHE_NAME, cacheName);
+			jsonObj.put(OpHandler.KEY, key);
+		} catch (JSONException e) {
+			throw new RuntimeException("JSON Exception", e);
+		}
+		
+		return jsonObj;
+	}
+	
+	private JSONObject toNotify(String key) {
+		JSONObject jsonObj = new JSONObject();
+		
+		try {
+			jsonObj.put(OpHandler.OP_CODE, "notify");
+			jsonObj.put(OpHandler.CACHE_NAME, cacheName);
+			jsonObj.put(OpHandler.KEY, key);
+		} catch (JSONException e) {
+			throw new RuntimeException("JSON Exception", e);
+		}
+		
+		return jsonObj;
+	}
+	
+	private JSONObject toUnnotify(String key) {
+		JSONObject jsonObj = new JSONObject();
+		
+		try {
+			jsonObj.put(OpHandler.OP_CODE, "unnotify");
+			jsonObj.put(OpHandler.CACHE_NAME, cacheName);
+			jsonObj.put(OpHandler.KEY, key);
+		} catch (JSONException e) {
+			throw new RuntimeException("JSON Exception", e);
+		}
+		
+		return jsonObj;
+	}
+}


Property changes on: trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/MockClient.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/OpHandlerTest.java
===================================================================
--- trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/OpHandlerTest.java	                        (rev 0)
+++ trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/OpHandlerTest.java	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,93 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.infinispan.websocket.handlers;
+
+import org.infinispan.websocket.MockChannel;
+import org.infinispan.websocket.OpHandler;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+/**
+ * 
+ * @author <a href="mailto:tom.fennelly at gmail.com">tom.fennelly at gmail.com</a>
+ */
+ at Test
+public class OpHandlerTest {
+	
+	@Test
+	public void test() throws JSONException {
+		MockChannel mockChannel = new MockChannel();
+		MockClient firstCacheClient = new MockClient("firstCache", mockChannel);
+		JSONObject jsonPayload;
+ 
+		// Put...
+		firstCacheClient.put("a", "aVal");
+		firstCacheClient.put("b", "bVal");
+		
+		// Get...
+		firstCacheClient.get("a");
+		jsonPayload = mockChannel.getJSONPayload();
+		Assert.assertEquals("firstCache", jsonPayload.get(OpHandler.CACHE_NAME));
+		Assert.assertEquals("a", jsonPayload.get(OpHandler.KEY));
+		Assert.assertEquals("aVal", jsonPayload.get(OpHandler.VALUE));
+		Assert.assertEquals("text/plain", jsonPayload.get(OpHandler.MIME));		
+		firstCacheClient.get("b");
+		jsonPayload = mockChannel.getJSONPayload();
+		Assert.assertEquals("firstCache", jsonPayload.get(OpHandler.CACHE_NAME));
+		Assert.assertEquals("b", jsonPayload.get(OpHandler.KEY));
+		Assert.assertEquals("bVal", jsonPayload.get(OpHandler.VALUE));
+		Assert.assertEquals("text/plain", jsonPayload.get(OpHandler.MIME));
+		firstCacheClient.get("x"); // not in cache
+		jsonPayload = mockChannel.getJSONPayload();
+		Assert.assertEquals("firstCache", jsonPayload.get(OpHandler.CACHE_NAME));
+		Assert.assertEquals("x", jsonPayload.get(OpHandler.KEY));
+		Assert.assertEquals(null, jsonPayload.get(OpHandler.VALUE));
+		
+		// Notify...
+		firstCacheClient.notify("a");
+		// Call to notify immediately pushes the value and then pushes it again later on modify...
+		jsonPayload = mockChannel.getJSONPayload(1000);
+		Assert.assertEquals("aVal", jsonPayload.get(OpHandler.VALUE));
+		// Modify the value should result in a push notification...
+		firstCacheClient.getCache().put("a", "aNewValue");
+		jsonPayload = mockChannel.getJSONPayload();
+		Assert.assertEquals("aNewValue", jsonPayload.get(OpHandler.VALUE));
+		// Modify something we're not listening to... nothing should happen...
+		firstCacheClient.getCache().put("b", "bNewValue");
+		try {
+			mockChannel.getJSONPayload(500);
+			Assert.fail("Expected timeout");
+		} catch (RuntimeException e) {
+			Assert.assertEquals("Timed out waiting for data to be pushed onto the channel.", e.getMessage());
+		}		
+		
+		// Remove...
+		firstCacheClient.remove("a");
+		firstCacheClient.get("a");
+		jsonPayload = mockChannel.getJSONPayload();
+		Assert.assertEquals("firstCache", jsonPayload.get(OpHandler.CACHE_NAME));
+		Assert.assertEquals("a", jsonPayload.get(OpHandler.KEY));
+		Assert.assertEquals(null, jsonPayload.get(OpHandler.VALUE));
+	}	
+}


Property changes on: trunk/server/websocket/src/test/java/org/infinispan/websocket/handlers/OpHandlerTest.java
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + LF

Added: trunk/server/websocket/src/test/resources/query-cache.html
===================================================================
--- trunk/server/websocket/src/test/resources/query-cache.html	                        (rev 0)
+++ trunk/server/websocket/src/test/resources/query-cache.html	2010-04-28 14:43:23 UTC (rev 1730)
@@ -0,0 +1,38 @@
+<html>
+	<head>
+		<title>Infinispan Cache Query</title>
+	</head>
+	<body>
+		<script type="text/javascript" src="http://localhost:61999/infinispan-ws.js" />
+		<script type="text/javascript">
+			<!--
+            var customerCache = new Cache("customerCache");
+	        customerCache.registerCallback(customerCacheCallback); 
+	        customerCache.notify("*");
+	        
+	        var orderCache = new Cache("orderCache");
+		    orderCache.registerCallback(orderCacheCallback); 
+		    orderCache.notify("*");
+
+			function customerCacheCallback(key, value) {
+				alert("customer cache");
+			}
+			function orderCacheCallback(key, value) {
+				var node = document.getElementById(key);				
+				node.value = value;
+			}
+			-->
+		</script>
+		<form onsubmit="return false;">
+		     <input type="text" id="ord" />
+			 <input type="button" value="Put" onclick="orderCache.put('ord', this.form.ord.value)" />
+			 <input type="button" value="Get" onclick="orderCache.get('ord')" />
+			 <input type="button" value="Remove" onclick="orderCache.remove('ord')" />
+			 <br/>
+		     <input type="text" id="cust" />
+			 <input type="button" value="Put" onclick="orderCache.put('cust', this.form.cust.value)" />
+			 <input type="button" value="Get" onclick="orderCache.get('cust')" />
+			 <input type="button" value="Remove" onclick="orderCache.remove('cust')" />
+		</form>
+	</body>
+</html>



More information about the infinispan-commits mailing list