Index: src/CacheWolf/PreferencesScreen.java
===================================================================
--- src/CacheWolf/PreferencesScreen.java	(Revision 2717)
+++ src/CacheWolf/PreferencesScreen.java	(Arbeitskopie)
@@ -362,7 +362,7 @@
 				Editor s = gpo.getEditor();
 				gpo.forwardGpsChkB.setState(pref.forwardGPS);
 				gpo.inputBoxForwardHost.setText(pref.forwardGpsHost);
-				gpo.gpsdChkB.setState(pref.useGPSD);
+				gpo.chcUseGpsd.select(pref.useGPSD);
 				if(pref.gpsdPort!=pref.DEFAULT_GPSD_PORT){
 					gpo.inputBoxGpsdHost.setText(pref.gpsdHost + ":" + Convert.toString(pref.gpsdPort));
 				}else{
@@ -376,7 +376,7 @@
 					pref.mySPO.baudRate = gpo.baudRate;
 					pref.forwardGPS = gpo.forwardGpsChkB.getState();
 					pref.forwardGpsHost = gpo.inputBoxForwardHost.getText();
-					pref.useGPSD = gpo.gpsdChkB.getState();
+					pref.useGPSD = gpo.chcUseGpsd.getInt();
 					String gpsdHostString = gpo.inputBoxGpsdHost.getText();	// hostname[:port]
 					int posColon = gpsdHostString.indexOf(':');
 					if(posColon>=0){
Index: src/CacheWolf/Preferences.java
===================================================================
--- src/CacheWolf/Preferences.java	(Revision 2717)
+++ src/CacheWolf/Preferences.java	(Arbeitskopie)
@@ -43,6 +43,9 @@
 	public final int DEFAULT_LOGS_PER_PAGE=5;
 	public final int DEFAULT_INITIAL_HINT_HEIGHT=10;
 	public final int DEFAULT_GPSD_PORT=2947;
+	public static final int GPSD_DISABLED   = 0; // do not use gpsd
+	public static final int GPSD_FORMAT_OLD = 1; // use old protocol
+	public static final int GPSD_FORMAT_NEW = 2; // use new protocol (JSON)
 	public static final int YES = 0;
 	public static final int NO = 1;
 	public static final int ASK = 2;
@@ -167,8 +170,8 @@
 	public boolean forwardGPS = false;
 	/** IP address for forwarding GPS data */
 	public String forwardGpsHost = "192.168.1.15";
-	/** True if the GPS data should be received from a GPSD on this or another host */
-	public boolean useGPSD = false;
+	/** Should GPS data be received from a GPSD on this or another host? */
+	public int useGPSD = GPSD_DISABLED;
 	/** IP address of GPSD host */
 	public String gpsdHost = "127.0.0.1";
 	/** Port for forwarding GPS data */
@@ -438,7 +441,7 @@
 			forwardGpsHost = atts.getValue("destinationHost");
 		}
 		else if(name.equals("gpsd")) {
-			useGPSD = Convert.toBoolean(atts.getValue("active"));
+			useGPSD = Convert.toInt(atts.getValue("active"));
 			gpsdHost = atts.getValue("host");
 			gpsdPort = Convert.toInt(atts.getValue("port"));
 		}
@@ -685,7 +688,7 @@
 			outp.print("    <proxy prx = \"" + SafeXML.clean(myproxy) + "\" prt = \"" + SafeXML.clean(myproxyport) + "\" active = \"" + SafeXML.strxmlencode(proxyActive) + "\" />\n");
 			outp.print("    <port portname = \"" + SafeXML.clean(mySPO.portName) + "\" baud = \"" + SafeXML.strxmlencode(mySPO.baudRate) + "\"/>\n");
 			outp.print("    <portforward active= \"" + SafeXML.clean(Convert.toString(forwardGPS)) + "\" destinationHost = \"" + SafeXML.clean(forwardGpsHost) + "\"/>\n");
-			outp.print("    <gpsd active= \"" + SafeXML.clean(Convert.toString(useGPSD)) + "\" host = \"" + SafeXML.clean(gpsdHost) + "\" port = \"" + SafeXML.strxmlencode(gpsdPort) + "\"/>\n");
+			outp.print("    <gpsd active= \"" + SafeXML.strxmlencode(useGPSD) + "\" host = \"" + SafeXML.clean(gpsdHost) + "\" port = \"" + SafeXML.strxmlencode(gpsdPort) + "\"/>\n");
 			outp.print("    <portlog active= \"" + SafeXML.clean(Convert.toString(logGPS)) + "\" logTimer = \"" + SafeXML.clean(logGPSTimer) + "\"/>\n");
 			outp.print("    <font size =\"" + SafeXML.strxmlencode(fontSize) + "\"/>\n");
 			outp.print("    <screen menuattop=\""+menuAtTop+"\" tabsattop=\""+tabsAtTop+"\" showstatus=\""+showStatus+"\" hasclosebutton=\""+hasCloseButton+
Index: src/CacheWolf/GPSPortOptions.java
===================================================================
--- src/CacheWolf/GPSPortOptions.java	(Revision 2717)
+++ src/CacheWolf/GPSPortOptions.java	(Arbeitskopie)
@@ -35,6 +35,13 @@
 import ewe.sys.*;
 import ewe.util.*;
 
+// Stuff needed by the GpsdThread class:
+import net.ax86.GPS;
+import net.ax86.GPSException;
+import org.json.JSONObject;
+import org.json.JSONException;
+
+
 /**
  * Thread for reading data from COM-port
  *
@@ -95,7 +102,100 @@
 	}
 }
 
-class GpsdThread extends mThread{
+/**
+ * Thread for reading data from gpsd and simply displaying it to the user.
+ *
+ * This is a modified version of {@link CacheWolf.navi.GpsdThread}.
+ *
+ * @author Tilman Blumenbach
+ */
+class GpsdThread extends mThread {
+	GPS gpsObj;
+	TextDisplay out;
+	boolean run;
+
+
+	public GpsdThread(TextDisplay td) throws IOException, JSONException, GPSException {
+		JSONObject response;
+		int proto_major;
+
+		gpsObj = new GPS(Global.getPref().gpsdHost, Global.getPref().gpsdPort);
+		gpsObj.stream( GPS.WATCH_ENABLE );
+
+		// Check major protocol version:
+		response = gpsObj.read();
+
+		if( ! response.getString( "class" ).equals( "VERSION" ) ) {
+			throw new GPSException( "Expected VERSION object at connect." );
+		} else if( ( proto_major = response.getInt( "proto_major" ) ) != 3 ) {
+			throw new GPSException( "Invalid protocol API version; got " +
+					proto_major + ", wanted 3." );
+		}
+
+		out = td;
+		// Show data to user:
+		out.appendText(response.toString(2) + "\n", true);
+	}
+
+
+	public void run() {
+		JSONObject response;
+
+		run = true;
+		while (run) {
+			try {
+				sleep(1000);
+			} catch (InterruptedException e) {
+				Global.getPref().log("Ignored Exception", e, true);
+			}
+
+			if( gpsObj == null ) {
+				continue;
+			}
+
+			try {
+				/* Tblue> This is ugly, but BufferedReader::ready() seems to
+				 *        be broken in Ewe, so instead of only polling when
+				 *        there is no data from gpsd, we poll on every iteration.
+				 *        Not ideal, but works for now.
+				 */
+				gpsObj.poll();
+
+				/* Tblue> TODO: I think this call should not block, but
+				 *              my GPS class does not yet support non-blocking
+				 *              reads... Seems to work, anyway.
+				 */
+				response = gpsObj.read();
+				out.appendText(response.toString(2) + "\n", true);
+
+				// Keep up with new devices:
+				if( response.getString( "class" ).equals( "DEVICE" ) &&
+				    response.has( "activated" ) && response.getDouble( "activated" ) != 0 )
+				{	// This is a new device, we need to tell gpsd we want to watch it:
+					Global.getPref().log( "New GPS device, sending WATCH command." );
+					gpsObj.stream( GPS.WATCH_ENABLE );
+				}
+			} catch( Exception e ) {
+				// We will just ignore this JSON object:
+				Global.getPref().log("Ignored Exception", e, true);
+			}
+		} // while
+	}
+
+
+	public boolean stop() {
+		run = false;
+
+		if( gpsObj == null ) {
+			return true;
+		}
+
+		gpsObj.cleanup();
+		return false; 
+	}
+}
+
+class OldGpsdThread extends mThread{
 	Socket gpsdSocket;
 	boolean run;
 	TextDisplay out;
@@ -103,7 +203,7 @@
 	String lastError = new String();
 	public String lastgot;
 	
-	public GpsdThread(TextDisplay td) throws IOException {
+	public OldGpsdThread(TextDisplay td) throws IOException {
 		try{
 			gpsdSocket = new Socket(Global.getPref().gpsdHost, Global.getPref().gpsdPort);
 		} catch (IOException e) {
@@ -177,17 +277,25 @@
 	mLabel  labelForwardHost;
 	public mCheckBox forwardGpsChkB;
 	public mInput inputBoxGpsdHost;
+	mLabel  labelUseGpsd;
+	public mChoice chcUseGpsd;
 	mLabel  labelGpsdHost;
-	public mCheckBox gpsdChkB;
 	public mInput inputBoxLogTimer;
 	mLabel  labelLogTimer;
 	public mCheckBox logGpsChkB;
 	mySerialThread serThread;
 	GpsdThread gpsdThread = null;
+	OldGpsdThread oldGpsdThread = null;
 	boolean gpsRunning = false;
 	MyEditor ed = new MyEditor();
 
-	
+	private String[] useGpsdChoices = new String[]{
+		MyLocale.getMsg(641, "No"),
+		MyLocale.getMsg(99999, "Yes (< v2.91)"),
+		MyLocale.getMsg(99999, "Yes (>= v2.91)"),
+	};
+
+
 	public Editor getEditor(){
 		// The following lines are mainly copied from SerialPortOptions.
 		// Reason: We want to use MyEditor instead of the default Editor,
@@ -224,29 +332,34 @@
 		
 		forwardGpsChkB = new mCheckBox("");
 		ed.addField(ed.addNext(forwardGpsChkB, CellConstants.DONTSTRETCH, (CellConstants.EAST | CellConstants.DONTFILL)), "forwardGpsChkB");
-		labelForwardHost = new mLabel(MyLocale.getMsg(7105, "Forward GPS data to host"));
+		labelForwardHost = new mLabel(MyLocale.getMsg(7105, "Forward GPS data to host (serial port only)"));
 		ed.addField(ed.addNext(labelForwardHost, CellConstants.DONTSTRETCH, (CellConstants.WEST | CellConstants.DONTFILL)), "labelForwardIP");
 		inputBoxForwardHost = new mInput("tcpForwardHost");
 		inputBoxForwardHost.setPromptControl(labelForwardHost);
 		inputBoxForwardHost.setToolTip(MyLocale.getMsg(7106, "All data from GPS will be sent to TCP-port 23\n and can be redirected there to a serial port\n by HW Virtual Serial Port"));
 		ed.addField(ed.addLast(inputBoxForwardHost,0 , (CellConstants.WEST | CellConstants.HFILL)), "tcpForwardHost");
 
-		gpsdChkB = new mCheckBox("");
-		ed.addField(ed.addNext(gpsdChkB, CellConstants.DONTSTRETCH, (CellConstants.EAST | CellConstants.DONTFILL)), "gpsdChkB");
-		labelGpsdHost = new mLabel(MyLocale.getMsg(7121, "Receive GPS from gpsd host"));
-		ed.addField(ed.addNext(labelGpsdHost, CellConstants.DONTSTRETCH, (CellConstants.WEST | CellConstants.DONTFILL)), "labelGpsd");
-		inputBoxGpsdHost = new mInput("GpsdHost");
-		inputBoxGpsdHost.setPromptControl(labelGpsdHost);
-		inputBoxGpsdHost.setToolTip(MyLocale.getMsg(7122, "GPS data will be received from a gpsd server, not from a serial port"));
-		ed.addField(ed.addLast(inputBoxGpsdHost,0 , (CellConstants.WEST | CellConstants.HFILL)), "GpsdHost");
-
 		logGpsChkB = new mCheckBox("");
 		ed.addField(ed.addNext(logGpsChkB, CellConstants.DONTSTRETCH, (CellConstants.EAST | CellConstants.DONTFILL)), "logGpsChkB");
-		labelLogTimer = new mLabel(MyLocale.getMsg(7107, "Interval in sec for logging"));
+		labelLogTimer = new mLabel(MyLocale.getMsg(7107, "Interval in sec for logging (serial port only)"));
 		ed.addField(ed.addNext(labelLogTimer, CellConstants.DONTSTRETCH, (CellConstants.WEST | CellConstants.DONTFILL)), "labelLogTimer");
 		inputBoxLogTimer = new mInput("GPSLogTimer");
 		inputBoxLogTimer.setPromptControl(labelLogTimer);
-		ed.addField(ed.addLast(inputBoxLogTimer,0 , (CellConstants.WEST | CellConstants.HFILL)), "GPSLogTimer");
+		ed.addField(ed.addLast(inputBoxLogTimer, 0, (CellConstants.WEST | CellConstants.HFILL)), "GPSLogTimer");
+
+		labelUseGpsd = new mLabel(MyLocale.getMsg(7121, "Receive GPS data from gpsd:"));
+		ed.addField(ed.addNext(labelUseGpsd, CellConstants.DONTSTRETCH, (CellConstants.EAST | CellConstants.DONTFILL)), "labelUseGpsd");
+		chcUseGpsd = new mChoice( useGpsdChoices, 0 );
+		chcUseGpsd.setPromptControl(labelUseGpsd);
+		chcUseGpsd.setToolTip(MyLocale.getMsg(7122, "GPS data will be received from a gpsd server, not from a serial port"));
+		ed.addField(ed.addLast(chcUseGpsd, 0, (CellConstants.WEST | CellConstants.HFILL)), "UseGpsd");
+
+		labelGpsdHost = new mLabel(MyLocale.getMsg(99999, "gpsd host:"));
+		ed.addField(ed.addNext(labelGpsdHost, CellConstants.DONTSTRETCH, (CellConstants.EAST | CellConstants.DONTFILL)), "labelGpsdHost");
+		inputBoxGpsdHost = new mInput("GpsdHost");
+		inputBoxGpsdHost.setPromptControl(labelGpsdHost);
+		ed.addField(ed.addLast(inputBoxGpsdHost, 0, (CellConstants.WEST | CellConstants.HFILL)), "GpsdHost");
+
 		this.ed.firstFocus = btnUpdatePortList;
 		gpsRunning = false;
 		return ed;
@@ -306,37 +419,67 @@
 		if (field.equals("test")){
 			if (!gpsRunning){
 				ed_.fromControls();
-				if(Global.getPref().useGPSD){
-					txtOutput.setText(MyLocale.getMsg(99999, "Displaying data from gpsd directly:\n"));
-					try {
-						btnScan.set(ControlConstants.Disabled, true);
-						btnScan.repaintNow();
-						gpsdThread = new GpsdThread(txtOutput);
-						gpsdThread.start();
-						btnTest.setText(Gui.getTextFrom(MyLocale.getMsg(7118, "Stop")));
-						gpsRunning = true;
-					} catch (IOException e) {
-						(new MessageBox(MyLocale.getMsg(4403, "Error"), 
-							MyLocale.getMsg(99999, "Could not connect to GPSD.") 
-							+ e.getMessage()
-							+ MyLocale.getMsg(99999, "\npossible reasons:\nGPSD is not running or GPSD host is not reachable"), 
-							FormBase.OKB)).execute(); 
-					}
-				}else{
-					txtOutput.setText(MyLocale.getMsg(7117, "Displaying data from serial port directly:\n"));
-					try {
-						btnScan.set(ControlConstants.Disabled, true);
-						btnScan.repaintNow();
-						this.portName = Common.fixSerialPortName(portName);
-						serThread = new mySerialThread(this, txtOutput);
-						serThread.start();
-						btnTest.setText(Gui.getTextFrom(MyLocale.getMsg(7118, "Stop")));
-						gpsRunning = true;
-					} catch (IOException e) {
-						btnScan.set(ControlConstants.Disabled, false);
-						btnScan.repaintNow();
-						txtOutput.appendText(MyLocale.getMsg(7108, "Failed to open serial port: ") + this.portName + ", IOException: " + e.getMessage() + "\n", true);
-					}
+
+				switch(Global.getPref().useGPSD) {
+					case Preferences.GPSD_FORMAT_NEW:
+						txtOutput.setText(MyLocale.getMsg(99999, "Displaying data from gpsd directly (JSON):\n"));
+						try {
+							btnScan.set(ControlConstants.Disabled, true);
+							btnScan.repaintNow();
+							gpsdThread = new GpsdThread(txtOutput);
+							gpsdThread.start();
+							btnTest.setText(Gui.getTextFrom(MyLocale.getMsg(7118, "Stop")));
+							gpsRunning = true;
+						} catch (IOException e) {
+							(new MessageBox(MyLocale.getMsg(4403, "Error"), 
+								MyLocale.getMsg(99999, "Could not connect to GPSD: ") 
+								+ e.getMessage()
+								+ MyLocale.getMsg(99999, "\nPossible reasons:\nGPSD is not running or GPSD host is not reachable"), 
+								FormBase.OKB)).execute(); 
+						} catch( Exception e ) {
+							// Other error (JSON/GPS).
+							(new MessageBox(MyLocale.getMsg(4403, "Error"),
+								MyLocale.getMsg(99999, "Could not initialize GPSD connection: ") 
+								+ e.getMessage(),
+								FormBase.OKB)).execute();
+						}
+						break;
+
+					case Preferences.GPSD_FORMAT_OLD:
+						txtOutput.setText(MyLocale.getMsg(99999, "Displaying data from gpsd directly (old protocol):\n"));
+						try {
+							btnScan.set(ControlConstants.Disabled, true);
+							btnScan.repaintNow();
+							oldGpsdThread = new OldGpsdThread(txtOutput);
+							oldGpsdThread.start();
+							btnTest.setText(Gui.getTextFrom(MyLocale.getMsg(7118, "Stop")));
+							gpsRunning = true;
+						} catch (IOException e) {
+							(new MessageBox(MyLocale.getMsg(4403, "Error"), 
+								MyLocale.getMsg(99999, "Could not connect to GPSD: ") 
+								+ e.getMessage()
+								+ MyLocale.getMsg(99999, "\nPossible reasons:\nGPSD is not running or GPSD host is not reachable"), 
+								FormBase.OKB)).execute(); 
+						}
+						break;
+
+					case Preferences.GPSD_DISABLED:
+					default:
+						txtOutput.setText(MyLocale.getMsg(7117, "Displaying data from serial port directly:\n"));
+						try {
+							btnScan.set(ControlConstants.Disabled, true);
+							btnScan.repaintNow();
+							this.portName = Common.fixSerialPortName(portName);
+							serThread = new mySerialThread(this, txtOutput);
+							serThread.start();
+							btnTest.setText(Gui.getTextFrom(MyLocale.getMsg(7118, "Stop")));
+							gpsRunning = true;
+						} catch (IOException e) {
+							btnScan.set(ControlConstants.Disabled, false);
+							btnScan.repaintNow();
+							txtOutput.appendText(MyLocale.getMsg(7108, "Failed to open serial port: ") + this.portName + ", IOException: " + e.getMessage() + "\n", true);
+						}
+						break;
 				}
 			}
 			else {
@@ -344,6 +487,8 @@
 					serThread.stop();
 				if(gpsdThread!=null)
 					gpsdThread.stop();
+				if(oldGpsdThread!=null)
+					oldGpsdThread.stop();
 				btnTest.setText(Gui.getTextFrom(MyLocale.getMsg(7104,"Test$t")));
 				gpsRunning = false;
 				btnScan.set(ControlConstants.Disabled, false);
Index: src/CacheWolf/navi/CWGPSPoint.java
===================================================================
--- src/CacheWolf/navi/CWGPSPoint.java	(Revision 2717)
+++ src/CacheWolf/navi/CWGPSPoint.java	(Arbeitskopie)
@@ -14,6 +14,10 @@
 import ewe.sys.*;
 import ewe.io.*;
 
+// Stuff needed by CWGPSPoint::examineGpsd():
+import org.json.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
 
 
 /**
@@ -26,15 +30,15 @@
 	public static final int LOGRAW  = 0x02;
 	public static final int LOGALL  = LOGNMEA|LOGRAW;
 
-	public double Speed; //Speed
-	public double Bear;	//Bearing
-	public String Time; //Time
-	public String Date;
-	public int Fix; //Fix
-	public int numSat; //Satellites in use, -1 indicates no data, -2 that data could not be interpreted
-	public int numSatsInView; //Satellites in view
+	public double Speed; // Speed: km/h
+	public double Bear;	// Bearing
+	public String Time; // Time: HHmmss.SS
+	public String Date; // Date: ddMMyy
+	public int Fix; // Fix (0: none, 1: GPS, 2: differential GPS). See getFix() for more possible values.
+	public int numSat; // Satellites in use, -1 indicates no data, -2 that data could not be interpreted
+	public int numSatsInView; // Satellites in view
 	public double HDOP; // Horizontal dilution of precision
-	public double Alt; //Altitude
+	public double Alt; // Altitude in meters
 
 	//Logging
 	int logTimer = 0;
@@ -384,12 +388,95 @@
 
 
 	/**
-	 * Sets the attributes from a GPSD string
+	 * Sets the attributes from a GPSD <code>POLL</code> object
+	 *
+	 * @param gps	{@link JSONObject} containing GPS <code>POLL</code> data.
+	 * @return true if some data could be interpreted false otherwise
+	 *         Tblue> For now, this always returns true. Any ideas what
+	 *                should be treated as not interpretable?
+	 * @throws JSONException When trying to access a not existing key (should not happen!).
+	 */
+	public boolean examineGpsd(JSONObject gps) throws JSONException {
+		JSONArray fixes    = gps.getJSONArray( "fixes" );
+		JSONArray skyviews = gps.getJSONArray( "skyviews" );
+		JSONArray sats;
+		JSONObject a_fix, a_skyview;
+		int fix_mode, i;
+		double my_lat, my_lon;
+		Time TimeObj = new Time();
+
+		lastStrExamined = gps.toString();
+
+		TimeObj.setTime( (long)( gps.getDouble( "timestamp" ) * 1000 ) );
+		this.Time = TimeObj.format( "HHmmss.SS" );
+		this.Date = TimeObj.format( "ddMMyy" );
+
+		if( fixes.length() > 0 ) {
+			// We will only use the first fix.
+			// TODO: Randomize?
+			a_fix = fixes.getJSONObject( 0 );
+			
+			// 0: no mode seen yet, 1: none, 2: 2D, 3: 3D.
+			// Tblue> Does 3D mean differential here?
+			this.Fix = ( fix_mode = a_fix.getInt( "mode" ) ) > 0 ? fix_mode - 1 : 0;
+
+			// Speed is in m/s.
+			if( a_fix.has( "speed" ) ) {
+				this.Speed = ( a_fix.getDouble( "speed" ) / 1000 ) * 60 * 60;
+			}
+
+			if( a_fix.has( "track" ) ) {
+				this.Bear = a_fix.getDouble( "track" );
+			}
+
+			if( a_fix.has( "alt" ) ) {
+				this.Alt = a_fix.getDouble( "alt" );
+			}
+
+			if( a_fix.has( "lat" ) && a_fix.has( "lon" ) ) {
+				my_lat = a_fix.getDouble( "lat" );
+				my_lon = a_fix.getDouble( "lon" );
+
+				set( my_lat > 0 ? "N" : "S", String.valueOf( my_lat ), "0", "0",
+					 my_lon > 0 ? "E" : "W", String.valueOf( my_lon ), "0", "0",
+					 TransformCoordinates.DD );
+			}
+		}
+
+		if( skyviews.length() > 0 )
+		{
+			// We will only use the first skyview.
+			// TODO: Randomize?
+			a_skyview = skyviews.getJSONObject( 0 );
+
+			if( a_skyview.has( "hdop" ) ) {
+				this.HDOP = a_skyview.getDouble( "hdop" );
+			}
+
+			sats = a_skyview.getJSONArray( "satellites" );
+			this.numSatsInView = sats.length();
+
+			if( this.numSatsInView > 0 )
+			{
+				for( this.numSat = 0, i = 0; i < this.numSatsInView; i++ ) {
+					if( sats.getJSONObject( i ).getBoolean( "used" ) ) {
+						this.numSat++;
+					}
+				}
+			}
+		}
+
+		return true;
+	}
+
+
+	/**
+	 * Sets the attributes from an old-style GPSD string.
 	 * @param gps	GPSD string with data to examine
 	 *              Format: GPSD,key=value,...
 	 * @return true if some data could be interpreted false otherwise
 	 */
-	public boolean examineGpsd(String gps){
+	public boolean examineOldGpsd(String gps){
 		boolean valid = false;
 		if(!gps.startsWith("GPSD,"))
 			return false;
@@ -455,6 +542,7 @@
 		return valid;
 	}
 
+
 	private boolean checkSumOK(String nmea){
 		int startPos = 1; // begin after $
 		int endPos = nmea.length() - 3;// without * an two checksum chars
Index: src/CacheWolf/navi/GotoPanel.java
===================================================================
--- src/CacheWolf/navi/GotoPanel.java	(Revision 2717)
+++ src/CacheWolf/navi/GotoPanel.java	(Arbeitskopie)
@@ -264,13 +264,13 @@
 		}
 		// receiving no data
 		if (fix == -1) {
-			if (gpsStatus != RED) (new MessageBox(MyLocale.getMsg(321, "Error"), MyLocale.getMsg(1510, "No data from GPS\nConnection to serial port closed"),FormBase.OKB)).exec();
+			if (gpsStatus != RED) (new MessageBox(MyLocale.getMsg(321, "Error"), MyLocale.getMsg(1510, "No data from GPS.\nConnection to serial port/gpsd closed."),FormBase.OKB)).exec();
 			gpsStatus = RED;
 			myNavigation.stopGps();
 		}
 		// cannot interpret data
 		if (fix == -2) {
-			if (gpsStatus != RED) (new MessageBox(MyLocale.getMsg(321, "Error"), MyLocale.getMsg(1511, "Cannot interpret data from GPS\n possible reasons:\n wrong Port,\n wrong Baudrate,\n not NMEA-Protocol\nConnection to serial port closed\nLast String tried to interprete:\n")+myNavigation.gpsPos.lastStrExamined, FormBase.OKB)).exec();
+			if (gpsStatus != RED) (new MessageBox(MyLocale.getMsg(321, "Error"), MyLocale.getMsg(1511, "Cannot interpret data from GPS/gpsd!\nPossible reasons:\nWrong port,\nwrong baud rate,\ninvalid protocol (need NMEA/gpsd).\nConnection to serial port closed.\nLast String tried to interpret:\n")+myNavigation.gpsPos.lastStrExamined, FormBase.OKB)).exec();
 			gpsStatus = RED;
 			myNavigation.stopGps(); // TODO automatic in myNavigate?
 		}
Index: src/CacheWolf/navi/Navigate.java
===================================================================
--- src/CacheWolf/navi/Navigate.java	(Revision 2717)
+++ src/CacheWolf/navi/Navigate.java	(Arbeitskopie)
@@ -10,13 +10,20 @@
 import ewe.io.SerialPortOptions;
 import ewe.net.Socket;
 import ewe.sys.mThread;
+import ewe.sys.Vm;
 import ewe.ui.FormBase;
 import ewe.ui.MessageBox;
 import ewe.util.mString;
 import CacheWolf.CacheHolder;
 import CacheWolf.navi.MovingMap;
 
+// Stuff needed by the GpsdThread class:
+import net.ax86.GPS;
+import net.ax86.GPSException;
+import org.json.JSONObject;
+import org.json.JSONException;
 
+
 /**
  * Non-Gui Class to handle all things regarding navigation
  * (GPS, Sun direction etc.)
@@ -37,6 +44,7 @@
 	public GotoPanel gotoPanel = null;
 	public MovingMap movingMap = null;
 	public GpsdThread gpsdThread = null;
+	public OldGpsdThread oldGpsdThread = null;
 	public SerialThread serThread = null;
 	public Preferences pref = Global.getPref();
 	public UpdateThread tickerThread;
@@ -55,52 +63,84 @@
 	public void startGps(boolean loggingOn, int loggingIntervall) {
 		lograw = loggingOn;
 		logIntervall = loggingIntervall; // TODO switch on and off during serthread running
-		if(Global.getPref().useGPSD){
-			try {
-				gpsdThread = new GpsdThread(gpsPos);
-				gpsdThread.start();
-				startDisplayTimer();
-				gpsRunning = true;
-				curTrack = new Track(trackColor); // TODO addTrack here to MovingMap? see MovingMapPanel.snapToGps
-				if (lograw)	gpsPos.startLog(Global.getProfile().dataDir, logIntervall, CWGPSPoint.LOGALL);
-				if (gotoPanel != null) gotoPanel.gpsStarted();
-				if (movingMap != null) movingMap.gpsStarted();
-			} catch (IOException e) {
-				(new MessageBox(MyLocale.getMsg(4403, "Error"),
-					MyLocale.getMsg(4408, "Could not connect to GPSD:")
-					+ e.getMessage()
-					+ MyLocale.getMsg(4409, "\npossible reasons:\nGPSD is not running or GPSD host is not reachable"),
-					FormBase.OKB)).execute();
-			}
-		}else{
-			if (serThread != null) if (serThread.isAlive()) return; // TODO use gpsRunning
-			try {
-				serThread = new SerialThread(pref.mySPO, gpsPos, (pref.forwardGPS ? pref.forwardGpsHost : ""));
-				if (pref.forwardGPS && !serThread.tcpForward) {
-					(new MessageBox(MyLocale.getMsg(4400, "Warning"),
-							MyLocale.getMsg(4401, "Ignoring error:\n could not forward GPS data to host:\n")
-							+ pref.forwardGpsHost+"\n" + serThread.lastError
-							+ MyLocale.getMsg(4402, "\nstop and start GPS to retry"), FormBase.OKB)).exec();
+		
+		switch(Global.getPref().useGPSD) {
+			// Tblue> TODO: NEW vs. OLD: This is ugly! The only line that's
+			//        different is the one where the object is created!
+			case Preferences.GPSD_FORMAT_NEW:
+				try {
+					gpsdThread = new GpsdThread(gpsPos);
+					gpsdThread.start();
+					startDisplayTimer();
+					gpsRunning = true;
+					curTrack = new Track(trackColor); // TODO addTrack here to MovingMap? see MovingMapPanel.snapToGps
+					if (gotoPanel != null) gotoPanel.gpsStarted();
+					if (movingMap != null) movingMap.gpsStarted();
+				} catch (IOException e) {
+					(new MessageBox(MyLocale.getMsg(4403, "Error"),
+						MyLocale.getMsg(4408, "Could not connect to GPSD: ")
+						+ e.getMessage()
+						+ MyLocale.getMsg(4409, "\nPossible reasons:\nGPSD is not running or GPSD host is not reachable"),
+						FormBase.OKB)).execute();
+				} catch( Exception e ) {
+					// Other error (JSON/GPS).
+					(new MessageBox(MyLocale.getMsg(4403, "Error"),
+						MyLocale.getMsg(99999, "Could not initialize GPSD connection: ") 
+						+ e.getMessage(),
+						FormBase.OKB)).execute();
 				}
-				serThread.start();
-				startDisplayTimer();
-				gpsRunning = true;
-				curTrack = new Track(trackColor); // TODO addTrack here to MovingMap? see MovingMapPanel.snapToGps
-				if (lograw)	gpsPos.startLog(Global.getProfile().dataDir, logIntervall, CWGPSPoint.LOGALL);
-				if (gotoPanel != null) gotoPanel.gpsStarted();
-				if (movingMap != null) movingMap.gpsStarted();
-			} catch (IOException e) {
-				(new MessageBox(MyLocale.getMsg(4403, "Error"),
-						MyLocale.getMsg(4404, "Could not connect to GPS-receiver.\n Error while opening serial Port ")
+				break;
+
+			case Preferences.GPSD_FORMAT_OLD:
+				try {
+					oldGpsdThread = new OldGpsdThread(gpsPos);
+					oldGpsdThread.start();
+					startDisplayTimer();
+					gpsRunning = true;
+					curTrack = new Track(trackColor); // TODO addTrack here to MovingMap? see MovingMapPanel.snapToGps
+					if (lograw)	gpsPos.startLog(Global.getProfile().dataDir, logIntervall, CWGPSPoint.LOGALL);
+					if (gotoPanel != null) gotoPanel.gpsStarted();
+					if (movingMap != null) movingMap.gpsStarted();
+				} catch (IOException e) {
+					(new MessageBox(MyLocale.getMsg(4403, "Error"),
+						MyLocale.getMsg(4408, "Could not connect to GPSD: ")
 						+ e.getMessage()
-						+ MyLocale.getMsg(4405, "\npossible reasons:\n Another (GPS-)program is blocking the port\nwrong port\nOn Loox: active infra-red port is blocking GPS"),
+						+ MyLocale.getMsg(4409, "\nPossible reasons:\nGPSD is not running or GPSD host is not reachable"),
 						FormBase.OKB)).execute();
-			} catch (UnsatisfiedLinkError e) {
-				(new MessageBox(MyLocale.getMsg(4403, "Error"),
-						MyLocale.getMsg(4404, "Could not connect to GPS-receiver.\n Error while opening serial Port ")
-						+ MyLocale.getMsg(4406, "Please copy jave_ewe.dll into the directory of the cachewolf program"),
-						FormBase.OKB)).execute();
-			}
+				}
+				break;
+
+			case Preferences.GPSD_DISABLED:
+			default:
+				if (serThread != null) if (serThread.isAlive()) return; // TODO use gpsRunning
+				try {
+					serThread = new SerialThread(pref.mySPO, gpsPos, (pref.forwardGPS ? pref.forwardGpsHost : ""));
+					if (pref.forwardGPS && !serThread.tcpForward) {
+						(new MessageBox(MyLocale.getMsg(4400, "Warning"),
+								MyLocale.getMsg(4401, "Ignoring error:\n could not forward GPS data to host:\n")
+								+ pref.forwardGpsHost+"\n" + serThread.lastError
+								+ MyLocale.getMsg(4402, "\nstop and start GPS to retry"), FormBase.OKB)).exec();
+					}
+					serThread.start();
+					startDisplayTimer();
+					gpsRunning = true;
+					curTrack = new Track(trackColor); // TODO addTrack here to MovingMap? see MovingMapPanel.snapToGps
+					if (lograw)	gpsPos.startLog(Global.getProfile().dataDir, logIntervall, CWGPSPoint.LOGALL);
+					if (gotoPanel != null) gotoPanel.gpsStarted();
+					if (movingMap != null) movingMap.gpsStarted();
+				} catch (IOException e) {
+					(new MessageBox(MyLocale.getMsg(4403, "Error"),
+							MyLocale.getMsg(4404, "Could not connect to GPS-receiver.\n Error while opening serial Port ")
+							+ e.getMessage()
+							+ MyLocale.getMsg(4405, "\npossible reasons:\n Another (GPS-)program is blocking the port\nwrong port\nOn Loox: active infra-red port is blocking GPS"),
+							FormBase.OKB)).execute();
+				} catch (UnsatisfiedLinkError e) {
+					(new MessageBox(MyLocale.getMsg(4403, "Error"),
+							MyLocale.getMsg(4404, "Could not connect to GPS-receiver.\n Error while opening serial Port ")
+							+ MyLocale.getMsg(4406, "Please copy jave_ewe.dll into the directory of the cachewolf program"),
+							FormBase.OKB)).execute();
+				}
+				break;
 		}
 	}
 	public void startDisplayTimer() {
@@ -115,6 +155,7 @@
 	public void stopGps() {
 		if(serThread!=null)	serThread.stop();
 		if(gpsdThread!=null) gpsdThread.stop();
+		if(oldGpsdThread!=null) oldGpsdThread.stop();
 		stopDisplayTimer();
 		gpsPos.stopLog();
 		gpsRunning = false;
@@ -123,7 +164,10 @@
 	}
 
 	public boolean isGpsPosValid() {
-		return (serThread != null && serThread.isAlive() || gpsdThread != null && gpsdThread.isAlive() ) && gpsPos.isValid() ; // && gpsPos.getfiex();
+		return ((serThread != null && serThread.isAlive()) ||
+		       (gpsdThread != null && gpsdThread.isAlive()) ||
+		       (oldGpsdThread != null && oldGpsdThread.isAlive()))
+		        && gpsPos.isValid() ; // && gpsPos.getfiex();
 	}
 
 
@@ -184,14 +228,137 @@
 }
 
 
-class GpsdThread extends mThread{
+/**
+ * Thread for reading data from gpsd.
+ *
+ * @author Tilman Blumenbach
+ */
+class GpsdThread extends mThread {
+	GPS gpsObj;
+	CWGPSPoint myGPS;
+	boolean run;
+
+
+	public GpsdThread(CWGPSPoint GPSPoint) throws IOException, JSONException, GPSException {
+		JSONObject response;
+		int proto_major;
+
+		myGPS = GPSPoint;
+		gpsObj = new GPS(Global.getPref().gpsdHost, Global.getPref().gpsdPort);
+		gpsObj.stream( GPS.WATCH_ENABLE );
+
+		// Check major protocol version:
+		response = gpsObj.read();
+
+		if( ! response.getString( "class" ).equals( "VERSION" ) ) {
+			throw new GPSException( "Expected VERSION object at connect." );
+		} else if( ( proto_major = response.getInt( "proto_major" ) ) != 3 ) {
+			throw new GPSException( "Invalid protocol API version; got " +
+					proto_major + ", want 3." );
+		}
+	}
+
+
+	public void run() {
+		JSONObject response;
+		String respClass;
+		int noData = 0;
+		int notInterpreted = 0;
+		boolean gotValidData = false; // redundant, but compiler complains.
+
+		run = true;
+		while (run) {
+			if( gpsObj != null ) {
+				gotValidData = false;
+
+				try {
+					/* Tblue> This is ugly, but BufferedReader::ready() seems to
+					 *        be broken in Ewe, so instead of only polling when
+					 *        there is no data from gpsd (by checking the return
+					 *        value of GPS::waiting(), we poll on every iteration.
+					 *        Not ideal, but works for now.
+					 */
+					gpsObj.poll();
+
+					/* Tblue> TODO: I think this call should not block, but
+					 *              my GPS class does not yet support non-blocking
+					 *              reads...
+					 */
+					response  = gpsObj.read();
+					
+					// If we get here we have got some data:
+					noData = 0;
+
+					respClass = response.getString( "class" );
+					if( respClass.equals( "DEVICE" ) && response.has( "activated" ) &&
+						response.getDouble( "activated" ) != 0 )
+					{	// This is a new device, we need to tell gpsd we want to watch it:
+						Global.getPref().log( "New GPS device, sending WATCH command." );
+						gpsObj.stream( GPS.WATCH_ENABLE );
+					} else if( respClass.equals( "POLL" ) ) {
+						gotValidData = myGPS.examineGpsd(response);
+					} else if( respClass.equals( "ERROR" ) ) {
+						Global.getPref().log( "Ignored gpsd error: " + response.getString( "message" ) );
+					}
+				} catch( Exception e ) {
+					// Something bad happened, will just ignore this JSON
+					// object:
+					Global.getPref().log("Ignored Exception", e, true);
+					gotValidData = false;
+				}
+
+				if( gotValidData ) {
+					notInterpreted = 0;
+				} else {
+					notInterpreted++;
+				}
+				if (notInterpreted > 22) {
+					myGPS.noInterpretableData();
+				}
+			}
+
+			try {
+				sleep(1000);
+			} catch (InterruptedException e) {
+				Global.getPref().log("Ignored Exception", e, true);
+			}
+
+			noData++;
+			if (noData > 5) {
+				myGPS.noDataError();
+			}
+
+/*
+			Vm.debug( "------------------" );
+			Vm.debug( "noData        : " + noData );
+			Vm.debug( "notInterpreted: " + notInterpreted );
+			Vm.debug( "gotValidData  : " + gotValidData );
+*/
+			//myGPS.printAll();
+		} // while
+
+		myGPS.noData();
+	}
+
+
+	public void stop() {
+		run = false;
+
+		if( gpsObj != null ) {
+			gpsObj.cleanup();
+		}
+	}
+}
+
+
+class OldGpsdThread extends mThread{
 	Socket gpsdSocket;
 	CWGPSPoint myGPS;
 	boolean run, tcpForward;
 	Socket tcpConn;
 	String lastError = new String();
 
-	public GpsdThread(CWGPSPoint GPSPoint) throws IOException {
+	public OldGpsdThread(CWGPSPoint GPSPoint) throws IOException {
 		try{
 			gpsdSocket = new Socket(Global.getPref().gpsdHost, Global.getPref().gpsdPort);
 		} catch (IOException e) {
@@ -218,13 +385,15 @@
 				if (gpsResult!=null) {
 					//Vm.debug("P -> " + gpsResult);
 					noData = 0;
-					if (myGPS.examineGpsd(gpsResult))
+					if (myGPS.examineOldGpsd(gpsResult))
 						notinterpreted = 0;
 					else
 						notinterpreted++;
 					if (notinterpreted > 22) myGPS.noInterpretableData();
 				}
 			}
+
+			//myGPS.printAll();
 		} // while
 		myGPS.noData();
 	}
@@ -260,6 +429,7 @@
 	}
 }
 
+
 /**
  * Thread for reading data from COM-port
  *
Index: lib/net/ax86/GPSException.java
===================================================================
--- lib/net/ax86/GPSException.java	(Revision 0)
+++ lib/net/ax86/GPSException.java	(Revision 0)
@@ -0,0 +1,49 @@
+package net.ax86;
+
+/**
+ * The GPSException is thrown by {@link GPS} when errors occur.
+ *
+ * Copyright (c) 2010 by Tilman Blumenbach.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * @author Tilman Blumenbach
+ * @version 1.0
+ *
+ * @see GPS
+ */
+public class GPSException extends Exception
+{
+	public GPSException()
+	{
+		super();
+	}
+
+
+	public GPSException( String message )
+	{
+		super( message );
+	}
+
+
+	public GPSException( String message, Throwable cause )
+	{
+		super( message, cause );
+	}
+
+
+	public GPSException( Throwable cause )
+	{
+		super( cause );
+	}
+}
Index: lib/net/ax86/GPS.java
===================================================================
--- lib/net/ax86/GPS.java	(Revision 0)
+++ lib/net/ax86/GPS.java	(Revision 0)
@@ -0,0 +1,377 @@
+package net.ax86;
+
+import ewe.io.IOException;
+import org.json.JSONException;
+
+import ewe.io.InputStreamReader;
+import ewe.io.BufferedReader;
+import ewe.io.OutputStreamWriter;
+import ewe.io.BufferedWriter;
+import ewe.net.Socket;
+
+import org.json.JSONObject;
+
+/**
+ * gpsd client library.
+ *
+ * This a simple library that allows low-level communication with the
+ * GPS daemon.
+ *
+ * Things that are in C libgps but not in this library (or are done in a
+ * different way):
+ *  - GPS::stream():
+ *     - Works with JSON data only. That means there is no option to change
+ *       the reporting format (no WATCH_JSON, WATCH_NMEA, WATCH_RARE,
+ *       WATCH_RAW flags).
+ *     - Always uses the new protocol (no WATCH_NEWSTYLE, WATCH_OLDSTYLE
+ *       flags).
+ *     - Non-blocking mode is not yet implemented (no POLL_NONBLOCK flag).
+ *       I think this is the only real limitation of this implementation.
+ *
+ * Copyright (c) 2010 by Tilman Blumenbach.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @author Tilman Blumenbach
+ * @version 0.9_ewe
+ *
+ * @see <a href="http://gpsd.berlios.de/gpsd.html">gpsd manpage for more information</a>
+ */
+public class GPS
+{
+	/**
+	 * The major version of this API (NOT the gpsd API!).
+	 */
+	public static final int API_MAJOR     = 0;
+	/**
+	 * The minor version of this API (NOT the gpsd API!).
+	 */
+	public static final int API_MINOR     = 9;
+
+	/**
+	 * Disable watcher mode and other given WATCH_* flags.
+	 *
+	 * @see #stream( int )
+	 */
+	public static final int WATCH_DISABLE = 1<<0;
+	/**
+	 * Enable watcher mode and other given WATCH_* flags.
+	 *
+	 * @see #stream( int )
+	 */
+	public static final int WATCH_ENABLE  = 1<<1;
+	/**
+	 * When reporting AIS data, scale integer quantities to floats if
+	 * they have a divisor or rendering formula assosiated with them.
+	 *
+	 * @see #stream( int )
+	 */
+	public static final int WATCH_SCALED  = 1<<2;
+	/**
+	 * Restrict watching to a speciied device, patch given as second
+	 * argument to {@link #stream( int, String ) stream()}.
+	 *
+	 * @see #stream( int, String )
+	 */
+	public static final int WATCH_DEVICE  = 1<<3;
+
+	/**
+	 * DEVICE flags: GPS data has been seen on this device.
+	 */
+	public static final int SEEN_GPS      = 0x01;
+	/**
+	 * DEVICE flags: RTCM2 data has been seen on this device.
+	 */
+	public static final int SEEN_RTCM2    = 0x02;
+	/**
+	 * DEVICE flags: RTCM3 data has been seen on this device.
+	 */
+	public static final int SEEN_RTCM3    = 0x04;
+	/**
+	 * DEVICE flags: AIS data has been seen on this device.
+	 */
+	public static final int SEEN_AIS      = 0x08;
+
+	/**
+	 * The Socket used for communication with gpsd.
+	 */
+	private Socket sock;
+	/**
+	 * BufferedReader for the {@link #sock Socket}.
+	 *
+	 * @see #sock
+	 */
+	protected BufferedReader in;
+	/**
+	 * BufferedWriter for the {@link #sock Socket}.
+	 *
+	 * @see #sock
+	 */
+	protected BufferedWriter out;
+
+	/**
+	 * A {@link GPSHook} object used for certain callbacks.
+	 *
+	 * @see GPSHook
+	 * @see #setHook( GPSHook )
+	 */
+	protected GPSHook hook = null;
+
+
+	/**
+	 * Constructor: Creates a new GPS object and connects its
+	 * {@link #sock Socket} to a gpsd.
+	 *
+	 * @param host Host name or IP address to connect to.
+	 * @param port Port to connect to.
+	 *
+	 * @throws IOException E. g. on a connection failure.
+	 *
+	 * @see #cleanup() Call the <code>cleanup()</code> method when you
+	 *                 do not need this object anymore.
+	 */
+	public GPS( String host, int port )
+		throws IOException
+	{
+		this.sock = new Socket( host, port );
+		this.in   = new BufferedReader( new InputStreamReader( this.sock.getInputStream() ) );
+		this.out  = new BufferedWriter( new OutputStreamWriter( this.sock.getOutputStream() ) );
+	}
+
+
+	/**
+	 * Constructor: Creates a new GPS object and connects its
+	 * {@link #sock Socket} to a gpsd.
+	 *
+	 * This simply calls {@link #constructor( String, int )} with the
+	 * default gpsd port 2947.
+	 *
+	 * @param host Host name or IP address to connect to.
+	 *
+	 * @throws IOException E. g. on a connection failure.
+	 *
+	 * @see #constructor( String, int )
+	 * @see #cleanup() Call the <code>cleanup()</code> method when you
+	 *                 do not need this object anymore.
+	 */
+	public GPS( String host )
+		throws IOException
+	{
+		this( host, 2947 );
+	}
+
+
+	/**
+	 * Sends data to gpsd.
+	 *
+	 * @param data The data to send.
+	 *
+	 * @throws IOException When data could not be written to the stream.
+	 */
+	public void send( String data )
+		throws IOException
+	{
+		this.out.write( data, 0, data.length() );
+		// Make sure it gets through:
+		this.out.flush();
+	}
+
+
+	/**
+	 * Asks gpsd for data from the last-seen fixes on all active GPS devices.
+	 *
+	 * This is a very simple method that just calls
+	 * {@link #send( String )} to send a "POLL" command to the
+	 * server.
+	 * 
+	 * @throws IOException When data could not be written to the stream.
+	 * 
+	 * @see #send( String )
+	 */
+	public void poll()
+		throws IOException
+	{
+		this.send( "?POLL;" );
+	}
+
+
+	/**
+	 * Gets the next {@linkplain JSONObject JSON object} from gpsd.
+	 *
+	 * This call blocks when there is no data available. Use
+	 * {@link #waiting()} to check if there is data waiting.
+	 *
+	 * If there is a {@linkplain GPSHook hook} set, its
+	 * {@link GPSHook#onRawData( JSONObject, String )} method will be called.
+	 *
+	 * @return A {@link JSONObject} containing the data received from gpsd.
+	 * 
+	 * @throws IOException On a stream reading failure.
+	 * @throws JSONException On a JSON parsing failure (e. g. syntax error).
+	 *
+	 * @see JSONObject
+	 * @see #waiting()
+	 * @see GPSHook#onRawData( JSONObject, String )
+	 */
+	public JSONObject read()
+		throws IOException, JSONException
+	{
+		String rawJSON = this.in.readLine();
+		JSONObject parsedJSON;
+
+		if( rawJSON == null )
+		{
+			throw new IOException( "End Of Stream reached" );
+		}
+
+		parsedJSON = new JSONObject( rawJSON );
+
+		// Call our hook, if set:
+		if( this.hook != null )
+		{
+			this.hook.onRawData( parsedJSON, rawJSON );
+		}
+
+		return parsedJSON;
+	}
+
+
+	/**
+	 * Checks whether there is data from gpsd to be {@linkplain #read() read}.
+	 *
+	 * Tblue> TODO: This does not seem to work correctly in Ewe (but it
+	 *              does with Sun's JRE). Ewe bug?
+	 * 
+	 * @return True if there is data to be read, false otherwise.
+	 */
+	public boolean waiting()
+	{
+		try
+		{
+			return this.in.ready();
+		}
+		catch( IOException ignored )
+		{
+			return false;
+		}
+	}
+
+
+	/**
+	 * Enables/tunes gpsd watcher mode.
+	 *
+	 * @param flags Flags describing the action(s) to take.
+	 * @param arg   A String treated as an argument to certain flags.
+	 *
+	 * @throws IOException E. g. on a {@link #sock Stream} write error.
+	 * @throws JSONException If we made a huge mistake like passing a
+	 *                       <code>null</code> String to one of the
+	 *                       {@link JSONObject#put()} methods. Should not
+	 *                       happen.
+	 * @throws GPSException If <code>arg</code> is <code>null</code> but
+	 *                      is needed by a flag.
+	 *
+	 * @see #WATCH_DISABLE
+	 * @see #WATCH_ENABLE
+	 * @see #WATCH_SCALED
+	 * @see #WATCH_DEVICE
+	 */
+	public void stream( int flags, String arg )
+		throws IOException, JSONException, GPSException
+	{
+		JSONObject GPSData = new JSONObject();
+
+		GPSData.put( "class", "WATCH" );
+
+		if( ( flags & WATCH_ENABLE ) != 0 )
+		{
+			GPSData.put( "enable", true );
+
+			if( ( flags & WATCH_SCALED ) != 0 )
+			{
+				GPSData.put( "scaled", true );
+			}
+			if( ( flags & WATCH_DEVICE ) != 0 )
+			{
+				if( arg == null )
+				{
+					throw new GPSException( "No argument provided with WATCH_DEVICE" );
+				}
+
+				GPSData.put( "device", arg );
+			}
+		}
+		else // WATCH_DISABLE?
+		{
+			GPSData.put( "enable", false );
+
+			if( ( flags & WATCH_SCALED ) != 0 )
+			{
+				GPSData.put( "scaled", false );
+			}
+		}
+
+		// Send request to the server:
+		this.send( "?WATCH=" + GPSData.toString() + ";" );
+	}
+
+
+	/**
+	 * Enables/tunes gpsd watcher mode.
+	 *
+	 * This simply calls {@link #stream( int, String )} with an <code>arg</code>
+	 * parameter of <code>null</code>.
+	 *
+	 * @param int Flags describing the action(s) to take.
+	 *
+	 * @throws IOException E. g. on a {@link #sock Stream} write error.
+	 * @throws JSONException If we made a huge mistake like passing a
+	 *                       <code>null</code> String to one of the
+	 *                       {@link JSONObject#put()} methods. Should not
+	 *                       happen.
+	 * @throws GPSException If <code>arg</code> is <code>null</code> but
+	 *                      is needed by a flag.
+	 *
+	 * @see #stream( int, String ) stream( int, String ) for information
+	 *                             about flags.
+	 */
+	public void stream( int flags )
+		throws IOException, JSONException, GPSException
+	{
+		this.stream( flags, null );
+	}
+
+
+	/**
+	 * Sets a {@linkplain GPSHook hook} to call on certain events.
+	 *
+	 * @param hook The {@link GPSHook} to set.
+	 * @see GPSHook
+	 */
+	public void setHook( GPSHook hook )
+	{
+		this.hook = hook;
+	}
+
+
+	/**
+	 * "Destructor": Does the final clean up.
+	 *
+	 * Call this method when you do not this object anymore.
+	 */
+	public void cleanup()
+	{
+		// Close our socket. Also closes its Input and Output streams.
+		this.sock.close();
+	}
+}
Index: lib/net/ax86/GPSTest.java
===================================================================
--- lib/net/ax86/GPSTest.java	(Revision 0)
+++ lib/net/ax86/GPSTest.java	(Revision 0)
@@ -0,0 +1,95 @@
+package net.ax86;
+
+import ewe.sys.*;
+
+import net.ax86.GPSException;
+import net.ax86.GPS;
+import org.json.*;
+
+/**
+ * Quick and dirty (but convenient) test class/program for the {@link GPS}
+ * class.
+ *
+ * @author Tilman Blumenbach
+ */
+public class GPSTest
+{
+	public static void main( String args[] )
+	{
+		JSONObject JSON = null, two;
+		JSONArray arr;
+		GPS g = null;
+		int maj = 0, min = 0;
+
+		Vm.startEwe(args);
+
+		try
+		{
+			g = new GPS( "127.0.0.1" );
+			g.stream( GPS.WATCH_ENABLE );
+			JSON = g.read();
+		}
+		catch( Exception e )
+		{
+			Vm.debug( "Could not connect to gpsd: " + e.getMessage() );
+			Vm.exit(1);
+		}
+
+		try
+		{
+			maj = JSON.getInt( "proto_major" );
+			min = JSON.getInt( "proto_minor" );
+		}
+		catch( JSONException e )
+		{
+			Vm.debug( "JSON error: " + e.getMessage() );
+			Vm.exit(1);
+		}
+		
+		Vm.debug( "Major: " + maj + ", minor: " + min );
+
+		try
+		{
+			while( true )
+			{
+				// Make sure we have enough data:
+				//Vm.debug( ">> POLLING!" );
+
+				JSON = g.read();
+				Vm.debug( ">> Got JSON [" + JSON.getString( "class" ) + "]: " );
+				Vm.debug( ">>> " + JSON.toString() );
+
+				if( JSON.getString( "class" ).equals( "DEVICE" ) &&
+				    JSON.has( "activated" ) && JSON.getDouble( "activated" ) != 0 )
+				{
+					Vm.debug( ">> Keeping up." );
+					g.stream( GPS.WATCH_ENABLE );
+				}
+				else if( JSON.getString( "class" ).equals( "POLL" ) )
+				{
+					arr = JSON.getJSONArray( "fixes" );
+					for( int i = 0; i < arr.length(); i++ )
+					{
+						two = arr.getJSONObject( i );
+						if( two.has( "lat" ) && two.has( "lon" ) )
+						{
+							Vm.debug( ">> LAT: " + two.getDouble( "lat" ) +
+									" LON: " + two.getDouble( "lon" ) );
+						}
+					}
+				}
+
+				g.poll();
+				Thread.sleep( 3000 );
+			}
+		}
+		catch( Exception e )
+		{
+			Vm.debug( "EOF?" );
+			e.printStackTrace();
+			Vm.exit(1);
+		}
+
+		Vm.exit(0);
+	}
+}
Index: lib/net/ax86/GPSHook.java
===================================================================
--- lib/net/ax86/GPSHook.java	(Revision 0)
+++ lib/net/ax86/GPSHook.java	(Revision 0)
@@ -0,0 +1,41 @@
+package net.ax86;
+
+import org.json.JSONObject;
+
+/**
+ * Hook interface supposed to be implemented by classes who want to be
+ * used as a "callback" on certain {@link GPS} events.
+ *
+ * Copyright (c) 2010 by Tilman Blumenbach.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * @author Tilman Blumenbach
+ * @version 1.0
+ *
+ * @see GPS
+ * @see GPS#setHook( GPSHook )
+ */
+public interface GPSHook
+{
+	/**
+	 * Gets called e. g. by {@link GPS#read()}.
+	 *
+	 * @param JSONObject The parsed JSON data received from gpsd.
+	 * @param String     The raw (unparsed) JSON data from gpsd.
+	 *
+	 * @see GPS#read()
+	 * @see JSONObject
+	 */
+	public void onRawData( JSONObject parsedData, String rawData );
+}
Index: lib/org/json/Test.java
===================================================================
--- lib/org/json/Test.java	(Revision 0)
+++ lib/org/json/Test.java	(Revision 0)
@@ -0,0 +1,378 @@
+package org.json;
+
+import ewe.sys.*;
+import ewe.sys.Double;
+
+import ewe.util.Enumeration;
+import ewe.util.Map;
+import ewe.io.StringWriter;
+
+import ewe.reflect.Array;
+import ewe.reflect.Wrapper;
+
+
+/**
+ * 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[]) {
+        Vm.startEwe(args);
+
+        Vm.debug( String.valueOf( Array.isArray( "foo" ) ) );
+        
+        double[] ia = { 1, 6, 95, 0, -200.6 };
+        int x;
+        Wrapper wrap = null;
+
+        for( x = 0; x < Array.getLength( ia ); x++ )
+        {
+          wrap = Array.getElement( ia, x, wrap );
+          Vm.debug( "[" + x + "] " + String.valueOf( wrap.toJavaWrapper() ) );
+        }
+  
+        Enumeration it;
+        JSONArray a;
+        JSONObject j;
+        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);
+            Vm.debug(a.toString());
+            Vm.debug("");
+            
+            j = new JSONObject();
+            o = null;
+            j.put("booga", o);
+            j.put("wooga", JSONObject.NULL);
+            Vm.debug(j.toString());
+            Vm.debug("");
+           
+            j = new JSONObject();
+            j.increment("two");
+            j.increment("two");
+            Vm.debug(j.toString());
+            Vm.debug("");
+            
+            
+            s = "{     \"list of lists\" : [         [1, 2, 3],         [4, 5, 6],     ] }";
+            j = new JSONObject(s);
+            Vm.debug(j.toString(4));
+                    
+            
+            j = new JSONObject(obj);
+            Vm.debug(j.toString());
+            
+            s = "{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, \"ratingCount\": null, \"averageRating\": null } }";
+            j = new JSONObject(s);
+            Vm.debug(j.toString(2));
+
+        	int ar[] = {1, 2, 3};
+        	JSONArray ja = new JSONArray(ar);
+        	Vm.debug(ja.toString());
+        	
+        	String sa[] = {"aString", "aNumber", "aBoolean"};            
+            j = new JSONObject(obj, sa);
+            j.put("Testing JSONString interface", obj);
+            Vm.debug(j.toString(4));          
+            
+            j = new JSONObject("{slashes: '///', closetag: '</script>', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}");
+            Vm.debug(j.toString(2));
+            Vm.debug("");
+
+            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));
+            Vm.debug(j.toString(4));
+
+            Vm.debug("String: " + j.getDouble("String"));
+            Vm.debug("  bool: " + j.getBoolean("bool"));
+            Vm.debug("    to: " + j.getString("to"));
+            Vm.debug("  true: " + j.getString("true"));
+            Vm.debug("   foo: " + j.getJSONArray("foo"));
+            Vm.debug("    op: " + j.getString("op"));
+            Vm.debug("   ten: " + j.getInt("ten"));
+            Vm.debug("  oops: " + j.optBoolean("oops"));
+
+            j = new JSONObject("{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}");
+            Vm.debug(j.toString(2));
+            Vm.debug("isNull: " + j.isNull("nix"));
+            Vm.debug("   has: " + j.has("nix"));
+            Vm.debug("");
+
+            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 /'}");
+            Vm.debug(j.toString());
+            Vm.debug("");
+
+            JSONTokener jt = new JSONTokener("{op:'test', to:'session', pre:1}{op:'test', to:'session', pre:2}");
+            j = new JSONObject(jt);
+            Vm.debug(j.toString());
+            Vm.debug("pre: " + j.optInt("pre"));
+            int i = jt.skipTo('{');
+            Vm.debug(String.valueOf(i));
+            j = new JSONObject(jt);
+            Vm.debug(j.toString());
+            Vm.debug("");
+
+            a = new JSONArray(" [\"<escape>\", next is an implied null , , ok,] ");
+            Vm.debug(a.toString());
+            Vm.debug("");
+
+            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}");
+            Vm.debug(j.toString(4));
+            Vm.debug("");
+            if (j.getBoolean("true") && !j.getBoolean("false")) {
+                Vm.debug("It's all good");
+            }
+
+            Vm.debug("");
+            j = new JSONObject(j, new String[]{"dec", "oct", "hex", "missing"});
+            Vm.debug(j.toString(4));
+
+            Vm.debug("");
+            j = new JSONObject("{string: \"98.6\", long: 2147483648, int: 2147483647, longer: 9223372036854775807, double: 9223372036854775808}");
+            Vm.debug(j.toString(4));
+
+            Vm.debug("\ngetInt");
+            Vm.debug("int    " + j.getInt("int"));
+            Vm.debug("long   " + j.getInt("long"));
+            Vm.debug("longer " + j.getInt("longer"));
+            //Vm.debug("double " + j.getInt("double"));
+            //Vm.debug("string " + j.getInt("string"));
+
+            Vm.debug("\ngetLong");
+            Vm.debug("int    " + j.getLong("int"));
+            Vm.debug("long   " + j.getLong("long"));
+            Vm.debug("longer " + j.getLong("longer"));
+            //Vm.debug("double " + j.getLong("double"));
+            //Vm.debug("string " + j.getLong("string"));
+
+            Vm.debug("\ngetDouble");
+            Vm.debug("int    " + j.getDouble("int"));
+            Vm.debug("long   " + j.getDouble("long"));
+            Vm.debug("longer " + j.getDouble("longer"));
+            Vm.debug("double " + j.getDouble("double"));
+            Vm.debug("string " + j.getDouble("string"));
+
+            j.put("good sized", 9223372036854775807L);
+            Vm.debug(j.toString(4));
+
+            a = new JSONArray("[2147483647, 2147483648, 9223372036854775807, 9223372036854775808]");
+            Vm.debug(a.toString(4));
+
+            Vm.debug("\nKeys: ");
+            it = j.keys();
+            while (it.hasMoreElements()) {
+                s = (String)it.nextElement();
+                Vm.debug(s + ": " + j.getString(s));
+            }
+
+
+            Vm.debug("\naccumulate: ");
+            j = new JSONObject();
+            j.accumulate("stooge", "Curly");
+            j.accumulate("stooge", "Larry");
+            j.accumulate("stooge", "Moe");
+            a = j.getJSONArray("stooge");
+            a.put(5, "Shemp");
+            Vm.debug(j.toString(4));
+
+            Map m = null;
+            
+            j = new JSONObject(m);
+            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);
+            Vm.debug(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);
+            Vm.debug(j.toString(4));
+            
+            s = " (\"San Francisco\", \"New York\", \"Seoul\", \"London\", \"Seattle\", \"Shanghai\")";
+            a = new JSONArray(s);
+            Vm.debug(a.toString());
+            
+            
+            Vm.debug("\nTesting Exceptions: ");
+
+            Vm.debug(">> Exception: ");
+            try {
+                a = new JSONArray("[\n\r\n\r}");
+                Vm.debug(a.toString());
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            
+            Vm.debug(">> Exception: ");
+            try {
+                a = new JSONArray("<\n\r\n\r      ");
+                Vm.debug(a.toString());
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            
+            Vm.debug(">> Exception: ");
+            try {
+                a = new JSONArray();
+                a.put(Double.NEGATIVE_INFINITY);
+                a.put(Double.NAN);
+                Vm.debug(a.toString());
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {
+                Vm.debug(String.valueOf(j.getDouble("stooge")));
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {
+                Vm.debug(String.valueOf(j.getDouble("howard")));
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {
+                Vm.debug(j.put(null, "howard").toString());
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {
+                Vm.debug(String.valueOf(a.getDouble(0)));
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {
+                Vm.debug(a.get(-1).toString());
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {
+                Vm.debug(a.put(Double.NAN).toString());
+            } catch (Exception e) {
+                Vm.debug(e.toString());
+            }
+            Vm.debug(">> Exception: ");
+            try {            	
+            	ja = new JSONArray(new Object());
+            	Vm.debug(ja.toString());
+            } catch (Exception e) {
+            	Vm.debug(e.toString());
+            }
+
+            Vm.debug(">> Exception: ");
+            try {            	
+            	s = "[)";
+            	a = new JSONArray(s);
+            	Vm.debug(a.toString());
+            } catch (Exception e) {
+            	Vm.debug(e.toString());
+            }
+
+            Vm.debug(">> Exception: ");
+            try {            	
+                s = "{\"koda\": true, \"koda\": true}";
+                j = new JSONObject(s);
+                Vm.debug(j.toString(4));
+            } catch (Exception e) {
+            	Vm.debug(e.toString());
+            }
+
+        } catch (Exception e) {
+            Vm.debug( "Exception caught:");
+            e.printStackTrace();
+        }
+
+      Vm.exit(0);
+    }
+}
Index: lib/org/json/JSONObject.java
===================================================================
--- lib/org/json/JSONObject.java	(Revision 0)
+++ lib/org/json/JSONObject.java	(Revision 0)
@@ -0,0 +1,1583 @@
+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 ewe.io.IOException;
+import ewe.io.Writer;
+import ewe.util.Iterator;
+import ewe.util.Enumeration;
+import ewe.util.Map;
+import ewe.util.Hashtable; // Tblue> TODO: Not thread-safe?
+import ewe.util.Utils;
+import ewe.util.StandardComparer;
+import ewe.reflect.Reflect;
+import ewe.reflect.Wrapper;
+import ewe.reflect.Field;
+import ewe.reflect.Array;
+
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
+
+/**
+ * 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>0x-</code> <small>(hex)</small> prefix.</li>
+ * </ul>
+ * @author JSON.org
+ * @author Tilman Blumenbach (modifications for Ewe)
+ * @version 2010-05-17_ewe
+ */
+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 Hashtable();
+    }
+
+
+    /**
+     * 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 Hashtable();
+        if (map != null) {
+            Iterator i = map.entries();
+            while (i.hasNext()) {
+                Map.MapEntry e = (Map.MapEntry)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();
+        
+        Reflect c = Reflect.getForObject( object );
+        Wrapper wrapped = null;
+        
+        for (int i = 0; i < names.length; i += 1) {
+            String name = names[i];
+            try {
+                wrapped = c.getField(name, Reflect.PUBLIC).getValue( object, wrapped );
+                putOpt(name, wrapped.toJavaWrapper());
+            } 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. 
+     *
+     * @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);
+        try {
+            return o instanceof Number ?
+                ((Number)o).intValue() :
+                Integer.parseInt((String)o);
+        } catch (Exception e) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                "] is not an int.");
+        }
+    }
+
+
+    /**
+     * 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. 
+     *
+     * @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);
+        try {
+            return o instanceof Number ?
+                ((Number)o).longValue() :
+                Long.parseLong((String)o);
+        } catch (Exception e) {
+            throw new JSONException("JSONObject[" + quote(key) +
+                "] is not a long.");
+        }
+    }
+
+
+    /**
+     * 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;
+        }
+        Enumeration i     = jo.keys();
+        String[]    names = new String[length];
+        int j = 0;
+        while (i.hasMoreElements()) {
+            names[j] = (String)i.nextElement();
+            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;
+        }
+        Reflect klass = Reflect.getForObject( object );
+        Field[] fields = klass.getFields( Reflect.PUBLIC );
+        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 enumeration of the keys.
+     */
+    public Enumeration keys() {
+        return ((Hashtable)(this.map)).keys();
+    }
+
+
+    /**
+     * 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();
+        Enumeration keys = keys();
+        while (keys.hasMoreElements()) {
+            ja.put(keys.nextElement());
+        }
+        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 = Reflect.getForObject(bean).getReflectedClass();
+
+// 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/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 a alphabetically sorted array of the keys in the JSONObject.
+     *
+     * Tblue> I know this is ugly and probably completely inefficient
+     *        but blame Ewe for not providing a TreeSet. 
+     * 
+     * @return An array containing the keys.
+     */
+    public Object[] sortedKeys() {
+      Enumeration iter = this.keys();
+      Object[] keys = new Object[this.length()];
+      int i;
+
+      for( i = 0; iter.hasMoreElements(); i++ ) {
+        keys[i] = iter.nextElement();
+      }
+
+      // Now sort the keys:
+      Utils.sort( keys, new StandardComparer(), false );
+      return keys;
+    }
+
+    /**
+     * 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 {
+            Enumeration  keys = keys();
+            StringBuffer sb = new StringBuffer("{");
+
+            while (keys.hasMoreElements()) {
+                if (sb.length() > 1) {
+                    sb.append(',');
+                }
+                Object o = keys.nextElement();
+                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 i, j;
+        int n = length();
+        if (n == 0) {
+            return "{}";
+        }
+        Object[]     keys = sortedKeys();
+        StringBuffer sb = new StringBuffer("{");
+        int          newindent = indent + indentFactor;
+        Object       o;
+        if (n == 1) {
+            o = keys[0];
+            sb.append(quote(o.toString()));
+            sb.append(": ");
+            sb.append(valueToString(this.map.get(o), indentFactor,
+                    indent));
+        } else {
+            for( i = 0; i < n; i++) {
+                o = keys[i];
+                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 (Array.isArray(value)) {
+            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 (Array.isArray(value)) {
+            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 || 
+            		 NULL.equals(object)      || object instanceof JSONString || 
+            		 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) {
+                 return object;
+             }
+             
+             if (Array.isArray(object)) {
+                 return new JSONArray(object);
+             }
+             if (object instanceof Map) {
+                 return new JSONObject((Map)object);
+             }
+             Package objectPackage = Reflect.getForObject(object).getReflectedClass().getPackage();
+             String objectPackageName = ( objectPackage != null ? objectPackage.getName() : "" );
+             if (objectPackageName.startsWith("java.") ||
+            		 objectPackageName.startsWith("javax.") ||
+            		 Reflect.getForObject(object).getReflectedClass().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;
+            Enumeration keys = keys();
+            writer.write('{');
+
+            while (keys.hasMoreElements()) {
+                if (b) {
+                    writer.write(',');
+                }
+                Object k = keys.nextElement();
+                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);
+        }
+     }
+}
Index: lib/org/json/JSONString.java
===================================================================
--- lib/org/json/JSONString.java	(Revision 0)
+++ lib/org/json/JSONString.java	(Revision 0)
@@ -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();
+}
Index: lib/org/json/JSONTokener.java
===================================================================
--- lib/org/json/JSONTokener.java	(Revision 0)
+++ lib/org/json/JSONTokener.java	(Revision 0)
@@ -0,0 +1,435 @@
+package org.json;
+
+import ewe.io.BufferedReader;
+import ewe.io.IOException;
+import ewe.io.Reader;
+import ewe.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_ewe
+ */
+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 + "]";
+    }
+}
Index: lib/org/json/JSONArray.java
===================================================================
--- lib/org/json/JSONArray.java	(Revision 0)
+++ lib/org/json/JSONArray.java	(Revision 0)
@@ -0,0 +1,880 @@
+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 ewe.io.IOException;
+import ewe.io.Writer;
+import ewe.util.Iterator;
+import ewe.util.Map;
+import ewe.util.Vector; // Tblue> TODO: Not thread-safe?
+import ewe.reflect.Wrapper;
+import ewe.reflect.Array;
+
+/**
+ * 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
+ * @author Tilman Blumenbach (modifications for Ewe)
+ * @version 2009-04-14_ewe
+ */
+public class JSONArray {
+
+
+    /**
+     * The Vector where the JSONArray's properties are kept.
+     */
+    private Vector myVector;
+
+
+    /**
+     * Construct an empty JSONArray.
+     */
+    public JSONArray() {
+        this.myVector = new Vector();
+    }
+
+    /**
+     * 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.myVector.add(null);
+            } else {
+                x.back();
+                this.myVector.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 an array
+     * @throws JSONException If not an array.
+     */
+    public JSONArray(Object array) throws JSONException {
+        this();
+
+        Wrapper wrapped = null;
+
+        if (Array.isArray(array)) {
+            int length = Array.getLength(array);
+            for (int i = 0; i < length; i++) {
+                wrapped = Array.getElement( array, i, wrapped );
+                this.put(JSONObject.wrap(wrapped.toJavaWrapper()));
+            }
+        } else {
+            throw new JSONException(
+"JSONArray initial value should be a string 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.myVector.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.myVector.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.myVector.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;
+    }
+
+
+    /**
+     * 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.myVector.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 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.myVector.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.myVector.del(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.myVector.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.myVector.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.myVector.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);
+        }
+    }
+}
Index: lib/org/json/JSONException.java
===================================================================
--- lib/org/json/JSONException.java	(Revision 0)
+++ lib/org/json/JSONException.java	(Revision 0)
@@ -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;
+    }
+}

